├── SUMMARY.md
├── .babelrc
├── image.png
├── public
├── favicon.ico
├── favicon.png
├── favicon-16x16.png
├── favicon-32x32.png
├── apple-touch-icon.png
├── android-chrome-192x192.png
├── android-chrome-512x512.png
└── index.html
├── src
├── components
│ ├── Loading
│ │ ├── loading.gif
│ │ └── Loading.jsx
│ ├── Navbar
│ │ ├── favicon-32x32.png
│ │ └── Navbar.jsx
│ ├── App.css
│ ├── ContractNotDeployed
│ │ └── ContractNotDeployed.jsx
│ ├── AccountDetails
│ │ └── AccountDetails.jsx
│ ├── ConnectMetamask
│ │ ├── ConnectToMetamask.jsx
│ │ └── metamask.svg
│ ├── MyCryptoBoyNFTDetails
│ │ └── MyCryptoBoyNFTDetails.jsx
│ ├── AllCryptoBoys
│ │ └── AllCryptoBoys.jsx
│ ├── MyCryptoBoys
│ │ └── MyCryptoBoys.jsx
│ ├── CryptoBoyNFTImage
│ │ └── CryptoBoyNFTImage.jsx
│ ├── Queries
│ │ └── Queries.jsx
│ ├── CryptoBoyNFTDetails
│ │ └── CryptoBoyNFTDetails.jsx
│ ├── App.js
│ └── FormAndPreview
│ │ └── FormAndPreview.jsx
├── index.js
├── contracts
│ ├── Migrations.sol
│ └── CryptoBoys.sol
├── serviceWorker.js
└── abis
│ └── Migrations.json
├── migrations
├── 1_initial_migration.js
└── 2_deploy_contracts.js
├── .gitignore
├── truffle-config.js
├── package.json
├── README.md
└── test
└── CryptoBoys.test.js
/SUMMARY.md:
--------------------------------------------------------------------------------
1 | # Table of contents
2 |
3 | * [README](README.md)
4 |
--------------------------------------------------------------------------------
/.babelrc:
--------------------------------------------------------------------------------
1 | {
2 | "presets": ["es2015", "stage-2", "stage-3"]
3 | }
4 |
--------------------------------------------------------------------------------
/image.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/L-Nova0211/Eth_Kervan_NFT_Marketplace/HEAD/image.png
--------------------------------------------------------------------------------
/public/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/L-Nova0211/Eth_Kervan_NFT_Marketplace/HEAD/public/favicon.ico
--------------------------------------------------------------------------------
/public/favicon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/L-Nova0211/Eth_Kervan_NFT_Marketplace/HEAD/public/favicon.png
--------------------------------------------------------------------------------
/public/favicon-16x16.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/L-Nova0211/Eth_Kervan_NFT_Marketplace/HEAD/public/favicon-16x16.png
--------------------------------------------------------------------------------
/public/favicon-32x32.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/L-Nova0211/Eth_Kervan_NFT_Marketplace/HEAD/public/favicon-32x32.png
--------------------------------------------------------------------------------
/public/apple-touch-icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/L-Nova0211/Eth_Kervan_NFT_Marketplace/HEAD/public/apple-touch-icon.png
--------------------------------------------------------------------------------
/public/android-chrome-192x192.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/L-Nova0211/Eth_Kervan_NFT_Marketplace/HEAD/public/android-chrome-192x192.png
--------------------------------------------------------------------------------
/public/android-chrome-512x512.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/L-Nova0211/Eth_Kervan_NFT_Marketplace/HEAD/public/android-chrome-512x512.png
--------------------------------------------------------------------------------
/src/components/Loading/loading.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/L-Nova0211/Eth_Kervan_NFT_Marketplace/HEAD/src/components/Loading/loading.gif
--------------------------------------------------------------------------------
/src/components/Navbar/favicon-32x32.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/L-Nova0211/Eth_Kervan_NFT_Marketplace/HEAD/src/components/Navbar/favicon-32x32.png
--------------------------------------------------------------------------------
/src/components/App.css:
--------------------------------------------------------------------------------
1 | input::-webkit-input-placeholder {
2 | font-size: 1rem;
3 | }
4 |
5 | input[type="color"]:hover {
6 | cursor: pointer;
7 | }
8 |
--------------------------------------------------------------------------------
/migrations/1_initial_migration.js:
--------------------------------------------------------------------------------
1 | const Migrations = artifacts.require("Migrations");
2 |
3 | module.exports = function(deployer) {
4 | deployer.deploy(Migrations);
5 | };
6 |
--------------------------------------------------------------------------------
/migrations/2_deploy_contracts.js:
--------------------------------------------------------------------------------
1 | const CryptoBoys = artifacts.require("CryptoBoys");
2 |
3 | module.exports = async function(deployer) {
4 | await deployer.deploy(CryptoBoys);
5 | };
6 |
--------------------------------------------------------------------------------
/src/components/Loading/Loading.jsx:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import loadingGIF from "./loading.gif";
3 |
4 | const Loading = () => {
5 | return ;
6 | };
7 |
8 | export default Loading;
9 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
2 |
3 | # dependencies
4 | /node_modules
5 | /.pnp
6 | .pnp.js
7 |
8 | # testing
9 | /coverage
10 |
11 | # production
12 | /build
13 |
14 | # misc
15 | .DS_Store
16 | .env
17 | .env.local
18 | .env.development.local
19 | .env.test.local
20 | .env.production.local
21 |
22 | npm-debug.log*
23 | yarn-debug.log*
24 | yarn-error.log*
25 |
26 | # Local Netlify folder
27 | .netlify
--------------------------------------------------------------------------------
/src/components/ContractNotDeployed/ContractNotDeployed.jsx:
--------------------------------------------------------------------------------
1 | import React from "react";
2 |
3 | const ContractNotDeployed = () => {
4 | return (
5 |
6 |
Crypto Boys Contract Not Deployed To This Network.
7 |
8 |
9 | Connect Metamask to Kovan Testnet Or Localhost 7545 running a custom RPC
10 | like Ganache.
11 |
12 |
13 | );
14 | };
15 |
16 | export default ContractNotDeployed;
17 |
--------------------------------------------------------------------------------
/truffle-config.js:
--------------------------------------------------------------------------------
1 | require("babel-register");
2 | require("babel-polyfill");
3 |
4 | module.exports = {
5 | networks: {
6 | development: {
7 | host: "127.0.0.1",
8 | port: 7545,
9 | network_id: "*", // Match any network id
10 | },
11 | },
12 | contracts_directory: "./src/contracts/",
13 | contracts_build_directory: "./src/abis/",
14 | compilers: {
15 | solc: {
16 | version: "pragma",
17 | optimizer: {
18 | enabled: true,
19 | runs: 200,
20 | },
21 | },
22 | },
23 | };
24 |
--------------------------------------------------------------------------------
/src/index.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import ReactDOM from "react-dom";
3 | import App from "./components/App";
4 | import * as serviceWorker from "./serviceWorker";
5 |
6 | import "./bootstrap.min.css";
7 |
8 | ReactDOM.render(
9 |
10 |
11 | ,
12 | document.getElementById("root")
13 | );
14 |
15 | // If you want your app to work offline and load faster, you can change
16 | // unregister() to register() below. Note this comes with some pitfalls.
17 | // Learn more about service workers: https://bit.ly/CRA-PWA
18 | serviceWorker.unregister();
19 |
--------------------------------------------------------------------------------
/src/contracts/Migrations.sol:
--------------------------------------------------------------------------------
1 | // SPDX-License-Identifier: MIT
2 | pragma solidity >=0.4.21 <0.8.0;
3 |
4 | contract Migrations {
5 | address public owner;
6 | uint public last_completed_migration;
7 |
8 | constructor() {
9 | owner = msg.sender;
10 | }
11 |
12 | modifier restricted() {
13 | if (msg.sender == owner) _;
14 | }
15 |
16 | function setCompleted(uint completed) public restricted {
17 | last_completed_migration = completed;
18 | }
19 |
20 | function upgrade(address new_address) public restricted {
21 | Migrations upgraded = Migrations(new_address);
22 | upgraded.setCompleted(last_completed_migration);
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/src/components/AccountDetails/AccountDetails.jsx:
--------------------------------------------------------------------------------
1 | import React from "react";
2 |
3 | const AccountDetails = ({ accountAddress, accountBalance }) => {
4 | return (
5 |
6 |
7 |
CryptoBoy NFT Marketplace
8 |
9 | This is an NFT marketplace where you can mint ERC721 implemented{" "}
10 | Crypto Boy NFTs and manage them.
11 |
12 |
13 |
Account address :
14 |
{accountAddress}
15 |
Account balance :
16 |
{accountBalance} Ξ
17 |
18 |
19 | );
20 | };
21 |
22 | export default AccountDetails;
23 |
--------------------------------------------------------------------------------
/src/components/ConnectMetamask/ConnectToMetamask.jsx:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import metamaskIcon from "./metamask.svg";
3 |
4 | const ConnectToMetamask = ({ connectToMetamask }) => {
5 | return (
6 |
7 |
8 | CryptoBoy NFT Marketplace
9 |
10 |
11 | This is an NFT marketplace where you can mint your ERC721 implemented{" "}
12 | Crypto Boy NFTs and manage them.
13 |
14 |
15 |
20 | Connect Metamask{" "}
21 |
26 |
27 |
28 | );
29 | };
30 |
31 | export default ConnectToMetamask;
32 |
--------------------------------------------------------------------------------
/src/components/MyCryptoBoyNFTDetails/MyCryptoBoyNFTDetails.jsx:
--------------------------------------------------------------------------------
1 | import React from "react";
2 |
3 | const MyCryptoBoyNFTDetails = (props) => {
4 | const {
5 | tokenId,
6 | tokenName,
7 | price,
8 | mintedBy,
9 | previousOwner,
10 | numberOfTransfers,
11 | } = props.cryptoboy;
12 | return (
13 |
14 |
15 | Token Id :{" "}
16 | {tokenId.toNumber()}
17 |
18 |
19 | Name : {tokenName}
20 |
21 |
22 | Price :{" "}
23 | {window.web3.utils.fromWei(price.toString(), "Ether")} Ξ
24 |
25 |
26 | No. of Transfers :{" "}
27 | {numberOfTransfers.toNumber()}
28 |
29 | {props.accountAddress === mintedBy &&
30 | props.accountAddress !== previousOwner ? (
31 |
32 | Minted
33 |
34 | ) : (
35 |
Bought
36 | )}
37 |
38 | );
39 | };
40 |
41 | export default MyCryptoBoyNFTDetails;
42 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "cryptoboys",
3 | "version": "0.1.0",
4 | "description": "Crypto Boys NFT Marketplace",
5 | "author": "devpavan04",
6 | "homepage": "https://devpavan04.github.io/cryptoboys-nft-marketplace",
7 | "dependencies": {
8 | "@openzeppelin/contracts": "^3.4.1",
9 | "babel-polyfill": "6.26.0",
10 | "babel-preset-env": "1.7.0",
11 | "babel-preset-es2015": "6.24.1",
12 | "babel-preset-stage-2": "6.24.1",
13 | "babel-preset-stage-3": "6.24.1",
14 | "babel-register": "6.26.0",
15 | "bootstrap": "4.3.1",
16 | "chai": "4.2.0",
17 | "chai-as-promised": "7.1.1",
18 | "chai-bignumber": "3.0.0",
19 | "dayjs": "^1.10.4",
20 | "ipfs-http-client": "^49.0.2",
21 | "react": "16.8.4",
22 | "react-bootstrap": "1.0.0-beta.5",
23 | "react-dom": "16.8.4",
24 | "react-router-dom": "^5.2.0",
25 | "react-scripts": "2.1.3",
26 | "truffle": "5.0.5",
27 | "truffle-flattener": "^1.5.0",
28 | "web3": "1.0.0-beta.55"
29 | },
30 | "scripts": {
31 | "start": "react-scripts start",
32 | "build": "react-scripts build && cp build/index.html build/404.html",
33 | "test": "react-scripts test",
34 | "eject": "react-scripts eject",
35 | "predeploy": "npm run build",
36 | "deploy": "gh-pages -d build"
37 | },
38 | "eslintConfig": {
39 | "extends": "react-app"
40 | },
41 | "browserslist": [
42 | ">0.2%",
43 | "not dead",
44 | "not ie <= 11",
45 | "not op_mini all"
46 | ],
47 | "devDependencies": {
48 | "gh-pages": "^3.1.0"
49 | }
50 | }
51 |
--------------------------------------------------------------------------------
/src/components/Navbar/Navbar.jsx:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import icon from "./favicon-32x32.png";
3 | import { Link } from "react-router-dom";
4 |
5 | const Navbar = () => {
6 | return (
7 |
8 |
9 |
10 |
11 | NFT's
12 |
13 |
18 |
19 |
20 |
21 |
25 |
26 |
27 | Home
28 |
29 |
30 |
31 |
32 | Mint NFT
33 |
34 |
35 |
36 |
37 | Marketplace
38 |
39 |
40 |
41 |
42 | My Tokens
43 |
44 |
45 |
46 |
47 | Queries
48 |
49 |
50 |
51 |
52 |
53 |
54 | );
55 | };
56 |
57 | export default Navbar;
58 |
--------------------------------------------------------------------------------
/src/components/AllCryptoBoys/AllCryptoBoys.jsx:
--------------------------------------------------------------------------------
1 | import React, { useState, useEffect } from "react";
2 | import CryptoBoyNFTImage from "../CryptoBoyNFTImage/CryptoBoyNFTImage";
3 | import CryptoBoyNFTDetails from "../CryptoBoyNFTDetails/CryptoBoyNFTDetails";
4 | import Loading from "../Loading/Loading";
5 |
6 | const AllCryptoBoys = ({
7 | cryptoBoys,
8 | accountAddress,
9 | totalTokensMinted,
10 | changeTokenPrice,
11 | toggleForSale,
12 | buyCryptoBoy,
13 | }) => {
14 | const [loading, setLoading] = useState(false);
15 |
16 | useEffect(() => {
17 | if (cryptoBoys.length !== 0) {
18 | if (cryptoBoys[0].metaData !== undefined) {
19 | setLoading(loading);
20 | } else {
21 | setLoading(false);
22 | }
23 | }
24 | }, [cryptoBoys]);
25 |
26 | return (
27 |
28 |
29 |
30 |
31 | Total No. of CryptoBoy's Minted On The Platform :{" "}
32 | {totalTokensMinted}
33 |
34 |
35 |
36 |
37 | {cryptoBoys.map((cryptoboy) => {
38 | return (
39 |
43 | {!loading ? (
44 |
51 | ) : (
52 |
53 | )}
54 |
61 |
62 | );
63 | })}
64 |
65 |
66 | );
67 | };
68 |
69 | export default AllCryptoBoys;
70 |
--------------------------------------------------------------------------------
/src/components/MyCryptoBoys/MyCryptoBoys.jsx:
--------------------------------------------------------------------------------
1 | import React, { useState, useEffect } from "react";
2 | import CryptoBoyNFTImage from "../CryptoBoyNFTImage/CryptoBoyNFTImage";
3 | import MyCryptoBoyNFTDetails from "../MyCryptoBoyNFTDetails/MyCryptoBoyNFTDetails";
4 | import Loading from "../Loading/Loading";
5 |
6 | const MyCryptoBoys = ({
7 | accountAddress,
8 | cryptoBoys,
9 | totalTokensOwnedByAccount,
10 | }) => {
11 | const [loading, setLoading] = useState(false);
12 | const [myCryptoBoys, setMyCryptoBoys] = useState([]);
13 |
14 | useEffect(() => {
15 | if (cryptoBoys.length !== 0) {
16 | if (cryptoBoys[0].metaData !== undefined) {
17 | setLoading(loading);
18 | } else {
19 | setLoading(false);
20 | }
21 | }
22 | const my_crypto_boys = cryptoBoys.filter(
23 | (cryptoboy) => cryptoboy.currentOwner === accountAddress
24 | );
25 | setMyCryptoBoys(my_crypto_boys);
26 | }, [cryptoBoys]);
27 |
28 | return (
29 |
30 |
31 |
32 |
33 | Total No. of CryptoBoy's You Own : {totalTokensOwnedByAccount}
34 |
35 |
36 |
37 |
38 | {myCryptoBoys.map((cryptoboy) => {
39 | return (
40 |
44 |
45 |
46 | {!loading ? (
47 |
54 | ) : (
55 |
56 | )}
57 |
58 |
59 |
63 |
64 |
65 |
66 | );
67 | })}
68 |
69 |
70 | );
71 | };
72 |
73 | export default MyCryptoBoys;
74 |
--------------------------------------------------------------------------------
/public/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
12 |
13 |
17 |
18 |
27 |
28 |
29 |
30 |
31 | Crypto Boy NFT
32 |
33 |
34 | You need to enable JavaScript to run this app.
35 |
36 |
46 |
47 |
52 |
57 |
62 |
63 |
64 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # README
2 |
3 | ## Crypto Boy NFT Marketplace
4 |
5 | _NFT marketplace DApp where users mint ERC721 implemented Crypto Boy NFTs._
6 |
7 | #### Features
8 |
9 | * Mint custom ERC721 implemented Crypto Boy Tokens.
10 | * Sell Crypto Boy tokens on the marketplace.
11 | * Set desired token price.
12 | * Toggle between keeping the token for sale and not for sale.
13 | * Keeps track of all the tokens owned by an account - minted and bought.
14 | * Query blockchain for token owner and token metadata.
15 | * User can mint a token only after every 5 minutes.
16 |
17 | ##
18 |
19 | #### Stack
20 |
21 | * [Solidity](https://docs.soliditylang.org/en/v0.7.6/) - Object-oriented, high-level language for implementing smart contracts.
22 | * [Bootstrap 4](https://getbootstrap.com/) - CSS framework for faster and easier web development.
23 | * [React.js](https://reactjs.org/) - JavaScript library for building user interfaces.
24 | * [web3.js](https://web3js.readthedocs.io/en/v1.3.4/) - Allows users to interact with a local or remote ethereum node using HTTP, IPC or WebSocket.
25 | * [Truffle](https://www.trufflesuite.com/truffle) - Development environment, testing framework and asset pipeline for blockchains using the Ethereum Virtual Machine (EVM).
26 | * [Ganache](https://www.trufflesuite.com/ganache) - Personal blockchain for Ethereum development used to deploy contracts, develop DApps, and run tests.
27 |
28 | ##
29 |
30 | #### Interact with the deployed DApp
31 |
32 | * Crypto Boy Marketplace DApp requires [Metamask](https://metamask.io/) browser wallet extension to interact with.
33 | * Connect metamask browser wallet to Kovan Test Network.
34 | * Request and get test etheres for the metamask account from [Kovan Faucet](https://gitter.im/kovan-testnet/faucet) to make transactions.
35 | * Crypto Boy Marketplace Smart Contract is deployed to Kovan Testnet - [0x420d2a6E87D87992EB01e5BFe762B3F437dBfD85](https://kovan.etherscan.io/address/0x420d2a6e87d87992eb01e5bfe762b3f437dbfd85)
36 | * Access Crypto Boy Marketplace DApp at [cryptoboys-NFT-marketplace](https://devpavan04.github.io/cryptoboys-nft-marketplace/) and start minting your Crypto Boys.
37 |
38 | ##
39 |
40 | #### Run the DApp Locally
41 |
42 | **Install truffle**
43 |
44 | ```
45 | npm install -g truffle
46 | ```
47 |
48 | **Install ganache-cli**
49 |
50 | ```
51 | npm i ganache-cli
52 | ```
53 |
54 | **Run ganache-cli**
55 |
56 | ```
57 | ganache-cli --port 7545
58 | ```
59 |
60 | **Open new terminal window and clone this repository**
61 |
62 | ```
63 | git clone https://github.com/devpavan04/cryptoboys-NFT-marketplace.git
64 | ```
65 |
66 | **Install dependencies**
67 |
68 | ```
69 | cd cryptoboys-NFT-marketplace
70 | npm install
71 | ```
72 |
73 | **Compile smart contract**
74 |
75 | ```
76 | truffle compile
77 | ```
78 |
79 | **Deploy smart contract to ganache**
80 |
81 | ```
82 | truffle migrate
83 | ```
84 |
85 | **Test smart contract**
86 |
87 | ```
88 | truffle test
89 | ```
90 |
91 | **Start DApp**
92 |
93 | ```
94 | npm start
95 | ```
96 |
97 | * Open metamask browser wallet and connect network to Localhost 7545.
98 | * Import accounts from ganache-cli into the metamask browser wallet to make transactions on the DApp.
99 |
--------------------------------------------------------------------------------
/src/components/ConnectMetamask/metamask.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/components/CryptoBoyNFTImage/CryptoBoyNFTImage.jsx:
--------------------------------------------------------------------------------
1 | import React from "react";
2 |
3 | const CryptoBoyNFTImage = ({ colors }) => {
4 | const {
5 | cardBorderColor,
6 | cardBackgroundColor,
7 | headBorderColor,
8 | headBackgroundColor,
9 | leftEyeBorderColor,
10 | rightEyeBorderColor,
11 | leftEyeBackgroundColor,
12 | rightEyeBackgroundColor,
13 | leftPupilBackgroundColor,
14 | rightPupilBackgroundColor,
15 | mouthColor,
16 | neckBackgroundColor,
17 | neckBorderColor,
18 | bodyBackgroundColor,
19 | bodyBorderColor,
20 | } = colors;
21 |
22 | const cryptoboy_card = {
23 | width: "280px",
24 | height: "260px",
25 | margin: "auto",
26 | backgroundColor: `${cardBackgroundColor}`,
27 | border: `10px solid ${cardBorderColor}`,
28 | };
29 |
30 | const head = {
31 | zIndex: "1",
32 | width: "100px",
33 | height: "100px",
34 | borderRadius: "50%",
35 | margin: "2rem auto 0",
36 | border: `8px solid ${headBorderColor}`,
37 | backgroundColor: `${headBackgroundColor}`,
38 | position: "relative",
39 | };
40 |
41 | const eyeLeft = {
42 | zIndex: "1",
43 | width: "60px",
44 | height: "60px",
45 | backgroundColor: `${leftEyeBackgroundColor}`,
46 | borderRadius: "50%",
47 | position: "absolute",
48 | top: "0rem",
49 | left: "-1.5rem",
50 | border: `6px solid ${leftEyeBorderColor}`,
51 | };
52 |
53 | const eyeRight = {
54 | zIndex: "1",
55 | width: "70px",
56 | height: "70px",
57 | backgroundColor: `${rightEyeBackgroundColor}`,
58 | borderRadius: "50%",
59 | position: "absolute",
60 | top: "-1.2rem",
61 | left: "2.8rem",
62 | border: `6px solid ${rightEyeBorderColor}`,
63 | };
64 |
65 | const pupilLeft = {
66 | width: "20px",
67 | height: "20px",
68 | backgroundColor: `${leftPupilBackgroundColor}`,
69 | borderRadius: "50%",
70 | position: "absolute",
71 | left: "1rem",
72 | top: "1rem",
73 | };
74 |
75 | const pupilRight = {
76 | width: "30px",
77 | height: "30px",
78 | backgroundColor: `${rightPupilBackgroundColor}`,
79 | borderRadius: "50%",
80 | position: "absolute",
81 | left: "1rem",
82 | top: "1rem",
83 | };
84 |
85 | const mouth = {
86 | position: "absolute",
87 | top: "12px",
88 | left: "0",
89 | right: "0",
90 | height: "60px",
91 | width: "60px",
92 | margin: "3px auto",
93 | borderRadius: "100%",
94 | borderBottom: `8px solid ${mouthColor}`,
95 | };
96 |
97 | const neck = {
98 | position: "relative",
99 | left: "7.7rem",
100 | top: "-0.1rem",
101 | width: "15px",
102 | height: "30px",
103 | backgroundColor: `${neckBackgroundColor}`,
104 | border: `4px solid ${neckBorderColor}`,
105 | };
106 |
107 | const body = {
108 | height: "50px",
109 | width: "90px",
110 | margin: "-0.4rem auto",
111 | border: `5px solid ${bodyBorderColor}`,
112 | borderRadius: "100% 100% 100% 100% / 100% 100% 0% 0%",
113 | backgroundColor: `${bodyBackgroundColor}`,
114 | position: "relative",
115 | left: "0.1rem",
116 | };
117 |
118 | const leftHand = {
119 | position: "absolute",
120 | top: "20px",
121 | width: "15px",
122 | height: "20px",
123 | borderRight: `5px solid ${bodyBorderColor}`,
124 | };
125 |
126 | const rightHand = {
127 | position: "absolute",
128 | top: "20px",
129 | width: "15px",
130 | height: "20px",
131 | borderRight: `5px solid ${bodyBorderColor}`,
132 | right: "1rem",
133 | };
134 |
135 | return (
136 |
152 | );
153 | };
154 |
155 | export default CryptoBoyNFTImage;
156 |
--------------------------------------------------------------------------------
/src/components/Queries/Queries.jsx:
--------------------------------------------------------------------------------
1 | import React, { useState } from "react";
2 |
3 | const Queries = (props) => {
4 | const [tokenIdForOwner, setTokenIdForOwner] = useState("");
5 | const [tokenOwner, setTokenOwner] = useState("");
6 | const [tokenIdForOwnerNotFound, setTokenIdForOwnerNotFound] = useState(false);
7 |
8 | const [tokenIdForMetadata, setTokenIdForMetadata] = useState("");
9 | const [tokenMetadata, setTokenMetadata] = useState("");
10 | const [tokenMetadataLink, setTokenMetadataLink] = useState("");
11 | const [tokenIdForMetadataNotFound, setTokenIdForMetadataNotFound] = useState(
12 | false
13 | );
14 |
15 | const getTokenOwner = async (e) => {
16 | e.preventDefault();
17 | try {
18 | const owner = await props.cryptoBoysContract.methods
19 | .getTokenOwner(tokenIdForOwner)
20 | .call();
21 | setTokenOwner(owner);
22 | setTimeout(() => {
23 | setTokenOwner("");
24 | setTokenIdForOwner("");
25 | }, 5000);
26 | } catch (e) {
27 | setTokenIdForOwnerNotFound(true);
28 | setTokenIdForOwner("");
29 | }
30 | };
31 |
32 | const getTokenMetadata = async (e) => {
33 | e.preventDefault();
34 | try {
35 | const metadata = await props.cryptoBoysContract.methods
36 | .getTokenMetaData(tokenIdForMetadata)
37 | .call();
38 | setTokenMetadata(
39 | metadata.substr(0, 60) + "..." + metadata.slice(metadata.length - 5)
40 | );
41 | setTokenMetadataLink(metadata);
42 | setTimeout(() => {
43 | setTokenMetadata("");
44 | setTokenIdForMetadata("");
45 | }, 5000);
46 | } catch (e) {
47 | setTokenIdForMetadataNotFound(true);
48 | setTokenIdForMetadata("");
49 | }
50 | };
51 |
52 | return (
53 |
54 |
55 |
56 |
Queries
57 |
58 |
59 |
60 |
61 |
62 |
Get Token Owner
63 |
86 |
{tokenOwner}
87 |
88 |
123 |
124 |
125 |
126 | );
127 | };
128 |
129 | export default Queries;
130 |
--------------------------------------------------------------------------------
/src/serviceWorker.js:
--------------------------------------------------------------------------------
1 | // This optional code is used to register a service worker.
2 | // register() is not called by default.
3 |
4 | // This lets the app load faster on subsequent visits in production, and gives
5 | // it offline capabilities. However, it also means that developers (and users)
6 | // will only see deployed updates on subsequent visits to a page, after all the
7 | // existing tabs open on the page have been closed, since previously cached
8 | // resources are updated in the background.
9 |
10 | // To learn more about the benefits of this model and instructions on how to
11 | // opt-in, read https://bit.ly/CRA-PWA
12 |
13 | const isLocalhost = Boolean(
14 | window.location.hostname === 'localhost' ||
15 | // [::1] is the IPv6 localhost address.
16 | window.location.hostname === '[::1]' ||
17 | // 127.0.0.1/8 is considered localhost for IPv4.
18 | window.location.hostname.match(
19 | /^127(?:\.(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)){3}$/
20 | )
21 | );
22 |
23 | export function register(config) {
24 | if (process.env.NODE_ENV === 'production' && 'serviceWorker' in navigator) {
25 | // The URL constructor is available in all browsers that support SW.
26 | const publicUrl = new URL(process.env.PUBLIC_URL, window.location.href);
27 | if (publicUrl.origin !== window.location.origin) {
28 | // Our service worker won't work if PUBLIC_URL is on a different origin
29 | // from what our page is served on. This might happen if a CDN is used to
30 | // serve assets; see https://github.com/facebook/create-react-app/issues/2374
31 | return;
32 | }
33 |
34 | window.addEventListener('load', () => {
35 | const swUrl = `${process.env.PUBLIC_URL}/service-worker.js`;
36 |
37 | if (isLocalhost) {
38 | // This is running on localhost. Let's check if a service worker still exists or not.
39 | checkValidServiceWorker(swUrl, config);
40 |
41 | // Add some additional logging to localhost, pointing developers to the
42 | // service worker/PWA documentation.
43 | navigator.serviceWorker.ready.then(() => {
44 | console.log(
45 | 'This web app is being served cache-first by a service ' +
46 | 'worker. To learn more, visit https://bit.ly/CRA-PWA'
47 | );
48 | });
49 | } else {
50 | // Is not localhost. Just register service worker
51 | registerValidSW(swUrl, config);
52 | }
53 | });
54 | }
55 | }
56 |
57 | function registerValidSW(swUrl, config) {
58 | navigator.serviceWorker
59 | .register(swUrl)
60 | .then(registration => {
61 | registration.onupdatefound = () => {
62 | const installingWorker = registration.installing;
63 | if (installingWorker == null) {
64 | return;
65 | }
66 | installingWorker.onstatechange = () => {
67 | if (installingWorker.state === 'installed') {
68 | if (navigator.serviceWorker.controller) {
69 | // At this point, the updated precached content has been fetched,
70 | // but the previous service worker will still serve the older
71 | // content until all client tabs are closed.
72 | console.log(
73 | 'New content is available and will be used when all ' +
74 | 'tabs for this page are closed. See https://bit.ly/CRA-PWA.'
75 | );
76 |
77 | // Execute callback
78 | if (config && config.onUpdate) {
79 | config.onUpdate(registration);
80 | }
81 | } else {
82 | // At this point, everything has been precached.
83 | // It's the perfect time to display a
84 | // "Content is cached for offline use." message.
85 | console.log('Content is cached for offline use.');
86 |
87 | // Execute callback
88 | if (config && config.onSuccess) {
89 | config.onSuccess(registration);
90 | }
91 | }
92 | }
93 | };
94 | };
95 | })
96 | .catch(error => {
97 | console.error('Error during service worker registration:', error);
98 | });
99 | }
100 |
101 | function checkValidServiceWorker(swUrl, config) {
102 | // Check if the service worker can be found. If it can't reload the page.
103 | fetch(swUrl)
104 | .then(response => {
105 | // Ensure service worker exists, and that we really are getting a JS file.
106 | const contentType = response.headers.get('content-type');
107 | if (
108 | response.status === 404 ||
109 | (contentType != null && contentType.indexOf('javascript') === -1)
110 | ) {
111 | // No service worker found. Probably a different app. Reload the page.
112 | navigator.serviceWorker.ready.then(registration => {
113 | registration.unregister().then(() => {
114 | window.location.reload();
115 | });
116 | });
117 | } else {
118 | // Service worker found. Proceed as normal.
119 | registerValidSW(swUrl, config);
120 | }
121 | })
122 | .catch(() => {
123 | console.log(
124 | 'No internet connection found. App is running in offline mode.'
125 | );
126 | });
127 | }
128 |
129 | export function unregister() {
130 | if ('serviceWorker' in navigator) {
131 | navigator.serviceWorker.ready.then(registration => {
132 | registration.unregister();
133 | });
134 | }
135 | }
136 |
--------------------------------------------------------------------------------
/src/components/CryptoBoyNFTDetails/CryptoBoyNFTDetails.jsx:
--------------------------------------------------------------------------------
1 | import React, { Component } from "react";
2 |
3 | class CryptoBoyNFTDetails extends Component {
4 | constructor(props) {
5 | super(props);
6 | this.state = {
7 | newCryptoBoyPrice: "",
8 | };
9 | }
10 |
11 | callChangeTokenPriceFromApp = (tokenId, newPrice) => {
12 | this.props.changeTokenPrice(tokenId, newPrice);
13 | };
14 |
15 | render() {
16 | return (
17 |
18 |
19 | Token Id :{" "}
20 | {this.props.cryptoboy.tokenId.toNumber()}
21 |
22 |
23 | Name :{" "}
24 | {this.props.cryptoboy.tokenName}
25 |
26 |
27 | Minted By :{" "}
28 | {this.props.cryptoboy.mintedBy.substr(0, 5) +
29 | "..." +
30 | this.props.cryptoboy.mintedBy.slice(
31 | this.props.cryptoboy.mintedBy.length - 5
32 | )}
33 |
34 |
35 | Owned By :{" "}
36 | {this.props.cryptoboy.currentOwner.substr(0, 5) +
37 | "..." +
38 | this.props.cryptoboy.currentOwner.slice(
39 | this.props.cryptoboy.currentOwner.length - 5
40 | )}
41 |
42 |
43 | Previous Owner :{" "}
44 | {this.props.cryptoboy.previousOwner.substr(0, 5) +
45 | "..." +
46 | this.props.cryptoboy.previousOwner.slice(
47 | this.props.cryptoboy.previousOwner.length - 5
48 | )}
49 |
50 |
51 | Price :{" "}
52 | {window.web3.utils.fromWei(
53 | this.props.cryptoboy.price.toString(),
54 | "Ether"
55 | )}{" "}
56 | Ξ
57 |
58 |
59 | No. of Transfers :{" "}
60 | {this.props.cryptoboy.numberOfTransfers.toNumber()}
61 |
62 |
63 | {this.props.accountAddress === this.props.cryptoboy.currentOwner ? (
64 |
100 | ) : null}
101 |
102 |
103 | {this.props.accountAddress === this.props.cryptoboy.currentOwner ? (
104 | this.props.cryptoboy.forSale ? (
105 |
109 | this.props.toggleForSale(
110 | this.props.cryptoboy.tokenId.toNumber()
111 | )
112 | }
113 | >
114 | Remove from sale
115 |
116 | ) : (
117 |
121 | this.props.toggleForSale(
122 | this.props.cryptoboy.tokenId.toNumber()
123 | )
124 | }
125 | >
126 | Keep for sale
127 |
128 | )
129 | ) : null}
130 |
131 |
132 | {this.props.accountAddress !== this.props.cryptoboy.currentOwner ? (
133 | this.props.cryptoboy.forSale ? (
134 |
139 | this.props.buyCryptoBoy(
140 | this.props.cryptoboy.tokenId.toNumber(),
141 | e.target.value
142 | )
143 | }
144 | >
145 | Buy For{" "}
146 | {window.web3.utils.fromWei(
147 | this.props.cryptoboy.price.toString(),
148 | "Ether"
149 | )}{" "}
150 | Ξ
151 |
152 | ) : (
153 | <>
154 |
159 | Buy For{" "}
160 | {window.web3.utils.fromWei(
161 | this.props.cryptoboy.price.toString(),
162 | "Ether"
163 | )}{" "}
164 | Ξ
165 |
166 |
Currently not for sale!
167 | >
168 | )
169 | ) : null}
170 |
171 |
172 | );
173 | }
174 | }
175 |
176 | export default CryptoBoyNFTDetails;
177 |
--------------------------------------------------------------------------------
/src/contracts/CryptoBoys.sol:
--------------------------------------------------------------------------------
1 | // SPDX-License-Identifier: MIT
2 | pragma solidity >=0.4.21 <0.8.0;
3 | pragma abicoder v2;
4 |
5 | // import ERC721 iterface
6 | import "./ERC721.sol";
7 |
8 | // CryptoBoys smart contract inherits ERC721 interface
9 | contract CryptoBoys is ERC721 {
10 |
11 | // this contract's token collection name
12 | string public collectionName;
13 | // this contract's token symbol
14 | string public collectionNameSymbol;
15 | // total number of crypto boys minted
16 | uint256 public cryptoBoyCounter;
17 |
18 | // define crypto boy struct
19 | struct CryptoBoy {
20 | uint256 tokenId;
21 | string tokenName;
22 | string tokenURI;
23 | address payable mintedBy;
24 | address payable currentOwner;
25 | address payable previousOwner;
26 | uint256 price;
27 | uint256 numberOfTransfers;
28 | bool forSale;
29 | }
30 |
31 | // map cryptoboy's token id to crypto boy
32 | mapping(uint256 => CryptoBoy) public allCryptoBoys;
33 | // check if token name exists
34 | mapping(string => bool) public tokenNameExists;
35 | // check if color exists
36 | mapping(string => bool) public colorExists;
37 | // check if token URI exists
38 | mapping(string => bool) public tokenURIExists;
39 |
40 | // initialize contract while deployment with contract's collection name and token
41 | constructor() ERC721("Crypto Boys Collection", "CB") {
42 | collectionName = name();
43 | collectionNameSymbol = symbol();
44 | }
45 |
46 | // mint a new crypto boy
47 | function mintCryptoBoy(string memory _name, string memory _tokenURI, uint256 _price, string[] calldata _colors) external {
48 | // check if thic fucntion caller is not an zero address account
49 | require(msg.sender != address(0));
50 | // increment counter
51 | cryptoBoyCounter ++;
52 | // check if a token exists with the above token id => incremented counter
53 | require(!_exists(cryptoBoyCounter));
54 |
55 | // loop through the colors passed and check if each colors already exists or not
56 | for(uint i=0; i<_colors.length; i++) {
57 | require(!colorExists[_colors[i]]);
58 | }
59 | // check if the token URI already exists or not
60 | require(!tokenURIExists[_tokenURI]);
61 | // check if the token name already exists or not
62 | require(!tokenNameExists[_name]);
63 |
64 | // mint the token
65 | _mint(msg.sender, cryptoBoyCounter);
66 | // set token URI (bind token id with the passed in token URI)
67 | _setTokenURI(cryptoBoyCounter, _tokenURI);
68 |
69 | // loop through the colors passed and make each of the colors as exists since the token is already minted
70 | for (uint i=0; i<_colors.length; i++) {
71 | colorExists[_colors[i]] = true;
72 | }
73 | // make passed token URI as exists
74 | tokenURIExists[_tokenURI] = true;
75 | // make token name passed as exists
76 | tokenNameExists[_name] = true;
77 |
78 | // creat a new crypto boy (struct) and pass in new values
79 | CryptoBoy memory newCryptoBoy = CryptoBoy(
80 | cryptoBoyCounter,
81 | _name,
82 | _tokenURI,
83 | msg.sender,
84 | msg.sender,
85 | address(0),
86 | _price,
87 | 0,
88 | true);
89 | // add the token id and it's crypto boy to all crypto boys mapping
90 | allCryptoBoys[cryptoBoyCounter] = newCryptoBoy;
91 | }
92 |
93 | // get owner of the token
94 | function getTokenOwner(uint256 _tokenId) public view returns(address) {
95 | address _tokenOwner = ownerOf(_tokenId);
96 | return _tokenOwner;
97 | }
98 |
99 | // get metadata of the token
100 | function getTokenMetaData(uint _tokenId) public view returns(string memory) {
101 | string memory tokenMetaData = tokenURI(_tokenId);
102 | return tokenMetaData;
103 | }
104 |
105 | // get total number of tokens minted so far
106 | function getNumberOfTokensMinted() public view returns(uint256) {
107 | uint256 totalNumberOfTokensMinted = totalSupply();
108 | return totalNumberOfTokensMinted;
109 | }
110 |
111 | // get total number of tokens owned by an address
112 | function getTotalNumberOfTokensOwnedByAnAddress(address _owner) public view returns(uint256) {
113 | uint256 totalNumberOfTokensOwned = balanceOf(_owner);
114 | return totalNumberOfTokensOwned;
115 | }
116 |
117 | // check if the token already exists
118 | function getTokenExists(uint256 _tokenId) public view returns(bool) {
119 | bool tokenExists = _exists(_tokenId);
120 | return tokenExists;
121 | }
122 |
123 | // by a token by passing in the token's id
124 | function buyToken(uint256 _tokenId) public payable {
125 | // check if the function caller is not an zero account address
126 | require(msg.sender != address(0));
127 | // check if the token id of the token being bought exists or not
128 | require(_exists(_tokenId));
129 | // get the token's owner
130 | address tokenOwner = ownerOf(_tokenId);
131 | // token's owner should not be an zero address account
132 | require(tokenOwner != address(0));
133 | // the one who wants to buy the token should not be the token's owner
134 | require(tokenOwner != msg.sender);
135 | // get that token from all crypto boys mapping and create a memory of it defined as (struct => CryptoBoy)
136 | CryptoBoy memory cryptoboy = allCryptoBoys[_tokenId];
137 | // price sent in to buy should be equal to or more than the token's price
138 | require(msg.value >= cryptoboy.price);
139 | // token should be for sale
140 | require(cryptoboy.forSale);
141 | // transfer the token from owner to the caller of the function (buyer)
142 | _transfer(tokenOwner, msg.sender, _tokenId);
143 | // get owner of the token
144 | address payable sendTo = cryptoboy.currentOwner;
145 | // send token's worth of ethers to the owner
146 | sendTo.transfer(msg.value);
147 | // update the token's previous owner
148 | cryptoboy.previousOwner = cryptoboy.currentOwner;
149 | // update the token's current owner
150 | cryptoboy.currentOwner = msg.sender;
151 | // update the how many times this token was transfered
152 | cryptoboy.numberOfTransfers += 1;
153 | // set and update that token in the mapping
154 | allCryptoBoys[_tokenId] = cryptoboy;
155 | }
156 |
157 | function changeTokenPrice(uint256 _tokenId, uint256 _newPrice) public {
158 | // require caller of the function is not an empty address
159 | require(msg.sender != address(0));
160 | // require that token should exist
161 | require(_exists(_tokenId));
162 | // get the token's owner
163 | address tokenOwner = ownerOf(_tokenId);
164 | // check that token's owner should be equal to the caller of the function
165 | require(tokenOwner == msg.sender);
166 | // get that token from all crypto boys mapping and create a memory of it defined as (struct => CryptoBoy)
167 | CryptoBoy memory cryptoboy = allCryptoBoys[_tokenId];
168 | // update token's price with new price
169 | cryptoboy.price = _newPrice;
170 | // set and update that token in the mapping
171 | allCryptoBoys[_tokenId] = cryptoboy;
172 | }
173 |
174 | // switch between set for sale and set not for sale
175 | function toggleForSale(uint256 _tokenId) public {
176 | // require caller of the function is not an empty address
177 | require(msg.sender != address(0));
178 | // require that token should exist
179 | require(_exists(_tokenId));
180 | // get the token's owner
181 | address tokenOwner = ownerOf(_tokenId);
182 | // check that token's owner should be equal to the caller of the function
183 | require(tokenOwner == msg.sender);
184 | // get that token from all crypto boys mapping and create a memory of it defined as (struct => CryptoBoy)
185 | CryptoBoy memory cryptoboy = allCryptoBoys[_tokenId];
186 | // if token's forSale is false make it true and vice versa
187 | if(cryptoboy.forSale) {
188 | cryptoboy.forSale = false;
189 | } else {
190 | cryptoboy.forSale = true;
191 | }
192 | // set and update that token in the mapping
193 | allCryptoBoys[_tokenId] = cryptoboy;
194 | }
195 | }
--------------------------------------------------------------------------------
/src/components/App.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from "react";
2 | import { HashRouter, Route } from "react-router-dom";
3 | import "./App.css";
4 | import Web3 from "web3";
5 | import CryptoBoys from "../abis/CryptoBoys.json";
6 |
7 | import FormAndPreview from "../components/FormAndPreview/FormAndPreview";
8 | import AllCryptoBoys from "./AllCryptoBoys/AllCryptoBoys";
9 | import AccountDetails from "./AccountDetails/AccountDetails";
10 | import ContractNotDeployed from "./ContractNotDeployed/ContractNotDeployed";
11 | import ConnectToMetamask from "./ConnectMetamask/ConnectToMetamask";
12 | import Loading from "./Loading/Loading";
13 | import Navbar from "./Navbar/Navbar";
14 | import MyCryptoBoys from "./MyCryptoBoys/MyCryptoBoys";
15 | import Queries from "./Queries/Queries";
16 |
17 | const ipfsClient = require("ipfs-http-client");
18 | const ipfs = ipfsClient({
19 | host: "ipfs.infura.io",
20 | port: 5001,
21 | protocol: "https",
22 | });
23 |
24 | class App extends Component {
25 | constructor(props) {
26 | super(props);
27 | this.state = {
28 | accountAddress: "",
29 | accountBalance: "",
30 | cryptoBoysContract: null,
31 | cryptoBoysCount: 0,
32 | cryptoBoys: [],
33 | loading: true,
34 | metamaskConnected: false,
35 | contractDetected: false,
36 | totalTokensMinted: 0,
37 | totalTokensOwnedByAccount: 0,
38 | nameIsUsed: false,
39 | colorIsUsed: false,
40 | colorsUsed: [],
41 | lastMintTime: null,
42 | };
43 | }
44 |
45 | componentWillMount = async () => {
46 | await this.loadWeb3();
47 | await this.loadBlockchainData();
48 | await this.setMetaData();
49 | await this.setMintBtnTimer();
50 | };
51 |
52 | setMintBtnTimer = () => {
53 | const mintBtn = document.getElementById("mintBtn");
54 | if (mintBtn !== undefined && mintBtn !== null) {
55 | this.setState({
56 | lastMintTime: localStorage.getItem(this.state.accountAddress),
57 | });
58 | this.state.lastMintTime === undefined || this.state.lastMintTime === null
59 | ? (mintBtn.innerHTML = "Mint My Crypto Boy")
60 | : this.checkIfCanMint(parseInt(this.state.lastMintTime));
61 | }
62 | };
63 |
64 | checkIfCanMint = (lastMintTime) => {
65 | const mintBtn = document.getElementById("mintBtn");
66 | const timeGap = 300000; //5min in milliseconds
67 | const countDownTime = lastMintTime + timeGap;
68 | const interval = setInterval(() => {
69 | const now = new Date().getTime();
70 | const diff = countDownTime - now;
71 | if (diff < 0) {
72 | mintBtn.removeAttribute("disabled");
73 | mintBtn.innerHTML = "Mint My Crypto Boy";
74 | localStorage.removeItem(this.state.accountAddress);
75 | clearInterval(interval);
76 | } else {
77 | const minutes = Math.floor((diff % (1000 * 60 * 60)) / (1000 * 60));
78 | const seconds = Math.floor((diff % (1000 * 60)) / 1000);
79 | mintBtn.setAttribute("disabled", true);
80 | mintBtn.innerHTML = `Next mint in ${minutes}m ${seconds}s`;
81 | }
82 | }, 1000);
83 | };
84 |
85 | loadWeb3 = async () => {
86 | if (window.ethereum) {
87 | window.web3 = new Web3(window.ethereum);
88 | } else if (window.web3) {
89 | window.web3 = new Web3(window.web3.currentProvider);
90 | } else {
91 | window.alert(
92 | "Non-Ethereum browser detected. You should consider trying MetaMask!"
93 | );
94 | }
95 | };
96 |
97 | loadBlockchainData = async () => {
98 | const web3 = window.web3;
99 | const accounts = await web3.eth.getAccounts();
100 | if (accounts.length === 0) {
101 | this.setState({ metamaskConnected: false });
102 | } else {
103 | this.setState({ metamaskConnected: true });
104 | this.setState({ loading: true });
105 | this.setState({ accountAddress: accounts[0] });
106 | let accountBalance = await web3.eth.getBalance(accounts[0]);
107 | accountBalance = web3.utils.fromWei(accountBalance, "Ether");
108 | this.setState({ accountBalance });
109 | this.setState({ loading: false });
110 | const networkId = await web3.eth.net.getId();
111 | const networkData = CryptoBoys.networks[networkId];
112 | if (networkData) {
113 | this.setState({ loading: true });
114 | const cryptoBoysContract = web3.eth.Contract(
115 | CryptoBoys.abi,
116 | networkData.address
117 | );
118 | this.setState({ cryptoBoysContract });
119 | this.setState({ contractDetected: true });
120 | const cryptoBoysCount = await cryptoBoysContract.methods
121 | .cryptoBoyCounter()
122 | .call();
123 | this.setState({ cryptoBoysCount });
124 | for (var i = 1; i <= cryptoBoysCount; i++) {
125 | const cryptoBoy = await cryptoBoysContract.methods
126 | .allCryptoBoys(i)
127 | .call();
128 | this.setState({
129 | cryptoBoys: [...this.state.cryptoBoys, cryptoBoy],
130 | });
131 | }
132 | let totalTokensMinted = await cryptoBoysContract.methods
133 | .getNumberOfTokensMinted()
134 | .call();
135 | totalTokensMinted = totalTokensMinted.toNumber();
136 | this.setState({ totalTokensMinted });
137 | let totalTokensOwnedByAccount = await cryptoBoysContract.methods
138 | .getTotalNumberOfTokensOwnedByAnAddress(this.state.accountAddress)
139 | .call();
140 | totalTokensOwnedByAccount = totalTokensOwnedByAccount.toNumber();
141 | this.setState({ totalTokensOwnedByAccount });
142 | this.setState({ loading: false });
143 | } else {
144 | this.setState({ contractDetected: false });
145 | }
146 | }
147 | };
148 |
149 | connectToMetamask = async () => {
150 | await window.ethereum.enable();
151 | this.setState({ metamaskConnected: true });
152 | window.location.reload();
153 | };
154 |
155 | setMetaData = async () => {
156 | if (this.state.cryptoBoys.length !== 0) {
157 | this.state.cryptoBoys.map(async (cryptoboy) => {
158 | const result = await fetch(cryptoboy.tokenURI);
159 | const metaData = await result.json();
160 | this.setState({
161 | cryptoBoys: this.state.cryptoBoys.map((cryptoboy) =>
162 | cryptoboy.tokenId.toNumber() === Number(metaData.tokenId)
163 | ? {
164 | ...cryptoboy,
165 | metaData,
166 | }
167 | : cryptoboy
168 | ),
169 | });
170 | });
171 | }
172 | };
173 |
174 | mintMyNFT = async (colors, name, tokenPrice) => {
175 | this.setState({ loading: true });
176 | const colorsArray = Object.values(colors);
177 | let colorsUsed = [];
178 | for (let i = 0; i < colorsArray.length; i++) {
179 | if (colorsArray[i] !== "") {
180 | let colorIsUsed = await this.state.cryptoBoysContract.methods
181 | .colorExists(colorsArray[i])
182 | .call();
183 | if (colorIsUsed) {
184 | colorsUsed = [...colorsUsed, colorsArray[i]];
185 | } else {
186 | continue;
187 | }
188 | }
189 | }
190 | const nameIsUsed = await this.state.cryptoBoysContract.methods
191 | .tokenNameExists(name)
192 | .call();
193 | if (colorsUsed.length === 0 && !nameIsUsed) {
194 | const {
195 | cardBorderColor,
196 | cardBackgroundColor,
197 | headBorderColor,
198 | headBackgroundColor,
199 | leftEyeBorderColor,
200 | rightEyeBorderColor,
201 | leftEyeBackgroundColor,
202 | rightEyeBackgroundColor,
203 | leftPupilBackgroundColor,
204 | rightPupilBackgroundColor,
205 | mouthColor,
206 | neckBackgroundColor,
207 | neckBorderColor,
208 | bodyBackgroundColor,
209 | bodyBorderColor,
210 | } = colors;
211 | let previousTokenId;
212 | previousTokenId = await this.state.cryptoBoysContract.methods
213 | .cryptoBoyCounter()
214 | .call();
215 | previousTokenId = previousTokenId.toNumber();
216 | const tokenId = previousTokenId + 1;
217 | const tokenObject = {
218 | tokenName: "Crypto Boy",
219 | tokenSymbol: "CB",
220 | tokenId: `${tokenId}`,
221 | name: name,
222 | metaData: {
223 | type: "color",
224 | colors: {
225 | cardBorderColor,
226 | cardBackgroundColor,
227 | headBorderColor,
228 | headBackgroundColor,
229 | leftEyeBorderColor,
230 | rightEyeBorderColor,
231 | leftEyeBackgroundColor,
232 | rightEyeBackgroundColor,
233 | leftPupilBackgroundColor,
234 | rightPupilBackgroundColor,
235 | mouthColor,
236 | neckBackgroundColor,
237 | neckBorderColor,
238 | bodyBackgroundColor,
239 | bodyBorderColor,
240 | },
241 | },
242 | };
243 | const cid = await ipfs.add(JSON.stringify(tokenObject));
244 | let tokenURI = `https://ipfs.infura.io/ipfs/${cid.path}`;
245 | const price = window.web3.utils.toWei(tokenPrice.toString(), "Ether");
246 | this.state.cryptoBoysContract.methods
247 | .mintCryptoBoy(name, tokenURI, price, colorsArray)
248 | .send({ from: this.state.accountAddress })
249 | .on("confirmation", () => {
250 | localStorage.setItem(this.state.accountAddress, new Date().getTime());
251 | this.setState({ loading: false });
252 | window.location.reload();
253 | });
254 | } else {
255 | if (nameIsUsed) {
256 | this.setState({ nameIsUsed: true });
257 | this.setState({ loading: false });
258 | } else if (colorsUsed.length !== 0) {
259 | this.setState({ colorIsUsed: true });
260 | this.setState({ colorsUsed });
261 | this.setState({ loading: false });
262 | }
263 | }
264 | };
265 |
266 | toggleForSale = (tokenId) => {
267 | this.setState({ loading: true });
268 | this.state.cryptoBoysContract.methods
269 | .toggleForSale(tokenId)
270 | .send({ from: this.state.accountAddress })
271 | .on("confirmation", () => {
272 | this.setState({ loading: false });
273 | window.location.reload();
274 | });
275 | };
276 |
277 | changeTokenPrice = (tokenId, newPrice) => {
278 | this.setState({ loading: true });
279 | const newTokenPrice = window.web3.utils.toWei(newPrice, "Ether");
280 | this.state.cryptoBoysContract.methods
281 | .changeTokenPrice(tokenId, newTokenPrice)
282 | .send({ from: this.state.accountAddress })
283 | .on("confirmation", () => {
284 | this.setState({ loading: false });
285 | window.location.reload();
286 | });
287 | };
288 |
289 | buyCryptoBoy = (tokenId, price) => {
290 | this.setState({ loading: true });
291 | this.state.cryptoBoysContract.methods
292 | .buyToken(tokenId)
293 | .send({ from: this.state.accountAddress, value: price })
294 | .on("confirmation", () => {
295 | this.setState({ loading: false });
296 | window.location.reload();
297 | });
298 | };
299 |
300 | render() {
301 | return (
302 |
303 | {!this.state.metamaskConnected ? (
304 |
305 | ) : !this.state.contractDetected ? (
306 |
307 | ) : this.state.loading ? (
308 |
309 | ) : (
310 | <>
311 |
312 |
313 | (
317 |
321 | )}
322 | />
323 | (
326 |
333 | )}
334 | />
335 | (
338 |
346 | )}
347 | />
348 | (
351 |
358 | )}
359 | />
360 | (
363 |
364 | )}
365 | />
366 |
367 | >
368 | )}
369 |
370 | );
371 | }
372 | }
373 |
374 | export default App;
375 |
--------------------------------------------------------------------------------
/test/CryptoBoys.test.js:
--------------------------------------------------------------------------------
1 | const { assert } = require("chai");
2 |
3 | const CryptoBoys = artifacts.require("./CryptoBoys.sol");
4 |
5 | require("chai")
6 | .use(require("chai-as-promised"))
7 | .should();
8 |
9 | contract("Crypto Boys", async (accounts) => {
10 | let cryptoBoys, result, cryptoBoyCount;
11 |
12 | before(async () => {
13 | cryptoBoys = await CryptoBoys.deployed();
14 | });
15 |
16 | describe("Deployment", async () => {
17 | it("contract has an address", async () => {
18 | const address = await cryptoBoys.address;
19 | assert.notEqual(address, 0x0);
20 | assert.notEqual(address, "");
21 | assert.notEqual(address, null);
22 | assert.notEqual(address, undefined);
23 | });
24 |
25 | it("has a name", async () => {
26 | const name = await cryptoBoys.collectionName();
27 | assert.equal(name, "Crypto Boys Collection");
28 | });
29 |
30 | it("has a symbol", async () => {
31 | const symbol = await cryptoBoys.collectionNameSymbol();
32 | assert.equal(symbol, "CB");
33 | });
34 | });
35 |
36 | describe("application features", async () => {
37 | it("allows users to mint ERC721 token", async () => {
38 | cryptoBoyCount = await cryptoBoys.cryptoBoyCounter();
39 | assert.equal(cryptoBoyCount.toNumber(), 0);
40 |
41 | let tokenExists;
42 | tokenExists = await cryptoBoys.getTokenExists(1, { from: accounts[0] });
43 | assert.equal(tokenExists, false);
44 |
45 | let tokenURIExists;
46 | tokenURIExists = await cryptoBoys.tokenURIExists(
47 | "https://gateway.pinata.cloud/ipfs/QmYFmJgQGH4uPHRYN15Xdv4aLd9o4Aq63y1e4GgN6kj5aK/2",
48 | { from: accounts[0] }
49 | );
50 | assert.equal(tokenURIExists, false);
51 |
52 | let tokenNameExists;
53 | tokenNameExists = await cryptoBoys.tokenNameExists("myCBNFT", {
54 | from: accounts[0],
55 | });
56 | assert.equal(tokenNameExists, false);
57 |
58 | let colorExists;
59 | const colorsArray1 = [
60 | "#2a2b2e",
61 | "#5a5a66",
62 | "#a4c2a8",
63 | "#aceb98",
64 | "#87ff65",
65 | "#995d81",
66 | "#eb8258",
67 | "#f6f740",
68 | "#d8dc6a",
69 | "#6689a1",
70 | "#fe938c",
71 | "#e6b89c",
72 | "#ead2ac",
73 | "#9cafb7",
74 | "#4281a4",
75 | ];
76 | for (let i = 0; i < colorsArray1.length; i++) {
77 | colorExists = await cryptoBoys.colorExists(colorsArray1[i], {
78 | from: accounts[0],
79 | });
80 | assert.equal(colorExists, false);
81 | }
82 |
83 | result = await cryptoBoys.mintCryptoBoy(
84 | "myCBNFT",
85 | "https://gateway.pinata.cloud/ipfs/QmYFmJgQGH4uPHRYN15Xdv4aLd9o4Aq63y1e4GgN6kj5aK/2",
86 | web3.utils.toWei("1", "Ether"),
87 | colorsArray1,
88 | { from: accounts[0] }
89 | );
90 |
91 | cryptoBoyCount = await cryptoBoys.cryptoBoyCounter();
92 | assert.equal(cryptoBoyCount.toNumber(), 1);
93 |
94 | tokenExists = await cryptoBoys.getTokenExists(1, { from: accounts[0] });
95 | assert.equal(tokenExists, true);
96 |
97 | tokenURIExists = await cryptoBoys.tokenURIExists(
98 | "https://gateway.pinata.cloud/ipfs/QmYFmJgQGH4uPHRYN15Xdv4aLd9o4Aq63y1e4GgN6kj5aK/2",
99 | { from: accounts[0] }
100 | );
101 | assert.equal(tokenURIExists, true);
102 |
103 | tokenNameExists = await cryptoBoys.tokenNameExists("myCBNFT", {
104 | from: accounts[0],
105 | });
106 | assert.equal(tokenNameExists, true);
107 |
108 | for (let i = 0; i < colorsArray1.length; i++) {
109 | colorExists = await cryptoBoys.colorExists(colorsArray1[i], {
110 | from: accounts[0],
111 | });
112 | assert.equal(colorExists, true);
113 | }
114 |
115 | let cryptoboy;
116 | cryptoboy = await cryptoBoys.allCryptoBoys(1, {
117 | from: accounts[0],
118 | });
119 | assert.equal(cryptoboy.tokenId.toNumber(), 1);
120 | assert.equal(cryptoboy.tokenName, "myCBNFT");
121 | assert.equal(
122 | cryptoboy.tokenURI,
123 | "https://gateway.pinata.cloud/ipfs/QmYFmJgQGH4uPHRYN15Xdv4aLd9o4Aq63y1e4GgN6kj5aK/2"
124 | );
125 | assert.equal(cryptoboy.mintedBy, accounts[0]);
126 | assert.equal(cryptoboy.currentOwner, accounts[0]);
127 | assert.equal(
128 | cryptoboy.previousOwner,
129 | 0x0000000000000000000000000000000000000000
130 | );
131 | assert.equal(web3.utils.fromWei(cryptoboy.price, "ether"), 1);
132 | assert.equal(cryptoboy.numberOfTransfers.toNumber(), 0);
133 | assert.equal(cryptoboy.forSale, true);
134 |
135 | const colorsArray2 = [
136 | "#212b2e",
137 | "#515a66",
138 | "#a1c2a8",
139 | "#a1eb98",
140 | "#81ff65",
141 | "#915d81",
142 | "#e18258",
143 | "#f1f740",
144 | "#d1dc6a",
145 | "#6189a1",
146 | "#f1938c",
147 | "#e1b89c",
148 | "#e1d2ac",
149 | "#91afb7",
150 | "#4181a4",
151 | ];
152 |
153 | await cryptoBoys.mintCryptoBoy(
154 | "myCBNFT2",
155 | "https://gateway.pinata.cloud/ipfs/QmYFmJgQGH4uPQRYN15Xdv4aLd9o4Aq63y1e4GgN6kj5aK/2",
156 | web3.utils.toWei("1", "Ether"),
157 | colorsArray2,
158 | { from: accounts[1] }
159 | );
160 |
161 | const colorsArray3 = [
162 | "#232b2e",
163 | "#535a66",
164 | "#a3c2a8",
165 | "#a3eb98",
166 | "#83ff65",
167 | "#935d81",
168 | "#e38258",
169 | "#f3f740",
170 | "#d3dc6a",
171 | "#6389a1",
172 | "#f3938c",
173 | "#e3b89c",
174 | "#e3d2ac",
175 | "#93afb7",
176 | "#4381a4",
177 | ];
178 |
179 | // same token uri -reject
180 | await cryptoBoys.mintCryptoBoy(
181 | "myCBNFT3",
182 | "https://gateway.pinata.cloud/ipfs/QmYFmJgQGH4uPQRYN15Xdv4aLd9o4Aq63y1e4GgN6kj5aK/2",
183 | web3.utils.toWei("1", "Ether"),
184 | colorsArray3,
185 | { from: accounts[3] }
186 | ).should.be.rejected;
187 |
188 | const colorsArray4 = [
189 | "#252b2e",
190 | "#555a66",
191 | "#a5c2a8",
192 | "#a5eb98",
193 | "#85ff65",
194 | "#955d81",
195 | "#e58258",
196 | "#f5f740",
197 | "#d5dc6a",
198 | "#6589a1",
199 | "#f5938c",
200 | "#e5b89c",
201 | "#e5d2ac",
202 | "#95afb7",
203 | "#4581a4",
204 | ];
205 |
206 | // 0x0 adress sending txn - reject
207 | await cryptoBoys.mintCryptoBoy(
208 | "myCBNFT4",
209 | "https://gateway.pinata.cloud/ipfs/QmYFmJgQGH4uPQRYN14Xdv4aLd9o4Aq63y1e4GgN6kj5aK/2",
210 | web3.utils.toWei("1", "Ether"),
211 | colorsArray4,
212 | { from: 0x0000000000000000000000000000000000000000 }
213 | ).should.be.rejected;
214 |
215 | const colorsArray5 = [
216 | "#2d2b2e",
217 | "#5d5a66",
218 | "#adc2a8",
219 | "#adeb98",
220 | "#8dff65",
221 | "#9d5d81",
222 | "#ed8258",
223 | "#fdf740",
224 | "#dddc6a",
225 | "#6d89a1",
226 | "#fd938c",
227 | "#edb89c",
228 | "#edd2ac",
229 | "#9dafb7",
230 | "#4d81a4",
231 | ];
232 |
233 | await cryptoBoys.mintCryptoBoy(
234 | "myCBNFT5",
235 | "https://gateway.pinata.cloud/ipfs/QmYFmJgQGH4uPRRYN15Xdv4aLd9o4Aq63y1e4GgN6kj5aK/2",
236 | web3.utils.toWei("1", "Ether"),
237 | colorsArray5,
238 | { from: accounts[0] }
239 | );
240 |
241 | const colorsArray6 = [
242 | "#2f2b2e",
243 | "#5f5a66",
244 | "#afc2a8",
245 | "#afeb98",
246 | "#8fff65",
247 | "#9f5d81",
248 | "#ef8258",
249 | "#fff740",
250 | "#dfdc6a",
251 | "#6f89a1",
252 | "#ff938c",
253 | "#efb89c",
254 | "#efd2ac",
255 | "#9fafb7",
256 | "#4f81a4",
257 | ];
258 |
259 | await cryptoBoys.mintCryptoBoy(
260 | "myCBNFT6",
261 | "https://gateway.pinata.cloud/ipfs/QmYFmJgQGH4uPSRYN15Xdv4aLd9o4Aq63y1e4GgN6kj5aK/2",
262 | web3.utils.toWei("1", "Ether"),
263 | colorsArray6,
264 | { from: accounts[0] }
265 | );
266 |
267 | const colorsArray7 = [
268 | "#2a2b22",
269 | "#5a5a62",
270 | "#a4c2a2",
271 | "#aceb92",
272 | "#87ff62",
273 | "#995d82",
274 | "#eb8252",
275 | "#f6f742",
276 | "#d8dc62",
277 | "#6689a2",
278 | "#fe9382",
279 | "#e6b892",
280 | "#ead2a2",
281 | "#9cafb2",
282 | "#4281a2",
283 | ];
284 |
285 | // same token name - reject
286 | await cryptoBoys.mintCryptoBoy(
287 | "myCBNFT6",
288 | "https://gateway.pinata.cloud/ipfs/QmYFmJgQGH4uPSRYN15Xdv4aLd3o4Aq63y1e4GgN6kj5aK/2",
289 | web3.utils.toWei("1", "Ether"),
290 | colorsArray7,
291 | { from: accounts[0] }
292 | ).should.be.rejected;
293 |
294 | const colorsArray8 = [
295 | "#2a242e",
296 | "#5a5466",
297 | "#a4c4a8",
298 | "#ace498",
299 | "#87f465",
300 | "#995481",
301 | "#eb8458",
302 | "#f6f440",
303 | "#d8d46a",
304 | "#6684a1",
305 | "#fe948c",
306 | "#e6b49c",
307 | "#f6f740",
308 | "#9ca4b7",
309 | "#4284a4",
310 | ];
311 |
312 | // same color/colors - reject (13th value of array8 is same as 8th value of array1)
313 | await cryptoBoys.mintCryptoBoy(
314 | "myCBNFT8",
315 | "https://gateway.pinata.cloud/ipfs/QmYFmJgQGH4uPSRYN15Xdv4aLd3o4Bq46y1f4GgN6kj5aK/2",
316 | web3.utils.toWei("1", "Ether"),
317 | colorsArray8,
318 | { from: accounts[0] }
319 | ).should.be.rejected;
320 | });
321 |
322 | it("returns address of the token's owner", async () => {
323 | const tokenOwner = await cryptoBoys.getTokenOwner(2);
324 | assert.equal(tokenOwner, accounts[1]);
325 | });
326 |
327 | // returns tokenURI of the token
328 | it("returns metadata of a token", async () => {
329 | const tokenMetaData = await cryptoBoys.getTokenMetaData(2);
330 | assert.equal(
331 | tokenMetaData,
332 | "https://gateway.pinata.cloud/ipfs/QmYFmJgQGH4uPQRYN15Xdv4aLd9o4Aq63y1e4GgN6kj5aK/2"
333 | );
334 | });
335 |
336 | it("returns total number of tokens minted so far", async () => {
337 | const totalNumberOfTokensMinted = await cryptoBoys.getNumberOfTokensMinted();
338 | assert.equal(totalNumberOfTokensMinted.toNumber(), 4);
339 | });
340 |
341 | it("returns total number of tokens owned by an address", async () => {
342 | const totalNumberOfTokensOwnedByAnAddress = await cryptoBoys.getTotalNumberOfTokensOwnedByAnAddress(
343 | accounts[0]
344 | );
345 | assert.equal(totalNumberOfTokensOwnedByAnAddress.toNumber(), 3);
346 | });
347 |
348 | it("allows users to buy token for specified ethers", async () => {
349 | const oldTokenOwner = await cryptoBoys.getTokenOwner(1);
350 | assert.equal(oldTokenOwner, accounts[0]);
351 |
352 | let oldTokenOwnerBalance;
353 | oldTokenOwnerBalance = await web3.eth.getBalance(accounts[0]);
354 | oldTokenOwnerBalance = new web3.utils.BN(oldTokenOwnerBalance);
355 |
356 | let oldTotalNumberOfTokensOwnedBySeller;
357 | oldTotalNumberOfTokensOwnedBySeller = await cryptoBoys.getTotalNumberOfTokensOwnedByAnAddress(
358 | accounts[0]
359 | );
360 | assert.equal(oldTotalNumberOfTokensOwnedBySeller.toNumber(), 3);
361 |
362 | let cryptoBoy;
363 | cryptoBoy = await cryptoBoys.allCryptoBoys(1, {
364 | from: accounts[0],
365 | });
366 | assert.equal(cryptoBoy.numberOfTransfers.toNumber(), 0);
367 |
368 | result = await cryptoBoys.buyToken(1, {
369 | from: accounts[2],
370 | value: web3.utils.toWei("1", "Ether"),
371 | });
372 |
373 | const newTokenOwner = await cryptoBoys.getTokenOwner(1);
374 | assert.equal(newTokenOwner, accounts[2]);
375 |
376 | let newTokenOwnerBalance;
377 | newTokenOwnerBalance = await web3.eth.getBalance(accounts[0]);
378 | newTokenOwnerBalance = new web3.utils.BN(newTokenOwnerBalance);
379 |
380 | let newTotalNumberOfTokensOwnedBySeller;
381 | newTotalNumberOfTokensOwnedBySeller = await cryptoBoys.getTotalNumberOfTokensOwnedByAnAddress(
382 | accounts[0]
383 | );
384 | assert.equal(newTotalNumberOfTokensOwnedBySeller.toNumber(), 2);
385 |
386 | cryptoBoy = await cryptoBoys.allCryptoBoys(1, {
387 | from: accounts[0],
388 | });
389 | assert.equal(cryptoBoy.numberOfTransfers.toNumber(), 1);
390 |
391 | let price;
392 | price = web3.utils.toWei("1", "Ether");
393 | price = new web3.utils.BN(price);
394 |
395 | const exepectedBalance = oldTokenOwnerBalance.add(price);
396 | assert.equal(
397 | newTokenOwnerBalance.toString(),
398 | exepectedBalance.toString()
399 | );
400 |
401 | cryptoBoy = await cryptoBoys.allCryptoBoys(1, {
402 | from: accounts[0],
403 | });
404 | assert.equal(cryptoBoy.currentOwner, accounts[2]);
405 |
406 | await cryptoBoys.buyToken(2, {
407 | from: 0x0000000000000000000000000000000000000000,
408 | value: web3.utils.toWei("1", "Ether"),
409 | }).should.be.rejected;
410 |
411 | await cryptoBoys.buyToken(56, {
412 | from: accounts[4],
413 | value: web3.utils.toWei("1", "Ether"),
414 | }).should.be.rejected;
415 |
416 | await cryptoBoys.buyToken(3, {
417 | from: accounts[0],
418 | value: web3.utils.toWei("1", "Ether"),
419 | }).should.be.rejected;
420 | });
421 |
422 | it("allows users to change token price", async () => {
423 | let cryptoBoyPrice;
424 | cryptoBoyPrice = await cryptoBoys.allCryptoBoys(1, {
425 | from: accounts[0],
426 | });
427 | assert.equal(web3.utils.fromWei(cryptoBoyPrice.price, "ether"), 1);
428 |
429 | result = await cryptoBoys.changeTokenPrice(
430 | 1,
431 | web3.utils.toWei("2", "Ether"),
432 | {
433 | from: accounts[2],
434 | }
435 | );
436 |
437 | cryptoBoyPrice = await cryptoBoys.allCryptoBoys(1, {
438 | from: accounts[0],
439 | });
440 | assert.equal(web3.utils.fromWei(cryptoBoyPrice.price, "ether"), 2);
441 |
442 | await cryptoBoys.changeTokenPrice(1, web3.utils.toWei("3", "Ether"), {
443 | from: 0x0000000000000000000000000000000000000000,
444 | }).should.be.rejected;
445 |
446 | await cryptoBoys.changeTokenPrice(82, web3.utils.toWei("3", "Ether"), {
447 | from: accounts[2],
448 | }).should.be.rejected;
449 |
450 | await cryptoBoys.changeTokenPrice(1, web3.utils.toWei("3", "Ether"), {
451 | from: accounts[6],
452 | }).should.be.rejected;
453 | });
454 |
455 | it("allows users to toggle between setting the token for sale or not for sale", async () => {
456 | let cryptoboy;
457 | cryptoboy = await cryptoBoys.allCryptoBoys(1, {
458 | from: accounts[0],
459 | });
460 | assert.equal(cryptoboy.forSale, true);
461 |
462 | result = await cryptoBoys.toggleForSale(1, { from: accounts[2] });
463 |
464 | cryptoboy = await cryptoBoys.allCryptoBoys(1, {
465 | from: accounts[0],
466 | });
467 | assert.equal(cryptoboy.forSale, false);
468 |
469 | result = await cryptoBoys.toggleForSale(1, { from: accounts[2] });
470 |
471 | cryptoboy = await cryptoBoys.allCryptoBoys(1, {
472 | from: accounts[0],
473 | });
474 | assert.equal(cryptoboy.forSale, true);
475 |
476 | await cryptoBoys.toggleForSale(1, {
477 | from: 0x0000000000000000000000000000000000000000,
478 | }).should.be.rejected;
479 |
480 | await cryptoBoys.toggleForSale(94, { from: accounts[2] }).should.be
481 | .rejected;
482 |
483 | await cryptoBoys.toggleForSale(1, { from: accounts[8] }).should.be
484 | .rejected;
485 | });
486 | });
487 | });
488 |
--------------------------------------------------------------------------------
/src/components/FormAndPreview/FormAndPreview.jsx:
--------------------------------------------------------------------------------
1 | import React, { Component } from "react";
2 | import CryptoBoyNFTImage from "../CryptoBoyNFTImage/CryptoBoyNFTImage";
3 |
4 | class FormAndPreview extends Component {
5 | constructor(props) {
6 | super(props);
7 | this.state = {
8 | userSelectedColors: [
9 | {
10 | cardBorderColor: "",
11 | cardBackgroundColor: "",
12 | headBorderColor: "",
13 | headBackgroundColor: "",
14 | leftEyeBorderColor: "",
15 | rightEyeBorderColor: "",
16 | leftEyeBackgroundColor: "",
17 | rightEyeBackgroundColor: "",
18 | leftPupilBackgroundColor: "",
19 | rightPupilBackgroundColor: "",
20 | mouthColor: "",
21 | neckBackgroundColor: "",
22 | neckBorderColor: "",
23 | bodyBackgroundColor: "",
24 | bodyBorderColor: "",
25 | },
26 | ],
27 | cryptoBoyName: "",
28 | cryptoBoyPrice: "",
29 | };
30 | }
31 |
32 | componentDidMount = async () => {
33 | await this.props.setMintBtnTimer();
34 | };
35 |
36 | callMintMyNFTFromApp = (e) => {
37 | e.preventDefault();
38 | this.props.mintMyNFT(
39 | this.state.userSelectedColors[0],
40 | this.state.cryptoBoyName,
41 | this.state.cryptoBoyPrice
42 | );
43 | };
44 |
45 | render() {
46 | return (
47 |
48 |
49 |
50 |
Color Your Crypto Boy As You Want It To be!
51 |
52 |
53 |
501 |
502 | );
503 | }
504 | }
505 |
506 | export default FormAndPreview;
507 |
--------------------------------------------------------------------------------
/src/abis/Migrations.json:
--------------------------------------------------------------------------------
1 | {
2 | "contractName": "Migrations",
3 | "abi": [
4 | {
5 | "inputs": [],
6 | "stateMutability": "nonpayable",
7 | "type": "constructor"
8 | },
9 | {
10 | "inputs": [],
11 | "name": "last_completed_migration",
12 | "outputs": [
13 | {
14 | "internalType": "uint256",
15 | "name": "",
16 | "type": "uint256"
17 | }
18 | ],
19 | "stateMutability": "view",
20 | "type": "function",
21 | "constant": true
22 | },
23 | {
24 | "inputs": [],
25 | "name": "owner",
26 | "outputs": [
27 | {
28 | "internalType": "address",
29 | "name": "",
30 | "type": "address"
31 | }
32 | ],
33 | "stateMutability": "view",
34 | "type": "function",
35 | "constant": true
36 | },
37 | {
38 | "inputs": [
39 | {
40 | "internalType": "uint256",
41 | "name": "completed",
42 | "type": "uint256"
43 | }
44 | ],
45 | "name": "setCompleted",
46 | "outputs": [],
47 | "stateMutability": "nonpayable",
48 | "type": "function"
49 | },
50 | {
51 | "inputs": [
52 | {
53 | "internalType": "address",
54 | "name": "new_address",
55 | "type": "address"
56 | }
57 | ],
58 | "name": "upgrade",
59 | "outputs": [],
60 | "stateMutability": "nonpayable",
61 | "type": "function"
62 | }
63 | ],
64 | "metadata": "{\"compiler\":{\"version\":\"0.7.6+commit.7338295f\"},\"language\":\"Solidity\",\"output\":{\"abi\":[{\"inputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"constructor\"},{\"inputs\":[],\"name\":\"last_completed_migration\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"owner\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"completed\",\"type\":\"uint256\"}],\"name\":\"setCompleted\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"new_address\",\"type\":\"address\"}],\"name\":\"upgrade\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"}],\"devdoc\":{\"kind\":\"dev\",\"methods\":{},\"version\":1},\"userdoc\":{\"kind\":\"user\",\"methods\":{},\"version\":1}},\"settings\":{\"compilationTarget\":{\"/home/pavansoratur/Github/cryptoboys-NFT-marketplace/src/contracts/Migrations.sol\":\"Migrations\"},\"evmVersion\":\"istanbul\",\"libraries\":{},\"metadata\":{\"bytecodeHash\":\"ipfs\"},\"optimizer\":{\"enabled\":false,\"runs\":200},\"remappings\":[]},\"sources\":{\"/home/pavansoratur/Github/cryptoboys-NFT-marketplace/src/contracts/Migrations.sol\":{\"keccak256\":\"0x12e875f75583a6d74b23acfbb440c0cb8c4161eb4b6e401b5a047a8241cc5365\",\"license\":\"MIT\",\"urls\":[\"bzz-raw://549b5653fb3ba12444dec07d5728b35f2b229faa8f8c524c403492dcc6847da6\",\"dweb:/ipfs/QmYqExk3AbYPmBAaMS6wFhV9jgLdWWQydhy9v3Sfcv5ynN\"]}},\"version\":1}",
65 | "bytecode": "0x608060405234801561001057600080fd5b50336000806101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555061029f806100606000396000f3fe608060405234801561001057600080fd5b506004361061004c5760003560e01c80630900f01014610051578063445df0ac146100955780638da5cb5b146100b3578063fdacd576146100e7575b600080fd5b6100936004803603602081101561006757600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190505050610115565b005b61009d6101e0565b6040518082815260200191505060405180910390f35b6100bb6101e6565b604051808273ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b610113600480360360208110156100fd57600080fd5b810190808035906020019092919050505061020a565b005b60008054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614156101dd5760008190508073ffffffffffffffffffffffffffffffffffffffff1663fdacd5766001546040518263ffffffff1660e01b815260040180828152602001915050600060405180830381600087803b1580156101c357600080fd5b505af11580156101d7573d6000803e3d6000fd5b50505050505b50565b60015481565b60008054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b60008054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff16141561026657806001819055505b5056fea26469706673582212206e5d0441122dbe0dff89068ada8bd4dc77aff4a709b45219e239268a9bfa623b64736f6c63430007060033",
66 | "deployedBytecode": "0x608060405234801561001057600080fd5b506004361061004c5760003560e01c80630900f01014610051578063445df0ac146100955780638da5cb5b146100b3578063fdacd576146100e7575b600080fd5b6100936004803603602081101561006757600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190505050610115565b005b61009d6101e0565b6040518082815260200191505060405180910390f35b6100bb6101e6565b604051808273ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b610113600480360360208110156100fd57600080fd5b810190808035906020019092919050505061020a565b005b60008054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614156101dd5760008190508073ffffffffffffffffffffffffffffffffffffffff1663fdacd5766001546040518263ffffffff1660e01b815260040180828152602001915050600060405180830381600087803b1580156101c357600080fd5b505af11580156101d7573d6000803e3d6000fd5b50505050505b50565b60015481565b60008054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b60008054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff16141561026657806001819055505b5056fea26469706673582212206e5d0441122dbe0dff89068ada8bd4dc77aff4a709b45219e239268a9bfa623b64736f6c63430007060033",
67 | "immutableReferences": {},
68 | "generatedSources": [],
69 | "deployedGeneratedSources": [],
70 | "sourceMap": "66:473:2:-:0;;;155:43;;;;;;;;;;183:10;175:5;;:18;;;;;;;;;;;;;;;;;;66:473;;;;;;",
71 | "deployedSourceMap": "66:473:2:-:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;372:165;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;;114:36;;;:::i;:::-;;;;;;;;;;;;;;;;;;;90:20;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;265:103;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;;372:165;248:5;;;;;;;;;;234:19;;:10;:19;;;230:26;;;434:19:::1;467:11;434:45;;485:8;:21;;;507:24;;485:47;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;::::0;::::1;;;;;;;;;;;;::::0;::::1;;;;;;;;;255:1;230:26:::0;372:165;:::o;114:36::-;;;;:::o;90:20::-;;;;;;;;;;;;:::o;265:103::-;248:5;;;;;;;;;;234:19;;:10;:19;;;230:26;;;354:9:::1;327:24;:36;;;;230:26:::0;265:103;:::o",
72 | "source": "// SPDX-License-Identifier: MIT\npragma solidity >=0.4.21 <0.8.0;\n\ncontract Migrations {\n address public owner;\n uint public last_completed_migration;\n\n constructor() {\n owner = msg.sender;\n }\n\n modifier restricted() {\n if (msg.sender == owner) _;\n }\n\n function setCompleted(uint completed) public restricted {\n last_completed_migration = completed;\n }\n\n function upgrade(address new_address) public restricted {\n Migrations upgraded = Migrations(new_address);\n upgraded.setCompleted(last_completed_migration);\n }\n}\n",
73 | "sourcePath": "/home/pavansoratur/Github/cryptoboys-NFT-marketplace/src/contracts/Migrations.sol",
74 | "ast": {
75 | "absolutePath": "/home/pavansoratur/Github/cryptoboys-NFT-marketplace/src/contracts/Migrations.sol",
76 | "exportedSymbols": {
77 | "Migrations": [
78 | 3555
79 | ]
80 | },
81 | "id": 3556,
82 | "license": "MIT",
83 | "nodeType": "SourceUnit",
84 | "nodes": [
85 | {
86 | "id": 3500,
87 | "literals": [
88 | "solidity",
89 | ">=",
90 | "0.4",
91 | ".21",
92 | "<",
93 | "0.8",
94 | ".0"
95 | ],
96 | "nodeType": "PragmaDirective",
97 | "src": "32:32:2"
98 | },
99 | {
100 | "abstract": false,
101 | "baseContracts": [],
102 | "contractDependencies": [],
103 | "contractKind": "contract",
104 | "fullyImplemented": true,
105 | "id": 3555,
106 | "linearizedBaseContracts": [
107 | 3555
108 | ],
109 | "name": "Migrations",
110 | "nodeType": "ContractDefinition",
111 | "nodes": [
112 | {
113 | "constant": false,
114 | "functionSelector": "8da5cb5b",
115 | "id": 3502,
116 | "mutability": "mutable",
117 | "name": "owner",
118 | "nodeType": "VariableDeclaration",
119 | "scope": 3555,
120 | "src": "90:20:2",
121 | "stateVariable": true,
122 | "storageLocation": "default",
123 | "typeDescriptions": {
124 | "typeIdentifier": "t_address",
125 | "typeString": "address"
126 | },
127 | "typeName": {
128 | "id": 3501,
129 | "name": "address",
130 | "nodeType": "ElementaryTypeName",
131 | "src": "90:7:2",
132 | "stateMutability": "nonpayable",
133 | "typeDescriptions": {
134 | "typeIdentifier": "t_address",
135 | "typeString": "address"
136 | }
137 | },
138 | "visibility": "public"
139 | },
140 | {
141 | "constant": false,
142 | "functionSelector": "445df0ac",
143 | "id": 3504,
144 | "mutability": "mutable",
145 | "name": "last_completed_migration",
146 | "nodeType": "VariableDeclaration",
147 | "scope": 3555,
148 | "src": "114:36:2",
149 | "stateVariable": true,
150 | "storageLocation": "default",
151 | "typeDescriptions": {
152 | "typeIdentifier": "t_uint256",
153 | "typeString": "uint256"
154 | },
155 | "typeName": {
156 | "id": 3503,
157 | "name": "uint",
158 | "nodeType": "ElementaryTypeName",
159 | "src": "114:4:2",
160 | "typeDescriptions": {
161 | "typeIdentifier": "t_uint256",
162 | "typeString": "uint256"
163 | }
164 | },
165 | "visibility": "public"
166 | },
167 | {
168 | "body": {
169 | "id": 3512,
170 | "nodeType": "Block",
171 | "src": "169:29:2",
172 | "statements": [
173 | {
174 | "expression": {
175 | "id": 3510,
176 | "isConstant": false,
177 | "isLValue": false,
178 | "isPure": false,
179 | "lValueRequested": false,
180 | "leftHandSide": {
181 | "id": 3507,
182 | "name": "owner",
183 | "nodeType": "Identifier",
184 | "overloadedDeclarations": [],
185 | "referencedDeclaration": 3502,
186 | "src": "175:5:2",
187 | "typeDescriptions": {
188 | "typeIdentifier": "t_address",
189 | "typeString": "address"
190 | }
191 | },
192 | "nodeType": "Assignment",
193 | "operator": "=",
194 | "rightHandSide": {
195 | "expression": {
196 | "id": 3508,
197 | "name": "msg",
198 | "nodeType": "Identifier",
199 | "overloadedDeclarations": [],
200 | "referencedDeclaration": 4294967281,
201 | "src": "183:3:2",
202 | "typeDescriptions": {
203 | "typeIdentifier": "t_magic_message",
204 | "typeString": "msg"
205 | }
206 | },
207 | "id": 3509,
208 | "isConstant": false,
209 | "isLValue": false,
210 | "isPure": false,
211 | "lValueRequested": false,
212 | "memberName": "sender",
213 | "nodeType": "MemberAccess",
214 | "src": "183:10:2",
215 | "typeDescriptions": {
216 | "typeIdentifier": "t_address_payable",
217 | "typeString": "address payable"
218 | }
219 | },
220 | "src": "175:18:2",
221 | "typeDescriptions": {
222 | "typeIdentifier": "t_address",
223 | "typeString": "address"
224 | }
225 | },
226 | "id": 3511,
227 | "nodeType": "ExpressionStatement",
228 | "src": "175:18:2"
229 | }
230 | ]
231 | },
232 | "id": 3513,
233 | "implemented": true,
234 | "kind": "constructor",
235 | "modifiers": [],
236 | "name": "",
237 | "nodeType": "FunctionDefinition",
238 | "parameters": {
239 | "id": 3505,
240 | "nodeType": "ParameterList",
241 | "parameters": [],
242 | "src": "166:2:2"
243 | },
244 | "returnParameters": {
245 | "id": 3506,
246 | "nodeType": "ParameterList",
247 | "parameters": [],
248 | "src": "169:0:2"
249 | },
250 | "scope": 3555,
251 | "src": "155:43:2",
252 | "stateMutability": "nonpayable",
253 | "virtual": false,
254 | "visibility": "public"
255 | },
256 | {
257 | "body": {
258 | "id": 3521,
259 | "nodeType": "Block",
260 | "src": "224:37:2",
261 | "statements": [
262 | {
263 | "condition": {
264 | "commonType": {
265 | "typeIdentifier": "t_address",
266 | "typeString": "address"
267 | },
268 | "id": 3518,
269 | "isConstant": false,
270 | "isLValue": false,
271 | "isPure": false,
272 | "lValueRequested": false,
273 | "leftExpression": {
274 | "expression": {
275 | "id": 3515,
276 | "name": "msg",
277 | "nodeType": "Identifier",
278 | "overloadedDeclarations": [],
279 | "referencedDeclaration": 4294967281,
280 | "src": "234:3:2",
281 | "typeDescriptions": {
282 | "typeIdentifier": "t_magic_message",
283 | "typeString": "msg"
284 | }
285 | },
286 | "id": 3516,
287 | "isConstant": false,
288 | "isLValue": false,
289 | "isPure": false,
290 | "lValueRequested": false,
291 | "memberName": "sender",
292 | "nodeType": "MemberAccess",
293 | "src": "234:10:2",
294 | "typeDescriptions": {
295 | "typeIdentifier": "t_address_payable",
296 | "typeString": "address payable"
297 | }
298 | },
299 | "nodeType": "BinaryOperation",
300 | "operator": "==",
301 | "rightExpression": {
302 | "id": 3517,
303 | "name": "owner",
304 | "nodeType": "Identifier",
305 | "overloadedDeclarations": [],
306 | "referencedDeclaration": 3502,
307 | "src": "248:5:2",
308 | "typeDescriptions": {
309 | "typeIdentifier": "t_address",
310 | "typeString": "address"
311 | }
312 | },
313 | "src": "234:19:2",
314 | "typeDescriptions": {
315 | "typeIdentifier": "t_bool",
316 | "typeString": "bool"
317 | }
318 | },
319 | "id": 3520,
320 | "nodeType": "IfStatement",
321 | "src": "230:26:2",
322 | "trueBody": {
323 | "id": 3519,
324 | "nodeType": "PlaceholderStatement",
325 | "src": "255:1:2"
326 | }
327 | }
328 | ]
329 | },
330 | "id": 3522,
331 | "name": "restricted",
332 | "nodeType": "ModifierDefinition",
333 | "parameters": {
334 | "id": 3514,
335 | "nodeType": "ParameterList",
336 | "parameters": [],
337 | "src": "221:2:2"
338 | },
339 | "src": "202:59:2",
340 | "virtual": false,
341 | "visibility": "internal"
342 | },
343 | {
344 | "body": {
345 | "id": 3533,
346 | "nodeType": "Block",
347 | "src": "321:47:2",
348 | "statements": [
349 | {
350 | "expression": {
351 | "id": 3531,
352 | "isConstant": false,
353 | "isLValue": false,
354 | "isPure": false,
355 | "lValueRequested": false,
356 | "leftHandSide": {
357 | "id": 3529,
358 | "name": "last_completed_migration",
359 | "nodeType": "Identifier",
360 | "overloadedDeclarations": [],
361 | "referencedDeclaration": 3504,
362 | "src": "327:24:2",
363 | "typeDescriptions": {
364 | "typeIdentifier": "t_uint256",
365 | "typeString": "uint256"
366 | }
367 | },
368 | "nodeType": "Assignment",
369 | "operator": "=",
370 | "rightHandSide": {
371 | "id": 3530,
372 | "name": "completed",
373 | "nodeType": "Identifier",
374 | "overloadedDeclarations": [],
375 | "referencedDeclaration": 3524,
376 | "src": "354:9:2",
377 | "typeDescriptions": {
378 | "typeIdentifier": "t_uint256",
379 | "typeString": "uint256"
380 | }
381 | },
382 | "src": "327:36:2",
383 | "typeDescriptions": {
384 | "typeIdentifier": "t_uint256",
385 | "typeString": "uint256"
386 | }
387 | },
388 | "id": 3532,
389 | "nodeType": "ExpressionStatement",
390 | "src": "327:36:2"
391 | }
392 | ]
393 | },
394 | "functionSelector": "fdacd576",
395 | "id": 3534,
396 | "implemented": true,
397 | "kind": "function",
398 | "modifiers": [
399 | {
400 | "id": 3527,
401 | "modifierName": {
402 | "id": 3526,
403 | "name": "restricted",
404 | "nodeType": "Identifier",
405 | "overloadedDeclarations": [],
406 | "referencedDeclaration": 3522,
407 | "src": "310:10:2",
408 | "typeDescriptions": {
409 | "typeIdentifier": "t_modifier$__$",
410 | "typeString": "modifier ()"
411 | }
412 | },
413 | "nodeType": "ModifierInvocation",
414 | "src": "310:10:2"
415 | }
416 | ],
417 | "name": "setCompleted",
418 | "nodeType": "FunctionDefinition",
419 | "parameters": {
420 | "id": 3525,
421 | "nodeType": "ParameterList",
422 | "parameters": [
423 | {
424 | "constant": false,
425 | "id": 3524,
426 | "mutability": "mutable",
427 | "name": "completed",
428 | "nodeType": "VariableDeclaration",
429 | "scope": 3534,
430 | "src": "287:14:2",
431 | "stateVariable": false,
432 | "storageLocation": "default",
433 | "typeDescriptions": {
434 | "typeIdentifier": "t_uint256",
435 | "typeString": "uint256"
436 | },
437 | "typeName": {
438 | "id": 3523,
439 | "name": "uint",
440 | "nodeType": "ElementaryTypeName",
441 | "src": "287:4:2",
442 | "typeDescriptions": {
443 | "typeIdentifier": "t_uint256",
444 | "typeString": "uint256"
445 | }
446 | },
447 | "visibility": "internal"
448 | }
449 | ],
450 | "src": "286:16:2"
451 | },
452 | "returnParameters": {
453 | "id": 3528,
454 | "nodeType": "ParameterList",
455 | "parameters": [],
456 | "src": "321:0:2"
457 | },
458 | "scope": 3555,
459 | "src": "265:103:2",
460 | "stateMutability": "nonpayable",
461 | "virtual": false,
462 | "visibility": "public"
463 | },
464 | {
465 | "body": {
466 | "id": 3553,
467 | "nodeType": "Block",
468 | "src": "428:109:2",
469 | "statements": [
470 | {
471 | "assignments": [
472 | 3542
473 | ],
474 | "declarations": [
475 | {
476 | "constant": false,
477 | "id": 3542,
478 | "mutability": "mutable",
479 | "name": "upgraded",
480 | "nodeType": "VariableDeclaration",
481 | "scope": 3553,
482 | "src": "434:19:2",
483 | "stateVariable": false,
484 | "storageLocation": "default",
485 | "typeDescriptions": {
486 | "typeIdentifier": "t_contract$_Migrations_$3555",
487 | "typeString": "contract Migrations"
488 | },
489 | "typeName": {
490 | "id": 3541,
491 | "name": "Migrations",
492 | "nodeType": "UserDefinedTypeName",
493 | "referencedDeclaration": 3555,
494 | "src": "434:10:2",
495 | "typeDescriptions": {
496 | "typeIdentifier": "t_contract$_Migrations_$3555",
497 | "typeString": "contract Migrations"
498 | }
499 | },
500 | "visibility": "internal"
501 | }
502 | ],
503 | "id": 3546,
504 | "initialValue": {
505 | "arguments": [
506 | {
507 | "id": 3544,
508 | "name": "new_address",
509 | "nodeType": "Identifier",
510 | "overloadedDeclarations": [],
511 | "referencedDeclaration": 3536,
512 | "src": "467:11:2",
513 | "typeDescriptions": {
514 | "typeIdentifier": "t_address",
515 | "typeString": "address"
516 | }
517 | }
518 | ],
519 | "expression": {
520 | "argumentTypes": [
521 | {
522 | "typeIdentifier": "t_address",
523 | "typeString": "address"
524 | }
525 | ],
526 | "id": 3543,
527 | "name": "Migrations",
528 | "nodeType": "Identifier",
529 | "overloadedDeclarations": [],
530 | "referencedDeclaration": 3555,
531 | "src": "456:10:2",
532 | "typeDescriptions": {
533 | "typeIdentifier": "t_type$_t_contract$_Migrations_$3555_$",
534 | "typeString": "type(contract Migrations)"
535 | }
536 | },
537 | "id": 3545,
538 | "isConstant": false,
539 | "isLValue": false,
540 | "isPure": false,
541 | "kind": "typeConversion",
542 | "lValueRequested": false,
543 | "names": [],
544 | "nodeType": "FunctionCall",
545 | "src": "456:23:2",
546 | "tryCall": false,
547 | "typeDescriptions": {
548 | "typeIdentifier": "t_contract$_Migrations_$3555",
549 | "typeString": "contract Migrations"
550 | }
551 | },
552 | "nodeType": "VariableDeclarationStatement",
553 | "src": "434:45:2"
554 | },
555 | {
556 | "expression": {
557 | "arguments": [
558 | {
559 | "id": 3550,
560 | "name": "last_completed_migration",
561 | "nodeType": "Identifier",
562 | "overloadedDeclarations": [],
563 | "referencedDeclaration": 3504,
564 | "src": "507:24:2",
565 | "typeDescriptions": {
566 | "typeIdentifier": "t_uint256",
567 | "typeString": "uint256"
568 | }
569 | }
570 | ],
571 | "expression": {
572 | "argumentTypes": [
573 | {
574 | "typeIdentifier": "t_uint256",
575 | "typeString": "uint256"
576 | }
577 | ],
578 | "expression": {
579 | "id": 3547,
580 | "name": "upgraded",
581 | "nodeType": "Identifier",
582 | "overloadedDeclarations": [],
583 | "referencedDeclaration": 3542,
584 | "src": "485:8:2",
585 | "typeDescriptions": {
586 | "typeIdentifier": "t_contract$_Migrations_$3555",
587 | "typeString": "contract Migrations"
588 | }
589 | },
590 | "id": 3549,
591 | "isConstant": false,
592 | "isLValue": false,
593 | "isPure": false,
594 | "lValueRequested": false,
595 | "memberName": "setCompleted",
596 | "nodeType": "MemberAccess",
597 | "referencedDeclaration": 3534,
598 | "src": "485:21:2",
599 | "typeDescriptions": {
600 | "typeIdentifier": "t_function_external_nonpayable$_t_uint256_$returns$__$",
601 | "typeString": "function (uint256) external"
602 | }
603 | },
604 | "id": 3551,
605 | "isConstant": false,
606 | "isLValue": false,
607 | "isPure": false,
608 | "kind": "functionCall",
609 | "lValueRequested": false,
610 | "names": [],
611 | "nodeType": "FunctionCall",
612 | "src": "485:47:2",
613 | "tryCall": false,
614 | "typeDescriptions": {
615 | "typeIdentifier": "t_tuple$__$",
616 | "typeString": "tuple()"
617 | }
618 | },
619 | "id": 3552,
620 | "nodeType": "ExpressionStatement",
621 | "src": "485:47:2"
622 | }
623 | ]
624 | },
625 | "functionSelector": "0900f010",
626 | "id": 3554,
627 | "implemented": true,
628 | "kind": "function",
629 | "modifiers": [
630 | {
631 | "id": 3539,
632 | "modifierName": {
633 | "id": 3538,
634 | "name": "restricted",
635 | "nodeType": "Identifier",
636 | "overloadedDeclarations": [],
637 | "referencedDeclaration": 3522,
638 | "src": "417:10:2",
639 | "typeDescriptions": {
640 | "typeIdentifier": "t_modifier$__$",
641 | "typeString": "modifier ()"
642 | }
643 | },
644 | "nodeType": "ModifierInvocation",
645 | "src": "417:10:2"
646 | }
647 | ],
648 | "name": "upgrade",
649 | "nodeType": "FunctionDefinition",
650 | "parameters": {
651 | "id": 3537,
652 | "nodeType": "ParameterList",
653 | "parameters": [
654 | {
655 | "constant": false,
656 | "id": 3536,
657 | "mutability": "mutable",
658 | "name": "new_address",
659 | "nodeType": "VariableDeclaration",
660 | "scope": 3554,
661 | "src": "389:19:2",
662 | "stateVariable": false,
663 | "storageLocation": "default",
664 | "typeDescriptions": {
665 | "typeIdentifier": "t_address",
666 | "typeString": "address"
667 | },
668 | "typeName": {
669 | "id": 3535,
670 | "name": "address",
671 | "nodeType": "ElementaryTypeName",
672 | "src": "389:7:2",
673 | "stateMutability": "nonpayable",
674 | "typeDescriptions": {
675 | "typeIdentifier": "t_address",
676 | "typeString": "address"
677 | }
678 | },
679 | "visibility": "internal"
680 | }
681 | ],
682 | "src": "388:21:2"
683 | },
684 | "returnParameters": {
685 | "id": 3540,
686 | "nodeType": "ParameterList",
687 | "parameters": [],
688 | "src": "428:0:2"
689 | },
690 | "scope": 3555,
691 | "src": "372:165:2",
692 | "stateMutability": "nonpayable",
693 | "virtual": false,
694 | "visibility": "public"
695 | }
696 | ],
697 | "scope": 3556,
698 | "src": "66:473:2"
699 | }
700 | ],
701 | "src": "32:508:2"
702 | },
703 | "legacyAST": {
704 | "attributes": {
705 | "absolutePath": "/home/pavansoratur/Github/cryptoboys-NFT-marketplace/src/contracts/Migrations.sol",
706 | "exportedSymbols": {
707 | "Migrations": [
708 | 3555
709 | ]
710 | },
711 | "license": "MIT"
712 | },
713 | "children": [
714 | {
715 | "attributes": {
716 | "literals": [
717 | "solidity",
718 | ">=",
719 | "0.4",
720 | ".21",
721 | "<",
722 | "0.8",
723 | ".0"
724 | ]
725 | },
726 | "id": 3500,
727 | "name": "PragmaDirective",
728 | "src": "32:32:2"
729 | },
730 | {
731 | "attributes": {
732 | "abstract": false,
733 | "baseContracts": [
734 | null
735 | ],
736 | "contractDependencies": [
737 | null
738 | ],
739 | "contractKind": "contract",
740 | "fullyImplemented": true,
741 | "linearizedBaseContracts": [
742 | 3555
743 | ],
744 | "name": "Migrations",
745 | "scope": 3556
746 | },
747 | "children": [
748 | {
749 | "attributes": {
750 | "constant": false,
751 | "functionSelector": "8da5cb5b",
752 | "mutability": "mutable",
753 | "name": "owner",
754 | "scope": 3555,
755 | "stateVariable": true,
756 | "storageLocation": "default",
757 | "type": "address",
758 | "visibility": "public"
759 | },
760 | "children": [
761 | {
762 | "attributes": {
763 | "name": "address",
764 | "stateMutability": "nonpayable",
765 | "type": "address"
766 | },
767 | "id": 3501,
768 | "name": "ElementaryTypeName",
769 | "src": "90:7:2"
770 | }
771 | ],
772 | "id": 3502,
773 | "name": "VariableDeclaration",
774 | "src": "90:20:2"
775 | },
776 | {
777 | "attributes": {
778 | "constant": false,
779 | "functionSelector": "445df0ac",
780 | "mutability": "mutable",
781 | "name": "last_completed_migration",
782 | "scope": 3555,
783 | "stateVariable": true,
784 | "storageLocation": "default",
785 | "type": "uint256",
786 | "visibility": "public"
787 | },
788 | "children": [
789 | {
790 | "attributes": {
791 | "name": "uint",
792 | "type": "uint256"
793 | },
794 | "id": 3503,
795 | "name": "ElementaryTypeName",
796 | "src": "114:4:2"
797 | }
798 | ],
799 | "id": 3504,
800 | "name": "VariableDeclaration",
801 | "src": "114:36:2"
802 | },
803 | {
804 | "attributes": {
805 | "implemented": true,
806 | "isConstructor": true,
807 | "kind": "constructor",
808 | "modifiers": [
809 | null
810 | ],
811 | "name": "",
812 | "scope": 3555,
813 | "stateMutability": "nonpayable",
814 | "virtual": false,
815 | "visibility": "public"
816 | },
817 | "children": [
818 | {
819 | "attributes": {
820 | "parameters": [
821 | null
822 | ]
823 | },
824 | "children": [],
825 | "id": 3505,
826 | "name": "ParameterList",
827 | "src": "166:2:2"
828 | },
829 | {
830 | "attributes": {
831 | "parameters": [
832 | null
833 | ]
834 | },
835 | "children": [],
836 | "id": 3506,
837 | "name": "ParameterList",
838 | "src": "169:0:2"
839 | },
840 | {
841 | "children": [
842 | {
843 | "children": [
844 | {
845 | "attributes": {
846 | "isConstant": false,
847 | "isLValue": false,
848 | "isPure": false,
849 | "lValueRequested": false,
850 | "operator": "=",
851 | "type": "address"
852 | },
853 | "children": [
854 | {
855 | "attributes": {
856 | "overloadedDeclarations": [
857 | null
858 | ],
859 | "referencedDeclaration": 3502,
860 | "type": "address",
861 | "value": "owner"
862 | },
863 | "id": 3507,
864 | "name": "Identifier",
865 | "src": "175:5:2"
866 | },
867 | {
868 | "attributes": {
869 | "isConstant": false,
870 | "isLValue": false,
871 | "isPure": false,
872 | "lValueRequested": false,
873 | "member_name": "sender",
874 | "type": "address payable"
875 | },
876 | "children": [
877 | {
878 | "attributes": {
879 | "overloadedDeclarations": [
880 | null
881 | ],
882 | "referencedDeclaration": 4294967281,
883 | "type": "msg",
884 | "value": "msg"
885 | },
886 | "id": 3508,
887 | "name": "Identifier",
888 | "src": "183:3:2"
889 | }
890 | ],
891 | "id": 3509,
892 | "name": "MemberAccess",
893 | "src": "183:10:2"
894 | }
895 | ],
896 | "id": 3510,
897 | "name": "Assignment",
898 | "src": "175:18:2"
899 | }
900 | ],
901 | "id": 3511,
902 | "name": "ExpressionStatement",
903 | "src": "175:18:2"
904 | }
905 | ],
906 | "id": 3512,
907 | "name": "Block",
908 | "src": "169:29:2"
909 | }
910 | ],
911 | "id": 3513,
912 | "name": "FunctionDefinition",
913 | "src": "155:43:2"
914 | },
915 | {
916 | "attributes": {
917 | "name": "restricted",
918 | "virtual": false,
919 | "visibility": "internal"
920 | },
921 | "children": [
922 | {
923 | "attributes": {
924 | "parameters": [
925 | null
926 | ]
927 | },
928 | "children": [],
929 | "id": 3514,
930 | "name": "ParameterList",
931 | "src": "221:2:2"
932 | },
933 | {
934 | "children": [
935 | {
936 | "attributes": {},
937 | "children": [
938 | {
939 | "attributes": {
940 | "commonType": {
941 | "typeIdentifier": "t_address",
942 | "typeString": "address"
943 | },
944 | "isConstant": false,
945 | "isLValue": false,
946 | "isPure": false,
947 | "lValueRequested": false,
948 | "operator": "==",
949 | "type": "bool"
950 | },
951 | "children": [
952 | {
953 | "attributes": {
954 | "isConstant": false,
955 | "isLValue": false,
956 | "isPure": false,
957 | "lValueRequested": false,
958 | "member_name": "sender",
959 | "type": "address payable"
960 | },
961 | "children": [
962 | {
963 | "attributes": {
964 | "overloadedDeclarations": [
965 | null
966 | ],
967 | "referencedDeclaration": 4294967281,
968 | "type": "msg",
969 | "value": "msg"
970 | },
971 | "id": 3515,
972 | "name": "Identifier",
973 | "src": "234:3:2"
974 | }
975 | ],
976 | "id": 3516,
977 | "name": "MemberAccess",
978 | "src": "234:10:2"
979 | },
980 | {
981 | "attributes": {
982 | "overloadedDeclarations": [
983 | null
984 | ],
985 | "referencedDeclaration": 3502,
986 | "type": "address",
987 | "value": "owner"
988 | },
989 | "id": 3517,
990 | "name": "Identifier",
991 | "src": "248:5:2"
992 | }
993 | ],
994 | "id": 3518,
995 | "name": "BinaryOperation",
996 | "src": "234:19:2"
997 | },
998 | {
999 | "id": 3519,
1000 | "name": "PlaceholderStatement",
1001 | "src": "255:1:2"
1002 | }
1003 | ],
1004 | "id": 3520,
1005 | "name": "IfStatement",
1006 | "src": "230:26:2"
1007 | }
1008 | ],
1009 | "id": 3521,
1010 | "name": "Block",
1011 | "src": "224:37:2"
1012 | }
1013 | ],
1014 | "id": 3522,
1015 | "name": "ModifierDefinition",
1016 | "src": "202:59:2"
1017 | },
1018 | {
1019 | "attributes": {
1020 | "functionSelector": "fdacd576",
1021 | "implemented": true,
1022 | "isConstructor": false,
1023 | "kind": "function",
1024 | "name": "setCompleted",
1025 | "scope": 3555,
1026 | "stateMutability": "nonpayable",
1027 | "virtual": false,
1028 | "visibility": "public"
1029 | },
1030 | "children": [
1031 | {
1032 | "children": [
1033 | {
1034 | "attributes": {
1035 | "constant": false,
1036 | "mutability": "mutable",
1037 | "name": "completed",
1038 | "scope": 3534,
1039 | "stateVariable": false,
1040 | "storageLocation": "default",
1041 | "type": "uint256",
1042 | "visibility": "internal"
1043 | },
1044 | "children": [
1045 | {
1046 | "attributes": {
1047 | "name": "uint",
1048 | "type": "uint256"
1049 | },
1050 | "id": 3523,
1051 | "name": "ElementaryTypeName",
1052 | "src": "287:4:2"
1053 | }
1054 | ],
1055 | "id": 3524,
1056 | "name": "VariableDeclaration",
1057 | "src": "287:14:2"
1058 | }
1059 | ],
1060 | "id": 3525,
1061 | "name": "ParameterList",
1062 | "src": "286:16:2"
1063 | },
1064 | {
1065 | "attributes": {
1066 | "parameters": [
1067 | null
1068 | ]
1069 | },
1070 | "children": [],
1071 | "id": 3528,
1072 | "name": "ParameterList",
1073 | "src": "321:0:2"
1074 | },
1075 | {
1076 | "attributes": {},
1077 | "children": [
1078 | {
1079 | "attributes": {
1080 | "overloadedDeclarations": [
1081 | null
1082 | ],
1083 | "referencedDeclaration": 3522,
1084 | "type": "modifier ()",
1085 | "value": "restricted"
1086 | },
1087 | "id": 3526,
1088 | "name": "Identifier",
1089 | "src": "310:10:2"
1090 | }
1091 | ],
1092 | "id": 3527,
1093 | "name": "ModifierInvocation",
1094 | "src": "310:10:2"
1095 | },
1096 | {
1097 | "children": [
1098 | {
1099 | "children": [
1100 | {
1101 | "attributes": {
1102 | "isConstant": false,
1103 | "isLValue": false,
1104 | "isPure": false,
1105 | "lValueRequested": false,
1106 | "operator": "=",
1107 | "type": "uint256"
1108 | },
1109 | "children": [
1110 | {
1111 | "attributes": {
1112 | "overloadedDeclarations": [
1113 | null
1114 | ],
1115 | "referencedDeclaration": 3504,
1116 | "type": "uint256",
1117 | "value": "last_completed_migration"
1118 | },
1119 | "id": 3529,
1120 | "name": "Identifier",
1121 | "src": "327:24:2"
1122 | },
1123 | {
1124 | "attributes": {
1125 | "overloadedDeclarations": [
1126 | null
1127 | ],
1128 | "referencedDeclaration": 3524,
1129 | "type": "uint256",
1130 | "value": "completed"
1131 | },
1132 | "id": 3530,
1133 | "name": "Identifier",
1134 | "src": "354:9:2"
1135 | }
1136 | ],
1137 | "id": 3531,
1138 | "name": "Assignment",
1139 | "src": "327:36:2"
1140 | }
1141 | ],
1142 | "id": 3532,
1143 | "name": "ExpressionStatement",
1144 | "src": "327:36:2"
1145 | }
1146 | ],
1147 | "id": 3533,
1148 | "name": "Block",
1149 | "src": "321:47:2"
1150 | }
1151 | ],
1152 | "id": 3534,
1153 | "name": "FunctionDefinition",
1154 | "src": "265:103:2"
1155 | },
1156 | {
1157 | "attributes": {
1158 | "functionSelector": "0900f010",
1159 | "implemented": true,
1160 | "isConstructor": false,
1161 | "kind": "function",
1162 | "name": "upgrade",
1163 | "scope": 3555,
1164 | "stateMutability": "nonpayable",
1165 | "virtual": false,
1166 | "visibility": "public"
1167 | },
1168 | "children": [
1169 | {
1170 | "children": [
1171 | {
1172 | "attributes": {
1173 | "constant": false,
1174 | "mutability": "mutable",
1175 | "name": "new_address",
1176 | "scope": 3554,
1177 | "stateVariable": false,
1178 | "storageLocation": "default",
1179 | "type": "address",
1180 | "visibility": "internal"
1181 | },
1182 | "children": [
1183 | {
1184 | "attributes": {
1185 | "name": "address",
1186 | "stateMutability": "nonpayable",
1187 | "type": "address"
1188 | },
1189 | "id": 3535,
1190 | "name": "ElementaryTypeName",
1191 | "src": "389:7:2"
1192 | }
1193 | ],
1194 | "id": 3536,
1195 | "name": "VariableDeclaration",
1196 | "src": "389:19:2"
1197 | }
1198 | ],
1199 | "id": 3537,
1200 | "name": "ParameterList",
1201 | "src": "388:21:2"
1202 | },
1203 | {
1204 | "attributes": {
1205 | "parameters": [
1206 | null
1207 | ]
1208 | },
1209 | "children": [],
1210 | "id": 3540,
1211 | "name": "ParameterList",
1212 | "src": "428:0:2"
1213 | },
1214 | {
1215 | "attributes": {},
1216 | "children": [
1217 | {
1218 | "attributes": {
1219 | "overloadedDeclarations": [
1220 | null
1221 | ],
1222 | "referencedDeclaration": 3522,
1223 | "type": "modifier ()",
1224 | "value": "restricted"
1225 | },
1226 | "id": 3538,
1227 | "name": "Identifier",
1228 | "src": "417:10:2"
1229 | }
1230 | ],
1231 | "id": 3539,
1232 | "name": "ModifierInvocation",
1233 | "src": "417:10:2"
1234 | },
1235 | {
1236 | "children": [
1237 | {
1238 | "attributes": {
1239 | "assignments": [
1240 | 3542
1241 | ]
1242 | },
1243 | "children": [
1244 | {
1245 | "attributes": {
1246 | "constant": false,
1247 | "mutability": "mutable",
1248 | "name": "upgraded",
1249 | "scope": 3553,
1250 | "stateVariable": false,
1251 | "storageLocation": "default",
1252 | "type": "contract Migrations",
1253 | "visibility": "internal"
1254 | },
1255 | "children": [
1256 | {
1257 | "attributes": {
1258 | "name": "Migrations",
1259 | "referencedDeclaration": 3555,
1260 | "type": "contract Migrations"
1261 | },
1262 | "id": 3541,
1263 | "name": "UserDefinedTypeName",
1264 | "src": "434:10:2"
1265 | }
1266 | ],
1267 | "id": 3542,
1268 | "name": "VariableDeclaration",
1269 | "src": "434:19:2"
1270 | },
1271 | {
1272 | "attributes": {
1273 | "isConstant": false,
1274 | "isLValue": false,
1275 | "isPure": false,
1276 | "isStructConstructorCall": false,
1277 | "lValueRequested": false,
1278 | "names": [
1279 | null
1280 | ],
1281 | "tryCall": false,
1282 | "type": "contract Migrations",
1283 | "type_conversion": true
1284 | },
1285 | "children": [
1286 | {
1287 | "attributes": {
1288 | "argumentTypes": [
1289 | {
1290 | "typeIdentifier": "t_address",
1291 | "typeString": "address"
1292 | }
1293 | ],
1294 | "overloadedDeclarations": [
1295 | null
1296 | ],
1297 | "referencedDeclaration": 3555,
1298 | "type": "type(contract Migrations)",
1299 | "value": "Migrations"
1300 | },
1301 | "id": 3543,
1302 | "name": "Identifier",
1303 | "src": "456:10:2"
1304 | },
1305 | {
1306 | "attributes": {
1307 | "overloadedDeclarations": [
1308 | null
1309 | ],
1310 | "referencedDeclaration": 3536,
1311 | "type": "address",
1312 | "value": "new_address"
1313 | },
1314 | "id": 3544,
1315 | "name": "Identifier",
1316 | "src": "467:11:2"
1317 | }
1318 | ],
1319 | "id": 3545,
1320 | "name": "FunctionCall",
1321 | "src": "456:23:2"
1322 | }
1323 | ],
1324 | "id": 3546,
1325 | "name": "VariableDeclarationStatement",
1326 | "src": "434:45:2"
1327 | },
1328 | {
1329 | "children": [
1330 | {
1331 | "attributes": {
1332 | "isConstant": false,
1333 | "isLValue": false,
1334 | "isPure": false,
1335 | "isStructConstructorCall": false,
1336 | "lValueRequested": false,
1337 | "names": [
1338 | null
1339 | ],
1340 | "tryCall": false,
1341 | "type": "tuple()",
1342 | "type_conversion": false
1343 | },
1344 | "children": [
1345 | {
1346 | "attributes": {
1347 | "argumentTypes": [
1348 | {
1349 | "typeIdentifier": "t_uint256",
1350 | "typeString": "uint256"
1351 | }
1352 | ],
1353 | "isConstant": false,
1354 | "isLValue": false,
1355 | "isPure": false,
1356 | "lValueRequested": false,
1357 | "member_name": "setCompleted",
1358 | "referencedDeclaration": 3534,
1359 | "type": "function (uint256) external"
1360 | },
1361 | "children": [
1362 | {
1363 | "attributes": {
1364 | "overloadedDeclarations": [
1365 | null
1366 | ],
1367 | "referencedDeclaration": 3542,
1368 | "type": "contract Migrations",
1369 | "value": "upgraded"
1370 | },
1371 | "id": 3547,
1372 | "name": "Identifier",
1373 | "src": "485:8:2"
1374 | }
1375 | ],
1376 | "id": 3549,
1377 | "name": "MemberAccess",
1378 | "src": "485:21:2"
1379 | },
1380 | {
1381 | "attributes": {
1382 | "overloadedDeclarations": [
1383 | null
1384 | ],
1385 | "referencedDeclaration": 3504,
1386 | "type": "uint256",
1387 | "value": "last_completed_migration"
1388 | },
1389 | "id": 3550,
1390 | "name": "Identifier",
1391 | "src": "507:24:2"
1392 | }
1393 | ],
1394 | "id": 3551,
1395 | "name": "FunctionCall",
1396 | "src": "485:47:2"
1397 | }
1398 | ],
1399 | "id": 3552,
1400 | "name": "ExpressionStatement",
1401 | "src": "485:47:2"
1402 | }
1403 | ],
1404 | "id": 3553,
1405 | "name": "Block",
1406 | "src": "428:109:2"
1407 | }
1408 | ],
1409 | "id": 3554,
1410 | "name": "FunctionDefinition",
1411 | "src": "372:165:2"
1412 | }
1413 | ],
1414 | "id": 3555,
1415 | "name": "ContractDefinition",
1416 | "src": "66:473:2"
1417 | }
1418 | ],
1419 | "id": 3556,
1420 | "name": "SourceUnit",
1421 | "src": "32:508:2"
1422 | },
1423 | "compiler": {
1424 | "name": "solc",
1425 | "version": "0.7.6+commit.7338295f.Emscripten.clang"
1426 | },
1427 | "networks": {
1428 | "1615634045645": {
1429 | "events": {},
1430 | "links": {},
1431 | "address": "0x5d9186563E9A90303C4d1BE43Cba06Ce6D82E3F3",
1432 | "transactionHash": "0x5441a1e14bf24a0072912be5ec290ba5cbc7f3e4e57c36af556910a06b15b60b"
1433 | },
1434 | "1615634582841": {
1435 | "events": {},
1436 | "links": {},
1437 | "address": "0x5e44a8f8E81Aa59c0cEE5e6B3eEF841875740EeA",
1438 | "transactionHash": "0xe8002ac7e696334701655d3f949c8e31cdb11bc2d06b1966ba58c2d05d8b6905"
1439 | },
1440 | "1615642927363": {
1441 | "events": {},
1442 | "links": {},
1443 | "address": "0x699eC1a8057dfAdbc88Bb806D1d88883eD5bb46E",
1444 | "transactionHash": "0x45ade6371d82fefd9d56f89dc73118bc99f2dd474fa0e4dc242d87d4bcfd7ede"
1445 | }
1446 | },
1447 | "schemaVersion": "3.3.4",
1448 | "updatedAt": "2021-03-13T13:42:33.729Z",
1449 | "networkType": "ethereum",
1450 | "devdoc": {
1451 | "kind": "dev",
1452 | "methods": {},
1453 | "version": 1
1454 | },
1455 | "userdoc": {
1456 | "kind": "user",
1457 | "methods": {},
1458 | "version": 1
1459 | }
1460 | }
--------------------------------------------------------------------------------