├── .gitignore
├── .secret
├── Contract address.txt
├── README.md
├── img
├── architecture.PNG
└── layout.PNG
├── migrations
├── 1_initial_migration.js
└── 2_deploy_contracts.js
├── package-lock.json
├── package.json
├── public
├── favicon.ico
├── index.html
└── manifest.json
├── src
├── App.js
├── abis
│ ├── Address.json
│ ├── Context.json
│ ├── ERC165.json
│ ├── ERC721.json
│ ├── ERC721Enumerable.json
│ ├── IERC165.json
│ ├── IERC721.json
│ ├── IERC721Enumerable.json
│ ├── IERC721Metadata.json
│ ├── IERC721Receiver.json
│ ├── Migrations.json
│ ├── NFTCollection.json
│ ├── NFTMarketplace.json
│ └── Strings.json
├── components
│ ├── Content
│ │ ├── Main.js
│ │ ├── MintNFT
│ │ │ └── MintForm.js
│ │ └── NFTCollection
│ │ │ └── NFTCollection.js
│ └── Layout
│ │ ├── Navbar.js
│ │ └── Spinner.js
├── connection
│ └── web3.js
├── contracts
│ ├── Migrations.sol
│ ├── NFTCollection.sol
│ └── NFTMarketplace.sol
├── helpers
│ └── utils.js
├── img
│ ├── eth.png
│ ├── logo.png
│ └── logo2.PNG
├── index.js
└── store
│ ├── CollectionProvider.js
│ ├── MarketplaceProvider.js
│ ├── Web3Provider.js
│ ├── collection-context.js
│ ├── marketplace-context.js
│ └── web3-context.js
├── test
├── NFTCollection.test.js
└── NFTMarketplace.test.js
└── truffle-config.js
/.gitignore:
--------------------------------------------------------------------------------
1 | /node_modules
2 | /build
3 | .env
--------------------------------------------------------------------------------
/.secret:
--------------------------------------------------------------------------------
1 | artwork cupboard belt viable acquire rapid fiction slight vibrant feature repair spring
--------------------------------------------------------------------------------
/Contract address.txt:
--------------------------------------------------------------------------------
1 | USE Hardhat
2 |
3 | nftcollection: 0x5F66Af282c94920EaC5d9d34B54661761A727Ae1
4 | nftmarketplace: 0x4605fad4fe8f5414Ce2e0e2353326fdCFA52cca2
5 | https://testnet.bscscan.com/address/0x4605fad4fe8f5414Ce2e0e2353326fdCFA52cca2#code
6 |
7 | USE Truffle
8 |
9 | 0x732C1cf4D5c0D90815E5d0D04E23Ca6bC7baDe60
10 | 0x00732aA81cffdF8f3749306EfD74cB7239F8f653
11 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | `#Ethereum`
2 |
3 | # mTC - NFT Marketplace
4 |
5 | This is an open decentralized NFT Marketplace built with smart contracts powered by Ethereum. It basically consists in an open platform where each user can mint his own NFT and expose it on a marketplace.
6 |
7 | ## Table of Contents
8 |
9 | - [Getting Started](#getting-started)
10 | - [The Project](#the-project)
11 | - [Resources](#resources)
12 |
13 | ## Getting Started
14 |
15 | These instructions will get you a copy of the project up and running on your local machine for development and testing purposes.
16 |
17 | ### The repository
18 |
19 | First, you will need to `clone` or `fork` the repository into your Github account:
20 |
21 |
22 |
23 | ```
24 | https://github.com/todaycodemaster/NFTProject.git
25 | ```
26 |
27 | ### Installing
28 |
29 | First, you will need to install the dependencies with: `npm install`.
30 |
31 | Run the following command in your terminal after cloning the main repo:
32 |
33 | ```
34 | $ npm install
35 | ```
36 |
37 | Then, you will need to install Truffle globally by running the following command int your terminal:
38 |
39 | ```
40 | $ npm install -g truffle
41 | ```
42 |
43 | ### Running the Tests
44 |
45 | First, you will have to compile the smart contracts by running the following command in your terminal:
46 |
47 | ```
48 | $ truffle compile
49 | ```
50 |
51 | Then you will have to install and run Ganache to run your blockchain locally:
52 |
53 | https://www.trufflesuite.com/ganache
54 |
55 | Then, the tests that validate your solution can be executed by runing the following
56 | command:
57 |
58 | ```
59 | $ truffle test
60 | ```
61 |
62 | ### Deployment on Local Blockchain
63 |
64 | Deploy the contracts on your Ganache local blockchain by running the following command:
65 |
66 | ```
67 | $ truffle migrate
68 | ```
69 |
70 | ### Opening the User Interface
71 |
72 | First of all, it is required to install Metamask wallet as a browser extension: https://metamask.io/
73 |
74 | Then you should configure Metamask to connect to your local blockchain run by Ganache. This requires the following:
75 | - Open Metamask
76 | - Open the Network Configuration panel
77 | - Open Custom RPC
78 | - Configure your private network by adding `http://localhost:7545` on the URL and `1337` as a chain ID.
79 | - Import the first Ganache Account to Metamask by copying the Account Private Key from Ganache and pasting it on Metamask
80 |
81 | Finally you just need to run the following command in your terminal to open the User Interface:
82 |
83 | ```
84 | $ npm start
85 | ```
86 |
87 | ### Deployment on Public Network
88 |
89 | In order to deploy your smart contract, you must create your .env file and specify:
90 |
91 | - `PRIVATE_KEYS` --> Private Key of the account you are using to deploy (typically the first one in the list of Ganache)
92 | - `INFURA_API_KEY` --> API key provided by Infura: https://infura.io
93 |
94 | Then, you will need to run the following command (let's use the testnet Ropsten in this example, remember to request some Ether for your account using a faucet):
95 |
96 | ```
97 | $ truffle migrate --network ropsten
98 | ```
99 |
100 | Finally you can run the following command to generate the build artifacts of your User Interface and then deploy to your favourite host:
101 |
102 | ```
103 | npm run build
104 | ```
105 |
106 |
107 | ### Technology stack
108 |
109 | - `Solidity`
110 | - `React.js`
111 | - `Truffle`
112 | - `Web3.js`
113 | - `Ganache`
114 | - `Node.js`
115 | - `Metamask`
116 | - `IPFS`
117 |
118 | ## The Project
119 |
120 | This project consists in an open platform where each user can mint his own NFT and expose it on a marketplace by making an offer or buying NFT from others. It includes:
121 |
122 | - A smart contract which represents a collection of NFTs by following the ERC-721 standard
123 | - A smart contract which represents the NFT Marketplace and contains all the logic to make offers, fill offers...
124 | - Tests built with JavaScripts to ensure smart contracts are accomplishing the expected functionalities
125 | - A React.js front-end application as a user interface
126 |
127 | ### Project architecture
128 |
129 |
130 |
131 | The user can access the application via web-browser, and he must have the Metamask wallet installed. This interface, built with React.js, relies on the web3.js library to communicate with the smart contracts through Metamask. This means that the data reflected on the front-end application is fetched from the Ethereum blockchain. Each action performed by the user (mint a NFT, offer NFT, buy NFT...) creates a transaction on Ethereum, which will require Metamask confirmation and pay a small fee, and this transaction will permanently modify the state of the NFTCollection and NFTMarketplace smart contracts. On top of it, the user will upload the NFT Metadata to the IPFS, creating a permanent hash which will be permanently recorded on the blockchain itself to prove ownership.
132 |
133 | ### NFT Marketplace features
134 |
135 |
136 |
137 | The user can perform the following actions on this NFT Marketplace:
138 |
139 | #### Mint
140 |
141 | The user must input a name, description and upload a file (image) to mint his own NFT. Once minted, a representation of this NFT will be displayed in the marketplace and initially it will be owned by its creator. This is open for everyone, meaning everyone can participate in this NFT creation within this collection.
142 |
143 | #### Make Offer
144 |
145 | The user can offer his NFT by specifying its price (in Ether). If someone fulfills this offer, then the ownership is transferred to a new owner.
146 |
147 | #### Cancel Offer
148 |
149 | The user can cancel an offer he did in the past if in the end he does not want to sell his NFT or wants to adjust the price.
150 |
151 | #### Buy
152 |
153 | A user can buy those NFT which someone else offered. This will require paying the requested price (the Ether will be transferred to the smart contract to be claimed later on)
154 |
155 | #### Claim Funds
156 |
157 | If a user sold an NFT, he can claim his funds by clicking the button in the top-right.
158 |
159 | ## Resources
160 |
161 | - [ethereum.org](https://ethereum.org/)
162 | - [truffle suit](https://www.trufflesuite.com/)
163 | - [node.js](https://nodejs.org/)
164 | - [web3.js](https://web3js.readthedocs.io/)
165 | - [react.js](https://reactjs.org/)
166 | - [IPFS](https://ipfs.io/)
167 |
--------------------------------------------------------------------------------
/img/architecture.PNG:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/devmad119/NFTProject/49e65174c550f4a612e4a14c1c0646c7b820a258/img/architecture.PNG
--------------------------------------------------------------------------------
/img/layout.PNG:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/devmad119/NFTProject/49e65174c550f4a612e4a14c1c0646c7b820a258/img/layout.PNG
--------------------------------------------------------------------------------
/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 NFTCollection = artifacts.require("NFTCollection");
2 | const NFTMarketplace = artifacts.require("NFTMarketplace");
3 |
4 | module.exports = async function (deployer) {
5 | await deployer.deploy(NFTCollection);
6 |
7 | const deployedNFT = await NFTCollection.deployed();
8 | const NFTAddress = deployedNFT.address;
9 | await deployer.deploy(NFTMarketplace, NFTAddress);
10 | };
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "nft-gallery",
3 | "version": "0.1.0",
4 | "description": "NFT Gallery",
5 | "author": "miqueltrallero.c@gmail.com",
6 | "homepage": ".",
7 | "engines": {
8 | "node": "14.17.3"
9 | },
10 | "license": "MIT",
11 | "dependencies": {
12 | "bootstrap": "^5.0.2",
13 | "caniuse-lite": "^1.0.30001301",
14 | "ipfs-http-client": "^51.0.1",
15 | "react": "^17.0.2",
16 | "react-bootstrap": "^1.6.1",
17 | "react-dom": "^17.0.2",
18 | "react-scripts": "^4.0.3",
19 | "web3": "^1.4.0"
20 | },
21 | "devDependencies": {
22 | "@openzeppelin/contracts": "^4.2.0",
23 | "@openzeppelin/test-helpers": "^0.5.12",
24 | "@truffle/hdwallet-provider": "^2.0.1",
25 | "dotenv": "^10.0.0",
26 | "truffle": "^5.3.14",
27 | "truffle-hdwallet-provider-privkey": "^0.3.0",
28 | "truffle-plugin-verify": "^0.5.21",
29 | "truffle-privatekey-provider": "^1.5.0"
30 | },
31 | "scripts": {
32 | "start": "react-scripts start",
33 | "build": "react-scripts build",
34 | "test": "react-scripts test",
35 | "eject": "react-scripts eject",
36 | "predeploy": "npm run build",
37 | "deploy": "gh-pages -d build"
38 | },
39 | "eslintConfig": {
40 | "extends": "react-app"
41 | },
42 | "browserslist": [
43 | ">0.2%",
44 | "not dead",
45 | "not ie <= 11",
46 | "not op_mini all"
47 | ]
48 | }
--------------------------------------------------------------------------------
/public/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/devmad119/NFTProject/49e65174c550f4a612e4a14c1c0646c7b820a258/public/favicon.ico
--------------------------------------------------------------------------------
/public/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
10 |
11 |
12 | nft-marketplace
13 |
14 |
15 | You need to enable JavaScript to run this app.
16 |
17 |
18 |
--------------------------------------------------------------------------------
/public/manifest.json:
--------------------------------------------------------------------------------
1 | {
2 | "short_name": "Starter Kit",
3 | "name": "mTC Starter Kit",
4 | "icons": [
5 | {
6 | "src": "favicon.ico",
7 | "sizes": "64x64 32x32 24x24 16x16",
8 | "type": "image/x-icon"
9 | }
10 | ],
11 | "start_url": ".",
12 | "display": "standalone",
13 | "theme_color": "#000000",
14 | "background_color": "#ffffff"
15 | }
--------------------------------------------------------------------------------
/src/App.js:
--------------------------------------------------------------------------------
1 | import React, { useContext, useEffect } from 'react';
2 |
3 | import web3 from './connection/web3';
4 | import Navbar from './components/Layout/Navbar';
5 | import Main from './components/Content/Main';
6 | import Web3Context from './store/web3-context';
7 | import CollectionContext from './store/collection-context';
8 | import MarketplaceContext from './store/marketplace-context'
9 | import NFTCollection from './abis/NFTCollection.json';
10 | import NFTMarketplace from './abis/NFTMarketplace.json';
11 |
12 | const App = () => {
13 | const web3Ctx = useContext(Web3Context);
14 | const collectionCtx = useContext(CollectionContext);
15 | const marketplaceCtx = useContext(MarketplaceContext);
16 |
17 | useEffect(() => {
18 | // Check if the user has Metamask active
19 | if (!web3) {
20 | window.alert('Non-Ethereum browser detected. You should consider trying MetaMask!');
21 | return;
22 | }
23 |
24 | // Function to fetch all the blockchain data
25 | const loadBlockchainData = async () => {
26 | // Request accounts acccess if needed
27 | try {
28 | await window.ethereum.request({ method: 'eth_requestAccounts' });
29 | } catch (error) {
30 | console.error(error);
31 | }
32 |
33 | // Load account
34 | const account = await web3Ctx.loadAccount(web3);
35 |
36 | // Load Network ID
37 | const networkId = await web3Ctx.loadNetworkId(web3);
38 | // Load Contracts
39 | const nftDeployedNetwork = NFTCollection.networks[networkId];
40 | const nftContract = collectionCtx.loadContract(web3, NFTCollection, nftDeployedNetwork);
41 |
42 | const mktDeployedNetwork = NFTMarketplace.networks[networkId];
43 | const mktContract = marketplaceCtx.loadContract(web3, NFTMarketplace, mktDeployedNetwork);
44 | console.log(nftDeployedNetwork);
45 | if (nftContract) {
46 | // Load total Supply
47 | const totalSupply = await collectionCtx.loadTotalSupply(nftContract);
48 |
49 | // Load Collection
50 | collectionCtx.loadCollection(nftContract, totalSupply);
51 |
52 | // Event subscription
53 | nftContract.events.Transfer()
54 | .on('data', (event) => {
55 | collectionCtx.updateCollection(nftContract, event.returnValues.tokenId, event.returnValues.to);
56 | collectionCtx.setNftIsLoading(false);
57 | })
58 | .on('error', (error) => {
59 | console.log(error);
60 | });
61 |
62 | } else {
63 | window.alert('NFTCollection contract not deployed to detected network.')
64 | }
65 |
66 | if (mktContract) {
67 | // Load offer count
68 | const offerCount = await marketplaceCtx.loadOfferCount(mktContract);
69 |
70 | // Load offers
71 | marketplaceCtx.loadOffers(mktContract, offerCount);
72 |
73 | // Load User Funds
74 | account && marketplaceCtx.loadUserFunds(mktContract, account);
75 |
76 | // Event OfferFilled subscription
77 | mktContract.events.OfferFilled()
78 | .on('data', (event) => {
79 | marketplaceCtx.updateOffer(event.returnValues.offerId);
80 | collectionCtx.updateOwner(event.returnValues.id, event.returnValues.newOwner);
81 | marketplaceCtx.setMktIsLoading(false);
82 | })
83 | .on('error', (error) => {
84 | console.log(error);
85 | });
86 |
87 | // Event Offer subscription
88 | mktContract.events.Offer()
89 | .on('data', (event) => {
90 | marketplaceCtx.addOffer(event.returnValues);
91 | marketplaceCtx.setMktIsLoading(false);
92 | })
93 | .on('error', (error) => {
94 | console.log(error);
95 | });
96 |
97 | // Event offerCancelled subscription
98 | mktContract.events.OfferCancelled()
99 | .on('data', (event) => {
100 | marketplaceCtx.updateOffer(event.returnValues.offerId);
101 | collectionCtx.updateOwner(event.returnValues.id, event.returnValues.owner);
102 | marketplaceCtx.setMktIsLoading(false);
103 | })
104 | .on('error', (error) => {
105 | console.log(error);
106 | });
107 |
108 | } else {
109 | window.alert('NFTMarketplace contract not deployed to detected network.')
110 | }
111 |
112 | collectionCtx.setNftIsLoading(false);
113 | marketplaceCtx.setMktIsLoading(false);
114 |
115 | // Metamask Event Subscription - Account changed
116 | window.ethereum.on('accountsChanged', (accounts) => {
117 | web3Ctx.loadAccount(web3);
118 | accounts[0] && marketplaceCtx.loadUserFunds(mktContract, accounts[0]);
119 | });
120 |
121 | // Metamask Event Subscription - Network changed
122 | window.ethereum.on('chainChanged', (chainId) => {
123 | window.location.reload();
124 | });
125 | };
126 |
127 | loadBlockchainData();
128 | }, []);
129 |
130 | const showNavbar = web3 && collectionCtx.contract && marketplaceCtx.contract;
131 | const showContent = web3 && collectionCtx.contract && marketplaceCtx.contract && web3Ctx.account;
132 |
133 | return (
134 |
135 | {showNavbar && }
136 | {showContent && }
137 |
138 | );
139 | };
140 |
141 | export default App;
--------------------------------------------------------------------------------
/src/abis/Context.json:
--------------------------------------------------------------------------------
1 | {
2 | "contractName": "Context",
3 | "abi": [],
4 | "metadata": "{\"compiler\":{\"version\":\"0.8.11+commit.d7f03943\"},\"language\":\"Solidity\",\"output\":{\"abi\":[],\"devdoc\":{\"kind\":\"dev\",\"methods\":{},\"version\":1},\"userdoc\":{\"kind\":\"user\",\"methods\":{},\"version\":1}},\"settings\":{\"compilationTarget\":{\"@openzeppelin/contracts/utils/Context.sol\":\"Context\"},\"evmVersion\":\"london\",\"libraries\":{},\"metadata\":{\"bytecodeHash\":\"ipfs\"},\"optimizer\":{\"enabled\":false,\"runs\":200},\"remappings\":[]},\"sources\":{\"@openzeppelin/contracts/utils/Context.sol\":{\"keccak256\":\"0x95098bd1d9c8dec4d80d3dedb88a0d949fa0d740ee99f2aa466bc308216ca6d5\",\"license\":\"MIT\",\"urls\":[\"bzz-raw://7fec968dcd68e13961521fa3c7dd87baecad91a2653b19240e81f21cc4f3ba85\",\"dweb:/ipfs/QmaXtsYt4Mphm8XHNUfk2me1cF3ssS2SqDBNFpYAzMjomC\"]}},\"version\":1}",
5 | "bytecode": "0x",
6 | "deployedBytecode": "0x",
7 | "immutableReferences": {},
8 | "generatedSources": [],
9 | "deployedGeneratedSources": [],
10 | "sourceMap": "",
11 | "deployedSourceMap": "",
12 | "source": "// SPDX-License-Identifier: MIT\n\npragma solidity ^0.8.0;\n\n/*\n * @dev Provides information about the current execution context, including the\n * sender of the transaction and its data. While these are generally available\n * via msg.sender and msg.data, they should not be accessed in such a direct\n * manner, since when dealing with meta-transactions the account sending and\n * paying for execution may not be the actual sender (as far as an application\n * is concerned).\n *\n * This contract is only required for intermediate, library-like contracts.\n */\nabstract contract Context {\n function _msgSender() internal view virtual returns (address) {\n return msg.sender;\n }\n\n function _msgData() internal view virtual returns (bytes calldata) {\n return msg.data;\n }\n}\n",
13 | "sourcePath": "@openzeppelin\\contracts\\utils\\Context.sol",
14 | "ast": {
15 | "absolutePath": "@openzeppelin/contracts/utils/Context.sol",
16 | "exportedSymbols": {
17 | "Context": [
18 | 1664
19 | ]
20 | },
21 | "id": 1665,
22 | "license": "MIT",
23 | "nodeType": "SourceUnit",
24 | "nodes": [
25 | {
26 | "id": 1645,
27 | "literals": [
28 | "solidity",
29 | "^",
30 | "0.8",
31 | ".0"
32 | ],
33 | "nodeType": "PragmaDirective",
34 | "src": "33:23:7"
35 | },
36 | {
37 | "abstract": true,
38 | "baseContracts": [],
39 | "canonicalName": "Context",
40 | "contractDependencies": [],
41 | "contractKind": "contract",
42 | "fullyImplemented": true,
43 | "id": 1664,
44 | "linearizedBaseContracts": [
45 | 1664
46 | ],
47 | "name": "Context",
48 | "nameLocation": "572:7:7",
49 | "nodeType": "ContractDefinition",
50 | "nodes": [
51 | {
52 | "body": {
53 | "id": 1653,
54 | "nodeType": "Block",
55 | "src": "648:34:7",
56 | "statements": [
57 | {
58 | "expression": {
59 | "expression": {
60 | "id": 1650,
61 | "name": "msg",
62 | "nodeType": "Identifier",
63 | "overloadedDeclarations": [],
64 | "referencedDeclaration": 4294967281,
65 | "src": "665:3:7",
66 | "typeDescriptions": {
67 | "typeIdentifier": "t_magic_message",
68 | "typeString": "msg"
69 | }
70 | },
71 | "id": 1651,
72 | "isConstant": false,
73 | "isLValue": false,
74 | "isPure": false,
75 | "lValueRequested": false,
76 | "memberName": "sender",
77 | "nodeType": "MemberAccess",
78 | "src": "665:10:7",
79 | "typeDescriptions": {
80 | "typeIdentifier": "t_address",
81 | "typeString": "address"
82 | }
83 | },
84 | "functionReturnParameters": 1649,
85 | "id": 1652,
86 | "nodeType": "Return",
87 | "src": "658:17:7"
88 | }
89 | ]
90 | },
91 | "id": 1654,
92 | "implemented": true,
93 | "kind": "function",
94 | "modifiers": [],
95 | "name": "_msgSender",
96 | "nameLocation": "595:10:7",
97 | "nodeType": "FunctionDefinition",
98 | "parameters": {
99 | "id": 1646,
100 | "nodeType": "ParameterList",
101 | "parameters": [],
102 | "src": "605:2:7"
103 | },
104 | "returnParameters": {
105 | "id": 1649,
106 | "nodeType": "ParameterList",
107 | "parameters": [
108 | {
109 | "constant": false,
110 | "id": 1648,
111 | "mutability": "mutable",
112 | "name": "",
113 | "nameLocation": "-1:-1:-1",
114 | "nodeType": "VariableDeclaration",
115 | "scope": 1654,
116 | "src": "639:7:7",
117 | "stateVariable": false,
118 | "storageLocation": "default",
119 | "typeDescriptions": {
120 | "typeIdentifier": "t_address",
121 | "typeString": "address"
122 | },
123 | "typeName": {
124 | "id": 1647,
125 | "name": "address",
126 | "nodeType": "ElementaryTypeName",
127 | "src": "639:7:7",
128 | "stateMutability": "nonpayable",
129 | "typeDescriptions": {
130 | "typeIdentifier": "t_address",
131 | "typeString": "address"
132 | }
133 | },
134 | "visibility": "internal"
135 | }
136 | ],
137 | "src": "638:9:7"
138 | },
139 | "scope": 1664,
140 | "src": "586:96:7",
141 | "stateMutability": "view",
142 | "virtual": true,
143 | "visibility": "internal"
144 | },
145 | {
146 | "body": {
147 | "id": 1662,
148 | "nodeType": "Block",
149 | "src": "755:32:7",
150 | "statements": [
151 | {
152 | "expression": {
153 | "expression": {
154 | "id": 1659,
155 | "name": "msg",
156 | "nodeType": "Identifier",
157 | "overloadedDeclarations": [],
158 | "referencedDeclaration": 4294967281,
159 | "src": "772:3:7",
160 | "typeDescriptions": {
161 | "typeIdentifier": "t_magic_message",
162 | "typeString": "msg"
163 | }
164 | },
165 | "id": 1660,
166 | "isConstant": false,
167 | "isLValue": false,
168 | "isPure": false,
169 | "lValueRequested": false,
170 | "memberName": "data",
171 | "nodeType": "MemberAccess",
172 | "src": "772:8:7",
173 | "typeDescriptions": {
174 | "typeIdentifier": "t_bytes_calldata_ptr",
175 | "typeString": "bytes calldata"
176 | }
177 | },
178 | "functionReturnParameters": 1658,
179 | "id": 1661,
180 | "nodeType": "Return",
181 | "src": "765:15:7"
182 | }
183 | ]
184 | },
185 | "id": 1663,
186 | "implemented": true,
187 | "kind": "function",
188 | "modifiers": [],
189 | "name": "_msgData",
190 | "nameLocation": "697:8:7",
191 | "nodeType": "FunctionDefinition",
192 | "parameters": {
193 | "id": 1655,
194 | "nodeType": "ParameterList",
195 | "parameters": [],
196 | "src": "705:2:7"
197 | },
198 | "returnParameters": {
199 | "id": 1658,
200 | "nodeType": "ParameterList",
201 | "parameters": [
202 | {
203 | "constant": false,
204 | "id": 1657,
205 | "mutability": "mutable",
206 | "name": "",
207 | "nameLocation": "-1:-1:-1",
208 | "nodeType": "VariableDeclaration",
209 | "scope": 1663,
210 | "src": "739:14:7",
211 | "stateVariable": false,
212 | "storageLocation": "calldata",
213 | "typeDescriptions": {
214 | "typeIdentifier": "t_bytes_calldata_ptr",
215 | "typeString": "bytes"
216 | },
217 | "typeName": {
218 | "id": 1656,
219 | "name": "bytes",
220 | "nodeType": "ElementaryTypeName",
221 | "src": "739:5:7",
222 | "typeDescriptions": {
223 | "typeIdentifier": "t_bytes_storage_ptr",
224 | "typeString": "bytes"
225 | }
226 | },
227 | "visibility": "internal"
228 | }
229 | ],
230 | "src": "738:16:7"
231 | },
232 | "scope": 1664,
233 | "src": "688:99:7",
234 | "stateMutability": "view",
235 | "virtual": true,
236 | "visibility": "internal"
237 | }
238 | ],
239 | "scope": 1665,
240 | "src": "554:235:7",
241 | "usedErrors": []
242 | }
243 | ],
244 | "src": "33:757:7"
245 | },
246 | "legacyAST": {
247 | "absolutePath": "@openzeppelin/contracts/utils/Context.sol",
248 | "exportedSymbols": {
249 | "Context": [
250 | 1664
251 | ]
252 | },
253 | "id": 1665,
254 | "license": "MIT",
255 | "nodeType": "SourceUnit",
256 | "nodes": [
257 | {
258 | "id": 1645,
259 | "literals": [
260 | "solidity",
261 | "^",
262 | "0.8",
263 | ".0"
264 | ],
265 | "nodeType": "PragmaDirective",
266 | "src": "33:23:7"
267 | },
268 | {
269 | "abstract": true,
270 | "baseContracts": [],
271 | "canonicalName": "Context",
272 | "contractDependencies": [],
273 | "contractKind": "contract",
274 | "fullyImplemented": true,
275 | "id": 1664,
276 | "linearizedBaseContracts": [
277 | 1664
278 | ],
279 | "name": "Context",
280 | "nameLocation": "572:7:7",
281 | "nodeType": "ContractDefinition",
282 | "nodes": [
283 | {
284 | "body": {
285 | "id": 1653,
286 | "nodeType": "Block",
287 | "src": "648:34:7",
288 | "statements": [
289 | {
290 | "expression": {
291 | "expression": {
292 | "id": 1650,
293 | "name": "msg",
294 | "nodeType": "Identifier",
295 | "overloadedDeclarations": [],
296 | "referencedDeclaration": 4294967281,
297 | "src": "665:3:7",
298 | "typeDescriptions": {
299 | "typeIdentifier": "t_magic_message",
300 | "typeString": "msg"
301 | }
302 | },
303 | "id": 1651,
304 | "isConstant": false,
305 | "isLValue": false,
306 | "isPure": false,
307 | "lValueRequested": false,
308 | "memberName": "sender",
309 | "nodeType": "MemberAccess",
310 | "src": "665:10:7",
311 | "typeDescriptions": {
312 | "typeIdentifier": "t_address",
313 | "typeString": "address"
314 | }
315 | },
316 | "functionReturnParameters": 1649,
317 | "id": 1652,
318 | "nodeType": "Return",
319 | "src": "658:17:7"
320 | }
321 | ]
322 | },
323 | "id": 1654,
324 | "implemented": true,
325 | "kind": "function",
326 | "modifiers": [],
327 | "name": "_msgSender",
328 | "nameLocation": "595:10:7",
329 | "nodeType": "FunctionDefinition",
330 | "parameters": {
331 | "id": 1646,
332 | "nodeType": "ParameterList",
333 | "parameters": [],
334 | "src": "605:2:7"
335 | },
336 | "returnParameters": {
337 | "id": 1649,
338 | "nodeType": "ParameterList",
339 | "parameters": [
340 | {
341 | "constant": false,
342 | "id": 1648,
343 | "mutability": "mutable",
344 | "name": "",
345 | "nameLocation": "-1:-1:-1",
346 | "nodeType": "VariableDeclaration",
347 | "scope": 1654,
348 | "src": "639:7:7",
349 | "stateVariable": false,
350 | "storageLocation": "default",
351 | "typeDescriptions": {
352 | "typeIdentifier": "t_address",
353 | "typeString": "address"
354 | },
355 | "typeName": {
356 | "id": 1647,
357 | "name": "address",
358 | "nodeType": "ElementaryTypeName",
359 | "src": "639:7:7",
360 | "stateMutability": "nonpayable",
361 | "typeDescriptions": {
362 | "typeIdentifier": "t_address",
363 | "typeString": "address"
364 | }
365 | },
366 | "visibility": "internal"
367 | }
368 | ],
369 | "src": "638:9:7"
370 | },
371 | "scope": 1664,
372 | "src": "586:96:7",
373 | "stateMutability": "view",
374 | "virtual": true,
375 | "visibility": "internal"
376 | },
377 | {
378 | "body": {
379 | "id": 1662,
380 | "nodeType": "Block",
381 | "src": "755:32:7",
382 | "statements": [
383 | {
384 | "expression": {
385 | "expression": {
386 | "id": 1659,
387 | "name": "msg",
388 | "nodeType": "Identifier",
389 | "overloadedDeclarations": [],
390 | "referencedDeclaration": 4294967281,
391 | "src": "772:3:7",
392 | "typeDescriptions": {
393 | "typeIdentifier": "t_magic_message",
394 | "typeString": "msg"
395 | }
396 | },
397 | "id": 1660,
398 | "isConstant": false,
399 | "isLValue": false,
400 | "isPure": false,
401 | "lValueRequested": false,
402 | "memberName": "data",
403 | "nodeType": "MemberAccess",
404 | "src": "772:8:7",
405 | "typeDescriptions": {
406 | "typeIdentifier": "t_bytes_calldata_ptr",
407 | "typeString": "bytes calldata"
408 | }
409 | },
410 | "functionReturnParameters": 1658,
411 | "id": 1661,
412 | "nodeType": "Return",
413 | "src": "765:15:7"
414 | }
415 | ]
416 | },
417 | "id": 1663,
418 | "implemented": true,
419 | "kind": "function",
420 | "modifiers": [],
421 | "name": "_msgData",
422 | "nameLocation": "697:8:7",
423 | "nodeType": "FunctionDefinition",
424 | "parameters": {
425 | "id": 1655,
426 | "nodeType": "ParameterList",
427 | "parameters": [],
428 | "src": "705:2:7"
429 | },
430 | "returnParameters": {
431 | "id": 1658,
432 | "nodeType": "ParameterList",
433 | "parameters": [
434 | {
435 | "constant": false,
436 | "id": 1657,
437 | "mutability": "mutable",
438 | "name": "",
439 | "nameLocation": "-1:-1:-1",
440 | "nodeType": "VariableDeclaration",
441 | "scope": 1663,
442 | "src": "739:14:7",
443 | "stateVariable": false,
444 | "storageLocation": "calldata",
445 | "typeDescriptions": {
446 | "typeIdentifier": "t_bytes_calldata_ptr",
447 | "typeString": "bytes"
448 | },
449 | "typeName": {
450 | "id": 1656,
451 | "name": "bytes",
452 | "nodeType": "ElementaryTypeName",
453 | "src": "739:5:7",
454 | "typeDescriptions": {
455 | "typeIdentifier": "t_bytes_storage_ptr",
456 | "typeString": "bytes"
457 | }
458 | },
459 | "visibility": "internal"
460 | }
461 | ],
462 | "src": "738:16:7"
463 | },
464 | "scope": 1664,
465 | "src": "688:99:7",
466 | "stateMutability": "view",
467 | "virtual": true,
468 | "visibility": "internal"
469 | }
470 | ],
471 | "scope": 1665,
472 | "src": "554:235:7",
473 | "usedErrors": []
474 | }
475 | ],
476 | "src": "33:757:7"
477 | },
478 | "compiler": {
479 | "name": "solc",
480 | "version": "0.8.11+commit.d7f03943.Emscripten.clang"
481 | },
482 | "networks": {},
483 | "schemaVersion": "3.4.4",
484 | "updatedAt": "2022-01-23T15:58:46.595Z",
485 | "devdoc": {
486 | "kind": "dev",
487 | "methods": {},
488 | "version": 1
489 | },
490 | "userdoc": {
491 | "kind": "user",
492 | "methods": {},
493 | "version": 1
494 | }
495 | }
--------------------------------------------------------------------------------
/src/abis/ERC165.json:
--------------------------------------------------------------------------------
1 | {
2 | "contractName": "ERC165",
3 | "abi": [
4 | {
5 | "inputs": [
6 | {
7 | "internalType": "bytes4",
8 | "name": "interfaceId",
9 | "type": "bytes4"
10 | }
11 | ],
12 | "name": "supportsInterface",
13 | "outputs": [
14 | {
15 | "internalType": "bool",
16 | "name": "",
17 | "type": "bool"
18 | }
19 | ],
20 | "stateMutability": "view",
21 | "type": "function"
22 | }
23 | ],
24 | "metadata": "{\"compiler\":{\"version\":\"0.8.11+commit.d7f03943\"},\"language\":\"Solidity\",\"output\":{\"abi\":[{\"inputs\":[{\"internalType\":\"bytes4\",\"name\":\"interfaceId\",\"type\":\"bytes4\"}],\"name\":\"supportsInterface\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"view\",\"type\":\"function\"}],\"devdoc\":{\"details\":\"Implementation of the {IERC165} interface. Contracts that want to implement ERC165 should inherit from this contract and override {supportsInterface} to check for the additional interface id that will be supported. For example: ```solidity function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) { return interfaceId == type(MyInterface).interfaceId || super.supportsInterface(interfaceId); } ``` Alternatively, {ERC165Storage} provides an easier to use but more expensive implementation.\",\"kind\":\"dev\",\"methods\":{\"supportsInterface(bytes4)\":{\"details\":\"See {IERC165-supportsInterface}.\"}},\"version\":1},\"userdoc\":{\"kind\":\"user\",\"methods\":{},\"version\":1}},\"settings\":{\"compilationTarget\":{\"@openzeppelin/contracts/utils/introspection/ERC165.sol\":\"ERC165\"},\"evmVersion\":\"london\",\"libraries\":{},\"metadata\":{\"bytecodeHash\":\"ipfs\"},\"optimizer\":{\"enabled\":false,\"runs\":200},\"remappings\":[]},\"sources\":{\"@openzeppelin/contracts/utils/introspection/ERC165.sol\":{\"keccak256\":\"0x5718c5df9bd67ac68a796961df938821bb5dc0cd4c6118d77e9145afb187409b\",\"license\":\"MIT\",\"urls\":[\"bzz-raw://d10e1d9b26042424789246603906ad06143bf9a928f4e99de8b5e3bdc662f549\",\"dweb:/ipfs/Qmejonoaj5MLekPus229rJQHcC6E9dz2xorjHJR84fMfmn\"]},\"@openzeppelin/contracts/utils/introspection/IERC165.sol\":{\"keccak256\":\"0xa28007762d9da9db878dd421960c8cb9a10471f47ab5c1b3309bfe48e9e79ff4\",\"license\":\"MIT\",\"urls\":[\"bzz-raw://796ab6e88af7bf0e78def0f059310c903af6a312b565344e0ff524a0f26e81c6\",\"dweb:/ipfs/QmcsVgLgzWdor3UnAztUkXKNGcysm1MPneWksF72AvnwBx\"]}},\"version\":1}",
25 | "bytecode": "0x",
26 | "deployedBytecode": "0x",
27 | "immutableReferences": {},
28 | "generatedSources": [],
29 | "deployedGeneratedSources": [],
30 | "sourceMap": "",
31 | "deployedSourceMap": "",
32 | "source": "// SPDX-License-Identifier: MIT\n\npragma solidity ^0.8.0;\n\nimport \"./IERC165.sol\";\n\n/**\n * @dev Implementation of the {IERC165} interface.\n *\n * Contracts that want to implement ERC165 should inherit from this contract and override {supportsInterface} to check\n * for the additional interface id that will be supported. For example:\n *\n * ```solidity\n * function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {\n * return interfaceId == type(MyInterface).interfaceId || super.supportsInterface(interfaceId);\n * }\n * ```\n *\n * Alternatively, {ERC165Storage} provides an easier to use but more expensive implementation.\n */\nabstract contract ERC165 is IERC165 {\n /**\n * @dev See {IERC165-supportsInterface}.\n */\n function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {\n return interfaceId == type(IERC165).interfaceId;\n }\n}\n",
33 | "sourcePath": "@openzeppelin\\contracts\\utils\\introspection\\ERC165.sol",
34 | "ast": {
35 | "absolutePath": "@openzeppelin/contracts/utils/introspection/ERC165.sol",
36 | "exportedSymbols": {
37 | "ERC165": [
38 | 1891
39 | ],
40 | "IERC165": [
41 | 1903
42 | ]
43 | },
44 | "id": 1892,
45 | "license": "MIT",
46 | "nodeType": "SourceUnit",
47 | "nodes": [
48 | {
49 | "id": 1869,
50 | "literals": [
51 | "solidity",
52 | "^",
53 | "0.8",
54 | ".0"
55 | ],
56 | "nodeType": "PragmaDirective",
57 | "src": "33:23:9"
58 | },
59 | {
60 | "absolutePath": "@openzeppelin/contracts/utils/introspection/IERC165.sol",
61 | "file": "./IERC165.sol",
62 | "id": 1870,
63 | "nameLocation": "-1:-1:-1",
64 | "nodeType": "ImportDirective",
65 | "scope": 1892,
66 | "sourceUnit": 1904,
67 | "src": "58:23:9",
68 | "symbolAliases": [],
69 | "unitAlias": ""
70 | },
71 | {
72 | "abstract": true,
73 | "baseContracts": [
74 | {
75 | "baseName": {
76 | "id": 1872,
77 | "name": "IERC165",
78 | "nodeType": "IdentifierPath",
79 | "referencedDeclaration": 1903,
80 | "src": "688:7:9"
81 | },
82 | "id": 1873,
83 | "nodeType": "InheritanceSpecifier",
84 | "src": "688:7:9"
85 | }
86 | ],
87 | "canonicalName": "ERC165",
88 | "contractDependencies": [],
89 | "contractKind": "contract",
90 | "documentation": {
91 | "id": 1871,
92 | "nodeType": "StructuredDocumentation",
93 | "src": "83:576:9",
94 | "text": " @dev Implementation of the {IERC165} interface.\n Contracts that want to implement ERC165 should inherit from this contract and override {supportsInterface} to check\n for the additional interface id that will be supported. For example:\n ```solidity\n function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {\n return interfaceId == type(MyInterface).interfaceId || super.supportsInterface(interfaceId);\n }\n ```\n Alternatively, {ERC165Storage} provides an easier to use but more expensive implementation."
95 | },
96 | "fullyImplemented": true,
97 | "id": 1891,
98 | "linearizedBaseContracts": [
99 | 1891,
100 | 1903
101 | ],
102 | "name": "ERC165",
103 | "nameLocation": "678:6:9",
104 | "nodeType": "ContractDefinition",
105 | "nodes": [
106 | {
107 | "baseFunctions": [
108 | 1902
109 | ],
110 | "body": {
111 | "id": 1889,
112 | "nodeType": "Block",
113 | "src": "854:64:9",
114 | "statements": [
115 | {
116 | "expression": {
117 | "commonType": {
118 | "typeIdentifier": "t_bytes4",
119 | "typeString": "bytes4"
120 | },
121 | "id": 1887,
122 | "isConstant": false,
123 | "isLValue": false,
124 | "isPure": false,
125 | "lValueRequested": false,
126 | "leftExpression": {
127 | "id": 1882,
128 | "name": "interfaceId",
129 | "nodeType": "Identifier",
130 | "overloadedDeclarations": [],
131 | "referencedDeclaration": 1876,
132 | "src": "871:11:9",
133 | "typeDescriptions": {
134 | "typeIdentifier": "t_bytes4",
135 | "typeString": "bytes4"
136 | }
137 | },
138 | "nodeType": "BinaryOperation",
139 | "operator": "==",
140 | "rightExpression": {
141 | "expression": {
142 | "arguments": [
143 | {
144 | "id": 1884,
145 | "name": "IERC165",
146 | "nodeType": "Identifier",
147 | "overloadedDeclarations": [],
148 | "referencedDeclaration": 1903,
149 | "src": "891:7:9",
150 | "typeDescriptions": {
151 | "typeIdentifier": "t_type$_t_contract$_IERC165_$1903_$",
152 | "typeString": "type(contract IERC165)"
153 | }
154 | }
155 | ],
156 | "expression": {
157 | "argumentTypes": [
158 | {
159 | "typeIdentifier": "t_type$_t_contract$_IERC165_$1903_$",
160 | "typeString": "type(contract IERC165)"
161 | }
162 | ],
163 | "id": 1883,
164 | "name": "type",
165 | "nodeType": "Identifier",
166 | "overloadedDeclarations": [],
167 | "referencedDeclaration": 4294967269,
168 | "src": "886:4:9",
169 | "typeDescriptions": {
170 | "typeIdentifier": "t_function_metatype_pure$__$returns$__$",
171 | "typeString": "function () pure"
172 | }
173 | },
174 | "id": 1885,
175 | "isConstant": false,
176 | "isLValue": false,
177 | "isPure": true,
178 | "kind": "functionCall",
179 | "lValueRequested": false,
180 | "names": [],
181 | "nodeType": "FunctionCall",
182 | "src": "886:13:9",
183 | "tryCall": false,
184 | "typeDescriptions": {
185 | "typeIdentifier": "t_magic_meta_type_t_contract$_IERC165_$1903",
186 | "typeString": "type(contract IERC165)"
187 | }
188 | },
189 | "id": 1886,
190 | "isConstant": false,
191 | "isLValue": false,
192 | "isPure": true,
193 | "lValueRequested": false,
194 | "memberName": "interfaceId",
195 | "nodeType": "MemberAccess",
196 | "src": "886:25:9",
197 | "typeDescriptions": {
198 | "typeIdentifier": "t_bytes4",
199 | "typeString": "bytes4"
200 | }
201 | },
202 | "src": "871:40:9",
203 | "typeDescriptions": {
204 | "typeIdentifier": "t_bool",
205 | "typeString": "bool"
206 | }
207 | },
208 | "functionReturnParameters": 1881,
209 | "id": 1888,
210 | "nodeType": "Return",
211 | "src": "864:47:9"
212 | }
213 | ]
214 | },
215 | "documentation": {
216 | "id": 1874,
217 | "nodeType": "StructuredDocumentation",
218 | "src": "702:56:9",
219 | "text": " @dev See {IERC165-supportsInterface}."
220 | },
221 | "functionSelector": "01ffc9a7",
222 | "id": 1890,
223 | "implemented": true,
224 | "kind": "function",
225 | "modifiers": [],
226 | "name": "supportsInterface",
227 | "nameLocation": "772:17:9",
228 | "nodeType": "FunctionDefinition",
229 | "overrides": {
230 | "id": 1878,
231 | "nodeType": "OverrideSpecifier",
232 | "overrides": [],
233 | "src": "830:8:9"
234 | },
235 | "parameters": {
236 | "id": 1877,
237 | "nodeType": "ParameterList",
238 | "parameters": [
239 | {
240 | "constant": false,
241 | "id": 1876,
242 | "mutability": "mutable",
243 | "name": "interfaceId",
244 | "nameLocation": "797:11:9",
245 | "nodeType": "VariableDeclaration",
246 | "scope": 1890,
247 | "src": "790:18:9",
248 | "stateVariable": false,
249 | "storageLocation": "default",
250 | "typeDescriptions": {
251 | "typeIdentifier": "t_bytes4",
252 | "typeString": "bytes4"
253 | },
254 | "typeName": {
255 | "id": 1875,
256 | "name": "bytes4",
257 | "nodeType": "ElementaryTypeName",
258 | "src": "790:6:9",
259 | "typeDescriptions": {
260 | "typeIdentifier": "t_bytes4",
261 | "typeString": "bytes4"
262 | }
263 | },
264 | "visibility": "internal"
265 | }
266 | ],
267 | "src": "789:20:9"
268 | },
269 | "returnParameters": {
270 | "id": 1881,
271 | "nodeType": "ParameterList",
272 | "parameters": [
273 | {
274 | "constant": false,
275 | "id": 1880,
276 | "mutability": "mutable",
277 | "name": "",
278 | "nameLocation": "-1:-1:-1",
279 | "nodeType": "VariableDeclaration",
280 | "scope": 1890,
281 | "src": "848:4:9",
282 | "stateVariable": false,
283 | "storageLocation": "default",
284 | "typeDescriptions": {
285 | "typeIdentifier": "t_bool",
286 | "typeString": "bool"
287 | },
288 | "typeName": {
289 | "id": 1879,
290 | "name": "bool",
291 | "nodeType": "ElementaryTypeName",
292 | "src": "848:4:9",
293 | "typeDescriptions": {
294 | "typeIdentifier": "t_bool",
295 | "typeString": "bool"
296 | }
297 | },
298 | "visibility": "internal"
299 | }
300 | ],
301 | "src": "847:6:9"
302 | },
303 | "scope": 1891,
304 | "src": "763:155:9",
305 | "stateMutability": "view",
306 | "virtual": true,
307 | "visibility": "public"
308 | }
309 | ],
310 | "scope": 1892,
311 | "src": "660:260:9",
312 | "usedErrors": []
313 | }
314 | ],
315 | "src": "33:888:9"
316 | },
317 | "legacyAST": {
318 | "absolutePath": "@openzeppelin/contracts/utils/introspection/ERC165.sol",
319 | "exportedSymbols": {
320 | "ERC165": [
321 | 1891
322 | ],
323 | "IERC165": [
324 | 1903
325 | ]
326 | },
327 | "id": 1892,
328 | "license": "MIT",
329 | "nodeType": "SourceUnit",
330 | "nodes": [
331 | {
332 | "id": 1869,
333 | "literals": [
334 | "solidity",
335 | "^",
336 | "0.8",
337 | ".0"
338 | ],
339 | "nodeType": "PragmaDirective",
340 | "src": "33:23:9"
341 | },
342 | {
343 | "absolutePath": "@openzeppelin/contracts/utils/introspection/IERC165.sol",
344 | "file": "./IERC165.sol",
345 | "id": 1870,
346 | "nameLocation": "-1:-1:-1",
347 | "nodeType": "ImportDirective",
348 | "scope": 1892,
349 | "sourceUnit": 1904,
350 | "src": "58:23:9",
351 | "symbolAliases": [],
352 | "unitAlias": ""
353 | },
354 | {
355 | "abstract": true,
356 | "baseContracts": [
357 | {
358 | "baseName": {
359 | "id": 1872,
360 | "name": "IERC165",
361 | "nodeType": "IdentifierPath",
362 | "referencedDeclaration": 1903,
363 | "src": "688:7:9"
364 | },
365 | "id": 1873,
366 | "nodeType": "InheritanceSpecifier",
367 | "src": "688:7:9"
368 | }
369 | ],
370 | "canonicalName": "ERC165",
371 | "contractDependencies": [],
372 | "contractKind": "contract",
373 | "documentation": {
374 | "id": 1871,
375 | "nodeType": "StructuredDocumentation",
376 | "src": "83:576:9",
377 | "text": " @dev Implementation of the {IERC165} interface.\n Contracts that want to implement ERC165 should inherit from this contract and override {supportsInterface} to check\n for the additional interface id that will be supported. For example:\n ```solidity\n function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {\n return interfaceId == type(MyInterface).interfaceId || super.supportsInterface(interfaceId);\n }\n ```\n Alternatively, {ERC165Storage} provides an easier to use but more expensive implementation."
378 | },
379 | "fullyImplemented": true,
380 | "id": 1891,
381 | "linearizedBaseContracts": [
382 | 1891,
383 | 1903
384 | ],
385 | "name": "ERC165",
386 | "nameLocation": "678:6:9",
387 | "nodeType": "ContractDefinition",
388 | "nodes": [
389 | {
390 | "baseFunctions": [
391 | 1902
392 | ],
393 | "body": {
394 | "id": 1889,
395 | "nodeType": "Block",
396 | "src": "854:64:9",
397 | "statements": [
398 | {
399 | "expression": {
400 | "commonType": {
401 | "typeIdentifier": "t_bytes4",
402 | "typeString": "bytes4"
403 | },
404 | "id": 1887,
405 | "isConstant": false,
406 | "isLValue": false,
407 | "isPure": false,
408 | "lValueRequested": false,
409 | "leftExpression": {
410 | "id": 1882,
411 | "name": "interfaceId",
412 | "nodeType": "Identifier",
413 | "overloadedDeclarations": [],
414 | "referencedDeclaration": 1876,
415 | "src": "871:11:9",
416 | "typeDescriptions": {
417 | "typeIdentifier": "t_bytes4",
418 | "typeString": "bytes4"
419 | }
420 | },
421 | "nodeType": "BinaryOperation",
422 | "operator": "==",
423 | "rightExpression": {
424 | "expression": {
425 | "arguments": [
426 | {
427 | "id": 1884,
428 | "name": "IERC165",
429 | "nodeType": "Identifier",
430 | "overloadedDeclarations": [],
431 | "referencedDeclaration": 1903,
432 | "src": "891:7:9",
433 | "typeDescriptions": {
434 | "typeIdentifier": "t_type$_t_contract$_IERC165_$1903_$",
435 | "typeString": "type(contract IERC165)"
436 | }
437 | }
438 | ],
439 | "expression": {
440 | "argumentTypes": [
441 | {
442 | "typeIdentifier": "t_type$_t_contract$_IERC165_$1903_$",
443 | "typeString": "type(contract IERC165)"
444 | }
445 | ],
446 | "id": 1883,
447 | "name": "type",
448 | "nodeType": "Identifier",
449 | "overloadedDeclarations": [],
450 | "referencedDeclaration": 4294967269,
451 | "src": "886:4:9",
452 | "typeDescriptions": {
453 | "typeIdentifier": "t_function_metatype_pure$__$returns$__$",
454 | "typeString": "function () pure"
455 | }
456 | },
457 | "id": 1885,
458 | "isConstant": false,
459 | "isLValue": false,
460 | "isPure": true,
461 | "kind": "functionCall",
462 | "lValueRequested": false,
463 | "names": [],
464 | "nodeType": "FunctionCall",
465 | "src": "886:13:9",
466 | "tryCall": false,
467 | "typeDescriptions": {
468 | "typeIdentifier": "t_magic_meta_type_t_contract$_IERC165_$1903",
469 | "typeString": "type(contract IERC165)"
470 | }
471 | },
472 | "id": 1886,
473 | "isConstant": false,
474 | "isLValue": false,
475 | "isPure": true,
476 | "lValueRequested": false,
477 | "memberName": "interfaceId",
478 | "nodeType": "MemberAccess",
479 | "src": "886:25:9",
480 | "typeDescriptions": {
481 | "typeIdentifier": "t_bytes4",
482 | "typeString": "bytes4"
483 | }
484 | },
485 | "src": "871:40:9",
486 | "typeDescriptions": {
487 | "typeIdentifier": "t_bool",
488 | "typeString": "bool"
489 | }
490 | },
491 | "functionReturnParameters": 1881,
492 | "id": 1888,
493 | "nodeType": "Return",
494 | "src": "864:47:9"
495 | }
496 | ]
497 | },
498 | "documentation": {
499 | "id": 1874,
500 | "nodeType": "StructuredDocumentation",
501 | "src": "702:56:9",
502 | "text": " @dev See {IERC165-supportsInterface}."
503 | },
504 | "functionSelector": "01ffc9a7",
505 | "id": 1890,
506 | "implemented": true,
507 | "kind": "function",
508 | "modifiers": [],
509 | "name": "supportsInterface",
510 | "nameLocation": "772:17:9",
511 | "nodeType": "FunctionDefinition",
512 | "overrides": {
513 | "id": 1878,
514 | "nodeType": "OverrideSpecifier",
515 | "overrides": [],
516 | "src": "830:8:9"
517 | },
518 | "parameters": {
519 | "id": 1877,
520 | "nodeType": "ParameterList",
521 | "parameters": [
522 | {
523 | "constant": false,
524 | "id": 1876,
525 | "mutability": "mutable",
526 | "name": "interfaceId",
527 | "nameLocation": "797:11:9",
528 | "nodeType": "VariableDeclaration",
529 | "scope": 1890,
530 | "src": "790:18:9",
531 | "stateVariable": false,
532 | "storageLocation": "default",
533 | "typeDescriptions": {
534 | "typeIdentifier": "t_bytes4",
535 | "typeString": "bytes4"
536 | },
537 | "typeName": {
538 | "id": 1875,
539 | "name": "bytes4",
540 | "nodeType": "ElementaryTypeName",
541 | "src": "790:6:9",
542 | "typeDescriptions": {
543 | "typeIdentifier": "t_bytes4",
544 | "typeString": "bytes4"
545 | }
546 | },
547 | "visibility": "internal"
548 | }
549 | ],
550 | "src": "789:20:9"
551 | },
552 | "returnParameters": {
553 | "id": 1881,
554 | "nodeType": "ParameterList",
555 | "parameters": [
556 | {
557 | "constant": false,
558 | "id": 1880,
559 | "mutability": "mutable",
560 | "name": "",
561 | "nameLocation": "-1:-1:-1",
562 | "nodeType": "VariableDeclaration",
563 | "scope": 1890,
564 | "src": "848:4:9",
565 | "stateVariable": false,
566 | "storageLocation": "default",
567 | "typeDescriptions": {
568 | "typeIdentifier": "t_bool",
569 | "typeString": "bool"
570 | },
571 | "typeName": {
572 | "id": 1879,
573 | "name": "bool",
574 | "nodeType": "ElementaryTypeName",
575 | "src": "848:4:9",
576 | "typeDescriptions": {
577 | "typeIdentifier": "t_bool",
578 | "typeString": "bool"
579 | }
580 | },
581 | "visibility": "internal"
582 | }
583 | ],
584 | "src": "847:6:9"
585 | },
586 | "scope": 1891,
587 | "src": "763:155:9",
588 | "stateMutability": "view",
589 | "virtual": true,
590 | "visibility": "public"
591 | }
592 | ],
593 | "scope": 1892,
594 | "src": "660:260:9",
595 | "usedErrors": []
596 | }
597 | ],
598 | "src": "33:888:9"
599 | },
600 | "compiler": {
601 | "name": "solc",
602 | "version": "0.8.11+commit.d7f03943.Emscripten.clang"
603 | },
604 | "networks": {},
605 | "schemaVersion": "3.4.4",
606 | "updatedAt": "2022-01-23T15:58:46.605Z",
607 | "devdoc": {
608 | "details": "Implementation of the {IERC165} interface. Contracts that want to implement ERC165 should inherit from this contract and override {supportsInterface} to check for the additional interface id that will be supported. For example: ```solidity function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) { return interfaceId == type(MyInterface).interfaceId || super.supportsInterface(interfaceId); } ``` Alternatively, {ERC165Storage} provides an easier to use but more expensive implementation.",
609 | "kind": "dev",
610 | "methods": {
611 | "supportsInterface(bytes4)": {
612 | "details": "See {IERC165-supportsInterface}."
613 | }
614 | },
615 | "version": 1
616 | },
617 | "userdoc": {
618 | "kind": "user",
619 | "methods": {},
620 | "version": 1
621 | }
622 | }
--------------------------------------------------------------------------------
/src/abis/IERC165.json:
--------------------------------------------------------------------------------
1 | {
2 | "contractName": "IERC165",
3 | "abi": [
4 | {
5 | "inputs": [
6 | {
7 | "internalType": "bytes4",
8 | "name": "interfaceId",
9 | "type": "bytes4"
10 | }
11 | ],
12 | "name": "supportsInterface",
13 | "outputs": [
14 | {
15 | "internalType": "bool",
16 | "name": "",
17 | "type": "bool"
18 | }
19 | ],
20 | "stateMutability": "view",
21 | "type": "function"
22 | }
23 | ],
24 | "metadata": "{\"compiler\":{\"version\":\"0.8.11+commit.d7f03943\"},\"language\":\"Solidity\",\"output\":{\"abi\":[{\"inputs\":[{\"internalType\":\"bytes4\",\"name\":\"interfaceId\",\"type\":\"bytes4\"}],\"name\":\"supportsInterface\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"view\",\"type\":\"function\"}],\"devdoc\":{\"details\":\"Interface of the ERC165 standard, as defined in the https://eips.ethereum.org/EIPS/eip-165[EIP]. Implementers can declare support of contract interfaces, which can then be queried by others ({ERC165Checker}). For an implementation, see {ERC165}.\",\"kind\":\"dev\",\"methods\":{\"supportsInterface(bytes4)\":{\"details\":\"Returns true if this contract implements the interface defined by `interfaceId`. See the corresponding https://eips.ethereum.org/EIPS/eip-165#how-interfaces-are-identified[EIP section] to learn more about how these ids are created. This function call must use less than 30 000 gas.\"}},\"version\":1},\"userdoc\":{\"kind\":\"user\",\"methods\":{},\"version\":1}},\"settings\":{\"compilationTarget\":{\"@openzeppelin/contracts/utils/introspection/IERC165.sol\":\"IERC165\"},\"evmVersion\":\"london\",\"libraries\":{},\"metadata\":{\"bytecodeHash\":\"ipfs\"},\"optimizer\":{\"enabled\":false,\"runs\":200},\"remappings\":[]},\"sources\":{\"@openzeppelin/contracts/utils/introspection/IERC165.sol\":{\"keccak256\":\"0xa28007762d9da9db878dd421960c8cb9a10471f47ab5c1b3309bfe48e9e79ff4\",\"license\":\"MIT\",\"urls\":[\"bzz-raw://796ab6e88af7bf0e78def0f059310c903af6a312b565344e0ff524a0f26e81c6\",\"dweb:/ipfs/QmcsVgLgzWdor3UnAztUkXKNGcysm1MPneWksF72AvnwBx\"]}},\"version\":1}",
25 | "bytecode": "0x",
26 | "deployedBytecode": "0x",
27 | "immutableReferences": {},
28 | "generatedSources": [],
29 | "deployedGeneratedSources": [],
30 | "sourceMap": "",
31 | "deployedSourceMap": "",
32 | "source": "// SPDX-License-Identifier: MIT\n\npragma solidity ^0.8.0;\n\n/**\n * @dev Interface of the ERC165 standard, as defined in the\n * https://eips.ethereum.org/EIPS/eip-165[EIP].\n *\n * Implementers can declare support of contract interfaces, which can then be\n * queried by others ({ERC165Checker}).\n *\n * For an implementation, see {ERC165}.\n */\ninterface IERC165 {\n /**\n * @dev Returns true if this contract implements the interface defined by\n * `interfaceId`. See the corresponding\n * https://eips.ethereum.org/EIPS/eip-165#how-interfaces-are-identified[EIP section]\n * to learn more about how these ids are created.\n *\n * This function call must use less than 30 000 gas.\n */\n function supportsInterface(bytes4 interfaceId) external view returns (bool);\n}\n",
33 | "sourcePath": "@openzeppelin\\contracts\\utils\\introspection\\IERC165.sol",
34 | "ast": {
35 | "absolutePath": "@openzeppelin/contracts/utils/introspection/IERC165.sol",
36 | "exportedSymbols": {
37 | "IERC165": [
38 | 1903
39 | ]
40 | },
41 | "id": 1904,
42 | "license": "MIT",
43 | "nodeType": "SourceUnit",
44 | "nodes": [
45 | {
46 | "id": 1893,
47 | "literals": [
48 | "solidity",
49 | "^",
50 | "0.8",
51 | ".0"
52 | ],
53 | "nodeType": "PragmaDirective",
54 | "src": "33:23:10"
55 | },
56 | {
57 | "abstract": false,
58 | "baseContracts": [],
59 | "canonicalName": "IERC165",
60 | "contractDependencies": [],
61 | "contractKind": "interface",
62 | "documentation": {
63 | "id": 1894,
64 | "nodeType": "StructuredDocumentation",
65 | "src": "58:279:10",
66 | "text": " @dev Interface of the ERC165 standard, as defined in the\n https://eips.ethereum.org/EIPS/eip-165[EIP].\n Implementers can declare support of contract interfaces, which can then be\n queried by others ({ERC165Checker}).\n For an implementation, see {ERC165}."
67 | },
68 | "fullyImplemented": false,
69 | "id": 1903,
70 | "linearizedBaseContracts": [
71 | 1903
72 | ],
73 | "name": "IERC165",
74 | "nameLocation": "348:7:10",
75 | "nodeType": "ContractDefinition",
76 | "nodes": [
77 | {
78 | "documentation": {
79 | "id": 1895,
80 | "nodeType": "StructuredDocumentation",
81 | "src": "362:340:10",
82 | "text": " @dev Returns true if this contract implements the interface defined by\n `interfaceId`. See the corresponding\n https://eips.ethereum.org/EIPS/eip-165#how-interfaces-are-identified[EIP section]\n to learn more about how these ids are created.\n This function call must use less than 30 000 gas."
83 | },
84 | "functionSelector": "01ffc9a7",
85 | "id": 1902,
86 | "implemented": false,
87 | "kind": "function",
88 | "modifiers": [],
89 | "name": "supportsInterface",
90 | "nameLocation": "716:17:10",
91 | "nodeType": "FunctionDefinition",
92 | "parameters": {
93 | "id": 1898,
94 | "nodeType": "ParameterList",
95 | "parameters": [
96 | {
97 | "constant": false,
98 | "id": 1897,
99 | "mutability": "mutable",
100 | "name": "interfaceId",
101 | "nameLocation": "741:11:10",
102 | "nodeType": "VariableDeclaration",
103 | "scope": 1902,
104 | "src": "734:18:10",
105 | "stateVariable": false,
106 | "storageLocation": "default",
107 | "typeDescriptions": {
108 | "typeIdentifier": "t_bytes4",
109 | "typeString": "bytes4"
110 | },
111 | "typeName": {
112 | "id": 1896,
113 | "name": "bytes4",
114 | "nodeType": "ElementaryTypeName",
115 | "src": "734:6:10",
116 | "typeDescriptions": {
117 | "typeIdentifier": "t_bytes4",
118 | "typeString": "bytes4"
119 | }
120 | },
121 | "visibility": "internal"
122 | }
123 | ],
124 | "src": "733:20:10"
125 | },
126 | "returnParameters": {
127 | "id": 1901,
128 | "nodeType": "ParameterList",
129 | "parameters": [
130 | {
131 | "constant": false,
132 | "id": 1900,
133 | "mutability": "mutable",
134 | "name": "",
135 | "nameLocation": "-1:-1:-1",
136 | "nodeType": "VariableDeclaration",
137 | "scope": 1902,
138 | "src": "777:4:10",
139 | "stateVariable": false,
140 | "storageLocation": "default",
141 | "typeDescriptions": {
142 | "typeIdentifier": "t_bool",
143 | "typeString": "bool"
144 | },
145 | "typeName": {
146 | "id": 1899,
147 | "name": "bool",
148 | "nodeType": "ElementaryTypeName",
149 | "src": "777:4:10",
150 | "typeDescriptions": {
151 | "typeIdentifier": "t_bool",
152 | "typeString": "bool"
153 | }
154 | },
155 | "visibility": "internal"
156 | }
157 | ],
158 | "src": "776:6:10"
159 | },
160 | "scope": 1903,
161 | "src": "707:76:10",
162 | "stateMutability": "view",
163 | "virtual": false,
164 | "visibility": "external"
165 | }
166 | ],
167 | "scope": 1904,
168 | "src": "338:447:10",
169 | "usedErrors": []
170 | }
171 | ],
172 | "src": "33:753:10"
173 | },
174 | "legacyAST": {
175 | "absolutePath": "@openzeppelin/contracts/utils/introspection/IERC165.sol",
176 | "exportedSymbols": {
177 | "IERC165": [
178 | 1903
179 | ]
180 | },
181 | "id": 1904,
182 | "license": "MIT",
183 | "nodeType": "SourceUnit",
184 | "nodes": [
185 | {
186 | "id": 1893,
187 | "literals": [
188 | "solidity",
189 | "^",
190 | "0.8",
191 | ".0"
192 | ],
193 | "nodeType": "PragmaDirective",
194 | "src": "33:23:10"
195 | },
196 | {
197 | "abstract": false,
198 | "baseContracts": [],
199 | "canonicalName": "IERC165",
200 | "contractDependencies": [],
201 | "contractKind": "interface",
202 | "documentation": {
203 | "id": 1894,
204 | "nodeType": "StructuredDocumentation",
205 | "src": "58:279:10",
206 | "text": " @dev Interface of the ERC165 standard, as defined in the\n https://eips.ethereum.org/EIPS/eip-165[EIP].\n Implementers can declare support of contract interfaces, which can then be\n queried by others ({ERC165Checker}).\n For an implementation, see {ERC165}."
207 | },
208 | "fullyImplemented": false,
209 | "id": 1903,
210 | "linearizedBaseContracts": [
211 | 1903
212 | ],
213 | "name": "IERC165",
214 | "nameLocation": "348:7:10",
215 | "nodeType": "ContractDefinition",
216 | "nodes": [
217 | {
218 | "documentation": {
219 | "id": 1895,
220 | "nodeType": "StructuredDocumentation",
221 | "src": "362:340:10",
222 | "text": " @dev Returns true if this contract implements the interface defined by\n `interfaceId`. See the corresponding\n https://eips.ethereum.org/EIPS/eip-165#how-interfaces-are-identified[EIP section]\n to learn more about how these ids are created.\n This function call must use less than 30 000 gas."
223 | },
224 | "functionSelector": "01ffc9a7",
225 | "id": 1902,
226 | "implemented": false,
227 | "kind": "function",
228 | "modifiers": [],
229 | "name": "supportsInterface",
230 | "nameLocation": "716:17:10",
231 | "nodeType": "FunctionDefinition",
232 | "parameters": {
233 | "id": 1898,
234 | "nodeType": "ParameterList",
235 | "parameters": [
236 | {
237 | "constant": false,
238 | "id": 1897,
239 | "mutability": "mutable",
240 | "name": "interfaceId",
241 | "nameLocation": "741:11:10",
242 | "nodeType": "VariableDeclaration",
243 | "scope": 1902,
244 | "src": "734:18:10",
245 | "stateVariable": false,
246 | "storageLocation": "default",
247 | "typeDescriptions": {
248 | "typeIdentifier": "t_bytes4",
249 | "typeString": "bytes4"
250 | },
251 | "typeName": {
252 | "id": 1896,
253 | "name": "bytes4",
254 | "nodeType": "ElementaryTypeName",
255 | "src": "734:6:10",
256 | "typeDescriptions": {
257 | "typeIdentifier": "t_bytes4",
258 | "typeString": "bytes4"
259 | }
260 | },
261 | "visibility": "internal"
262 | }
263 | ],
264 | "src": "733:20:10"
265 | },
266 | "returnParameters": {
267 | "id": 1901,
268 | "nodeType": "ParameterList",
269 | "parameters": [
270 | {
271 | "constant": false,
272 | "id": 1900,
273 | "mutability": "mutable",
274 | "name": "",
275 | "nameLocation": "-1:-1:-1",
276 | "nodeType": "VariableDeclaration",
277 | "scope": 1902,
278 | "src": "777:4:10",
279 | "stateVariable": false,
280 | "storageLocation": "default",
281 | "typeDescriptions": {
282 | "typeIdentifier": "t_bool",
283 | "typeString": "bool"
284 | },
285 | "typeName": {
286 | "id": 1899,
287 | "name": "bool",
288 | "nodeType": "ElementaryTypeName",
289 | "src": "777:4:10",
290 | "typeDescriptions": {
291 | "typeIdentifier": "t_bool",
292 | "typeString": "bool"
293 | }
294 | },
295 | "visibility": "internal"
296 | }
297 | ],
298 | "src": "776:6:10"
299 | },
300 | "scope": 1903,
301 | "src": "707:76:10",
302 | "stateMutability": "view",
303 | "virtual": false,
304 | "visibility": "external"
305 | }
306 | ],
307 | "scope": 1904,
308 | "src": "338:447:10",
309 | "usedErrors": []
310 | }
311 | ],
312 | "src": "33:753:10"
313 | },
314 | "compiler": {
315 | "name": "solc",
316 | "version": "0.8.11+commit.d7f03943.Emscripten.clang"
317 | },
318 | "networks": {},
319 | "schemaVersion": "3.4.4",
320 | "updatedAt": "2022-01-23T15:58:46.607Z",
321 | "devdoc": {
322 | "details": "Interface of the ERC165 standard, as defined in the https://eips.ethereum.org/EIPS/eip-165[EIP]. Implementers can declare support of contract interfaces, which can then be queried by others ({ERC165Checker}). For an implementation, see {ERC165}.",
323 | "kind": "dev",
324 | "methods": {
325 | "supportsInterface(bytes4)": {
326 | "details": "Returns true if this contract implements the interface defined by `interfaceId`. See the corresponding https://eips.ethereum.org/EIPS/eip-165#how-interfaces-are-identified[EIP section] to learn more about how these ids are created. This function call must use less than 30 000 gas."
327 | }
328 | },
329 | "version": 1
330 | },
331 | "userdoc": {
332 | "kind": "user",
333 | "methods": {},
334 | "version": 1
335 | }
336 | }
--------------------------------------------------------------------------------
/src/abis/IERC721Metadata.json:
--------------------------------------------------------------------------------
1 | {
2 | "contractName": "IERC721Metadata",
3 | "abi": [
4 | {
5 | "anonymous": false,
6 | "inputs": [
7 | {
8 | "indexed": true,
9 | "internalType": "address",
10 | "name": "owner",
11 | "type": "address"
12 | },
13 | {
14 | "indexed": true,
15 | "internalType": "address",
16 | "name": "approved",
17 | "type": "address"
18 | },
19 | {
20 | "indexed": true,
21 | "internalType": "uint256",
22 | "name": "tokenId",
23 | "type": "uint256"
24 | }
25 | ],
26 | "name": "Approval",
27 | "type": "event"
28 | },
29 | {
30 | "anonymous": false,
31 | "inputs": [
32 | {
33 | "indexed": true,
34 | "internalType": "address",
35 | "name": "owner",
36 | "type": "address"
37 | },
38 | {
39 | "indexed": true,
40 | "internalType": "address",
41 | "name": "operator",
42 | "type": "address"
43 | },
44 | {
45 | "indexed": false,
46 | "internalType": "bool",
47 | "name": "approved",
48 | "type": "bool"
49 | }
50 | ],
51 | "name": "ApprovalForAll",
52 | "type": "event"
53 | },
54 | {
55 | "anonymous": false,
56 | "inputs": [
57 | {
58 | "indexed": true,
59 | "internalType": "address",
60 | "name": "from",
61 | "type": "address"
62 | },
63 | {
64 | "indexed": true,
65 | "internalType": "address",
66 | "name": "to",
67 | "type": "address"
68 | },
69 | {
70 | "indexed": true,
71 | "internalType": "uint256",
72 | "name": "tokenId",
73 | "type": "uint256"
74 | }
75 | ],
76 | "name": "Transfer",
77 | "type": "event"
78 | },
79 | {
80 | "inputs": [
81 | {
82 | "internalType": "address",
83 | "name": "to",
84 | "type": "address"
85 | },
86 | {
87 | "internalType": "uint256",
88 | "name": "tokenId",
89 | "type": "uint256"
90 | }
91 | ],
92 | "name": "approve",
93 | "outputs": [],
94 | "stateMutability": "nonpayable",
95 | "type": "function"
96 | },
97 | {
98 | "inputs": [
99 | {
100 | "internalType": "address",
101 | "name": "owner",
102 | "type": "address"
103 | }
104 | ],
105 | "name": "balanceOf",
106 | "outputs": [
107 | {
108 | "internalType": "uint256",
109 | "name": "balance",
110 | "type": "uint256"
111 | }
112 | ],
113 | "stateMutability": "view",
114 | "type": "function"
115 | },
116 | {
117 | "inputs": [
118 | {
119 | "internalType": "uint256",
120 | "name": "tokenId",
121 | "type": "uint256"
122 | }
123 | ],
124 | "name": "getApproved",
125 | "outputs": [
126 | {
127 | "internalType": "address",
128 | "name": "operator",
129 | "type": "address"
130 | }
131 | ],
132 | "stateMutability": "view",
133 | "type": "function"
134 | },
135 | {
136 | "inputs": [
137 | {
138 | "internalType": "address",
139 | "name": "owner",
140 | "type": "address"
141 | },
142 | {
143 | "internalType": "address",
144 | "name": "operator",
145 | "type": "address"
146 | }
147 | ],
148 | "name": "isApprovedForAll",
149 | "outputs": [
150 | {
151 | "internalType": "bool",
152 | "name": "",
153 | "type": "bool"
154 | }
155 | ],
156 | "stateMutability": "view",
157 | "type": "function"
158 | },
159 | {
160 | "inputs": [
161 | {
162 | "internalType": "uint256",
163 | "name": "tokenId",
164 | "type": "uint256"
165 | }
166 | ],
167 | "name": "ownerOf",
168 | "outputs": [
169 | {
170 | "internalType": "address",
171 | "name": "owner",
172 | "type": "address"
173 | }
174 | ],
175 | "stateMutability": "view",
176 | "type": "function"
177 | },
178 | {
179 | "inputs": [
180 | {
181 | "internalType": "address",
182 | "name": "from",
183 | "type": "address"
184 | },
185 | {
186 | "internalType": "address",
187 | "name": "to",
188 | "type": "address"
189 | },
190 | {
191 | "internalType": "uint256",
192 | "name": "tokenId",
193 | "type": "uint256"
194 | }
195 | ],
196 | "name": "safeTransferFrom",
197 | "outputs": [],
198 | "stateMutability": "nonpayable",
199 | "type": "function"
200 | },
201 | {
202 | "inputs": [
203 | {
204 | "internalType": "address",
205 | "name": "from",
206 | "type": "address"
207 | },
208 | {
209 | "internalType": "address",
210 | "name": "to",
211 | "type": "address"
212 | },
213 | {
214 | "internalType": "uint256",
215 | "name": "tokenId",
216 | "type": "uint256"
217 | },
218 | {
219 | "internalType": "bytes",
220 | "name": "data",
221 | "type": "bytes"
222 | }
223 | ],
224 | "name": "safeTransferFrom",
225 | "outputs": [],
226 | "stateMutability": "nonpayable",
227 | "type": "function"
228 | },
229 | {
230 | "inputs": [
231 | {
232 | "internalType": "address",
233 | "name": "operator",
234 | "type": "address"
235 | },
236 | {
237 | "internalType": "bool",
238 | "name": "_approved",
239 | "type": "bool"
240 | }
241 | ],
242 | "name": "setApprovalForAll",
243 | "outputs": [],
244 | "stateMutability": "nonpayable",
245 | "type": "function"
246 | },
247 | {
248 | "inputs": [
249 | {
250 | "internalType": "bytes4",
251 | "name": "interfaceId",
252 | "type": "bytes4"
253 | }
254 | ],
255 | "name": "supportsInterface",
256 | "outputs": [
257 | {
258 | "internalType": "bool",
259 | "name": "",
260 | "type": "bool"
261 | }
262 | ],
263 | "stateMutability": "view",
264 | "type": "function"
265 | },
266 | {
267 | "inputs": [
268 | {
269 | "internalType": "address",
270 | "name": "from",
271 | "type": "address"
272 | },
273 | {
274 | "internalType": "address",
275 | "name": "to",
276 | "type": "address"
277 | },
278 | {
279 | "internalType": "uint256",
280 | "name": "tokenId",
281 | "type": "uint256"
282 | }
283 | ],
284 | "name": "transferFrom",
285 | "outputs": [],
286 | "stateMutability": "nonpayable",
287 | "type": "function"
288 | },
289 | {
290 | "inputs": [],
291 | "name": "name",
292 | "outputs": [
293 | {
294 | "internalType": "string",
295 | "name": "",
296 | "type": "string"
297 | }
298 | ],
299 | "stateMutability": "view",
300 | "type": "function"
301 | },
302 | {
303 | "inputs": [],
304 | "name": "symbol",
305 | "outputs": [
306 | {
307 | "internalType": "string",
308 | "name": "",
309 | "type": "string"
310 | }
311 | ],
312 | "stateMutability": "view",
313 | "type": "function"
314 | },
315 | {
316 | "inputs": [
317 | {
318 | "internalType": "uint256",
319 | "name": "tokenId",
320 | "type": "uint256"
321 | }
322 | ],
323 | "name": "tokenURI",
324 | "outputs": [
325 | {
326 | "internalType": "string",
327 | "name": "",
328 | "type": "string"
329 | }
330 | ],
331 | "stateMutability": "view",
332 | "type": "function"
333 | }
334 | ],
335 | "metadata": "{\"compiler\":{\"version\":\"0.8.11+commit.d7f03943\"},\"language\":\"Solidity\",\"output\":{\"abi\":[{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"owner\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"approved\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"tokenId\",\"type\":\"uint256\"}],\"name\":\"Approval\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"owner\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"operator\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"bool\",\"name\":\"approved\",\"type\":\"bool\"}],\"name\":\"ApprovalForAll\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"from\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"tokenId\",\"type\":\"uint256\"}],\"name\":\"Transfer\",\"type\":\"event\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"tokenId\",\"type\":\"uint256\"}],\"name\":\"approve\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"owner\",\"type\":\"address\"}],\"name\":\"balanceOf\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"balance\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"tokenId\",\"type\":\"uint256\"}],\"name\":\"getApproved\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"operator\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"owner\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"operator\",\"type\":\"address\"}],\"name\":\"isApprovedForAll\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"name\",\"outputs\":[{\"internalType\":\"string\",\"name\":\"\",\"type\":\"string\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"tokenId\",\"type\":\"uint256\"}],\"name\":\"ownerOf\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"owner\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"from\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"tokenId\",\"type\":\"uint256\"}],\"name\":\"safeTransferFrom\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"from\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"tokenId\",\"type\":\"uint256\"},{\"internalType\":\"bytes\",\"name\":\"data\",\"type\":\"bytes\"}],\"name\":\"safeTransferFrom\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"operator\",\"type\":\"address\"},{\"internalType\":\"bool\",\"name\":\"_approved\",\"type\":\"bool\"}],\"name\":\"setApprovalForAll\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes4\",\"name\":\"interfaceId\",\"type\":\"bytes4\"}],\"name\":\"supportsInterface\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"symbol\",\"outputs\":[{\"internalType\":\"string\",\"name\":\"\",\"type\":\"string\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"tokenId\",\"type\":\"uint256\"}],\"name\":\"tokenURI\",\"outputs\":[{\"internalType\":\"string\",\"name\":\"\",\"type\":\"string\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"from\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"tokenId\",\"type\":\"uint256\"}],\"name\":\"transferFrom\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"}],\"devdoc\":{\"details\":\"See https://eips.ethereum.org/EIPS/eip-721\",\"kind\":\"dev\",\"methods\":{\"approve(address,uint256)\":{\"details\":\"Gives permission to `to` to transfer `tokenId` token to another account. The approval is cleared when the token is transferred. Only a single account can be approved at a time, so approving the zero address clears previous approvals. Requirements: - The caller must own the token or be an approved operator. - `tokenId` must exist. Emits an {Approval} event.\"},\"balanceOf(address)\":{\"details\":\"Returns the number of tokens in ``owner``'s account.\"},\"getApproved(uint256)\":{\"details\":\"Returns the account approved for `tokenId` token. Requirements: - `tokenId` must exist.\"},\"isApprovedForAll(address,address)\":{\"details\":\"Returns if the `operator` is allowed to manage all of the assets of `owner`. See {setApprovalForAll}\"},\"name()\":{\"details\":\"Returns the token collection name.\"},\"ownerOf(uint256)\":{\"details\":\"Returns the owner of the `tokenId` token. Requirements: - `tokenId` must exist.\"},\"safeTransferFrom(address,address,uint256)\":{\"details\":\"Safely transfers `tokenId` token from `from` to `to`, checking first that contract recipients are aware of the ERC721 protocol to prevent tokens from being forever locked. Requirements: - `from` cannot be the zero address. - `to` cannot be the zero address. - `tokenId` token must exist and be owned by `from`. - If the caller is not `from`, it must be have been allowed to move this token by either {approve} or {setApprovalForAll}. - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon a safe transfer. Emits a {Transfer} event.\"},\"safeTransferFrom(address,address,uint256,bytes)\":{\"details\":\"Safely transfers `tokenId` token from `from` to `to`. Requirements: - `from` cannot be the zero address. - `to` cannot be the zero address. - `tokenId` token must exist and be owned by `from`. - If the caller is not `from`, it must be approved to move this token by either {approve} or {setApprovalForAll}. - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon a safe transfer. Emits a {Transfer} event.\"},\"setApprovalForAll(address,bool)\":{\"details\":\"Approve or remove `operator` as an operator for the caller. Operators can call {transferFrom} or {safeTransferFrom} for any token owned by the caller. Requirements: - The `operator` cannot be the caller. Emits an {ApprovalForAll} event.\"},\"supportsInterface(bytes4)\":{\"details\":\"Returns true if this contract implements the interface defined by `interfaceId`. See the corresponding https://eips.ethereum.org/EIPS/eip-165#how-interfaces-are-identified[EIP section] to learn more about how these ids are created. This function call must use less than 30 000 gas.\"},\"symbol()\":{\"details\":\"Returns the token collection symbol.\"},\"tokenURI(uint256)\":{\"details\":\"Returns the Uniform Resource Identifier (URI) for `tokenId` token.\"},\"transferFrom(address,address,uint256)\":{\"details\":\"Transfers `tokenId` token from `from` to `to`. WARNING: Usage of this method is discouraged, use {safeTransferFrom} whenever possible. Requirements: - `from` cannot be the zero address. - `to` cannot be the zero address. - `tokenId` token must be owned by `from`. - If the caller is not `from`, it must be approved to move this token by either {approve} or {setApprovalForAll}. Emits a {Transfer} event.\"}},\"title\":\"ERC-721 Non-Fungible Token Standard, optional metadata extension\",\"version\":1},\"userdoc\":{\"kind\":\"user\",\"methods\":{},\"version\":1}},\"settings\":{\"compilationTarget\":{\"@openzeppelin/contracts/token/ERC721/extensions/IERC721Metadata.sol\":\"IERC721Metadata\"},\"evmVersion\":\"london\",\"libraries\":{},\"metadata\":{\"bytecodeHash\":\"ipfs\"},\"optimizer\":{\"enabled\":false,\"runs\":200},\"remappings\":[]},\"sources\":{\"@openzeppelin/contracts/token/ERC721/IERC721.sol\":{\"keccak256\":\"0xf101e8720213560fab41104d53b3cc7ba0456ef3a98455aa7f022391783144a0\",\"license\":\"MIT\",\"urls\":[\"bzz-raw://3e7820bcf567e6892d937c3cb10db263a4042e446799bca602535868d822384e\",\"dweb:/ipfs/QmPG2oeDjKncqsEeyYGjAN7CwAJmMgHterXGGnpzhha4z7\"]},\"@openzeppelin/contracts/token/ERC721/extensions/IERC721Metadata.sol\":{\"keccak256\":\"0xd32fb7f530a914b1083d10a6bed3a586f2451952fec04fe542bcc670a82f7ba5\",\"license\":\"MIT\",\"urls\":[\"bzz-raw://af63ab940a34687c45f0ad84960b048fc5f49330c92ccb422db7822a444733b9\",\"dweb:/ipfs/QmUShaQEu8HS1GjDnsMJQ8jkZEBrecn6NuDZ3pfjY1gVck\"]},\"@openzeppelin/contracts/utils/introspection/IERC165.sol\":{\"keccak256\":\"0xa28007762d9da9db878dd421960c8cb9a10471f47ab5c1b3309bfe48e9e79ff4\",\"license\":\"MIT\",\"urls\":[\"bzz-raw://796ab6e88af7bf0e78def0f059310c903af6a312b565344e0ff524a0f26e81c6\",\"dweb:/ipfs/QmcsVgLgzWdor3UnAztUkXKNGcysm1MPneWksF72AvnwBx\"]}},\"version\":1}",
336 | "bytecode": "0x",
337 | "deployedBytecode": "0x",
338 | "immutableReferences": {},
339 | "generatedSources": [],
340 | "deployedGeneratedSources": [],
341 | "sourceMap": "",
342 | "deployedSourceMap": "",
343 | "source": "// SPDX-License-Identifier: MIT\n\npragma solidity ^0.8.0;\n\nimport \"../IERC721.sol\";\n\n/**\n * @title ERC-721 Non-Fungible Token Standard, optional metadata extension\n * @dev See https://eips.ethereum.org/EIPS/eip-721\n */\ninterface IERC721Metadata is IERC721 {\n /**\n * @dev Returns the token collection name.\n */\n function name() external view returns (string memory);\n\n /**\n * @dev Returns the token collection symbol.\n */\n function symbol() external view returns (string memory);\n\n /**\n * @dev Returns the Uniform Resource Identifier (URI) for `tokenId` token.\n */\n function tokenURI(uint256 tokenId) external view returns (string memory);\n}\n",
344 | "sourcePath": "@openzeppelin\\contracts\\token\\ERC721\\extensions\\IERC721Metadata.sol",
345 | "ast": {
346 | "absolutePath": "@openzeppelin/contracts/token/ERC721/extensions/IERC721Metadata.sol",
347 | "exportedSymbols": {
348 | "IERC165": [
349 | 1903
350 | ],
351 | "IERC721": [
352 | 933
353 | ],
354 | "IERC721Metadata": [
355 | 1347
356 | ]
357 | },
358 | "id": 1348,
359 | "license": "MIT",
360 | "nodeType": "SourceUnit",
361 | "nodes": [
362 | {
363 | "id": 1322,
364 | "literals": [
365 | "solidity",
366 | "^",
367 | "0.8",
368 | ".0"
369 | ],
370 | "nodeType": "PragmaDirective",
371 | "src": "33:23:5"
372 | },
373 | {
374 | "absolutePath": "@openzeppelin/contracts/token/ERC721/IERC721.sol",
375 | "file": "../IERC721.sol",
376 | "id": 1323,
377 | "nameLocation": "-1:-1:-1",
378 | "nodeType": "ImportDirective",
379 | "scope": 1348,
380 | "sourceUnit": 934,
381 | "src": "58:24:5",
382 | "symbolAliases": [],
383 | "unitAlias": ""
384 | },
385 | {
386 | "abstract": false,
387 | "baseContracts": [
388 | {
389 | "baseName": {
390 | "id": 1325,
391 | "name": "IERC721",
392 | "nodeType": "IdentifierPath",
393 | "referencedDeclaration": 933,
394 | "src": "247:7:5"
395 | },
396 | "id": 1326,
397 | "nodeType": "InheritanceSpecifier",
398 | "src": "247:7:5"
399 | }
400 | ],
401 | "canonicalName": "IERC721Metadata",
402 | "contractDependencies": [],
403 | "contractKind": "interface",
404 | "documentation": {
405 | "id": 1324,
406 | "nodeType": "StructuredDocumentation",
407 | "src": "84:133:5",
408 | "text": " @title ERC-721 Non-Fungible Token Standard, optional metadata extension\n @dev See https://eips.ethereum.org/EIPS/eip-721"
409 | },
410 | "fullyImplemented": false,
411 | "id": 1347,
412 | "linearizedBaseContracts": [
413 | 1347,
414 | 933,
415 | 1903
416 | ],
417 | "name": "IERC721Metadata",
418 | "nameLocation": "228:15:5",
419 | "nodeType": "ContractDefinition",
420 | "nodes": [
421 | {
422 | "documentation": {
423 | "id": 1327,
424 | "nodeType": "StructuredDocumentation",
425 | "src": "261:58:5",
426 | "text": " @dev Returns the token collection name."
427 | },
428 | "functionSelector": "06fdde03",
429 | "id": 1332,
430 | "implemented": false,
431 | "kind": "function",
432 | "modifiers": [],
433 | "name": "name",
434 | "nameLocation": "333:4:5",
435 | "nodeType": "FunctionDefinition",
436 | "parameters": {
437 | "id": 1328,
438 | "nodeType": "ParameterList",
439 | "parameters": [],
440 | "src": "337:2:5"
441 | },
442 | "returnParameters": {
443 | "id": 1331,
444 | "nodeType": "ParameterList",
445 | "parameters": [
446 | {
447 | "constant": false,
448 | "id": 1330,
449 | "mutability": "mutable",
450 | "name": "",
451 | "nameLocation": "-1:-1:-1",
452 | "nodeType": "VariableDeclaration",
453 | "scope": 1332,
454 | "src": "363:13:5",
455 | "stateVariable": false,
456 | "storageLocation": "memory",
457 | "typeDescriptions": {
458 | "typeIdentifier": "t_string_memory_ptr",
459 | "typeString": "string"
460 | },
461 | "typeName": {
462 | "id": 1329,
463 | "name": "string",
464 | "nodeType": "ElementaryTypeName",
465 | "src": "363:6:5",
466 | "typeDescriptions": {
467 | "typeIdentifier": "t_string_storage_ptr",
468 | "typeString": "string"
469 | }
470 | },
471 | "visibility": "internal"
472 | }
473 | ],
474 | "src": "362:15:5"
475 | },
476 | "scope": 1347,
477 | "src": "324:54:5",
478 | "stateMutability": "view",
479 | "virtual": false,
480 | "visibility": "external"
481 | },
482 | {
483 | "documentation": {
484 | "id": 1333,
485 | "nodeType": "StructuredDocumentation",
486 | "src": "384:60:5",
487 | "text": " @dev Returns the token collection symbol."
488 | },
489 | "functionSelector": "95d89b41",
490 | "id": 1338,
491 | "implemented": false,
492 | "kind": "function",
493 | "modifiers": [],
494 | "name": "symbol",
495 | "nameLocation": "458:6:5",
496 | "nodeType": "FunctionDefinition",
497 | "parameters": {
498 | "id": 1334,
499 | "nodeType": "ParameterList",
500 | "parameters": [],
501 | "src": "464:2:5"
502 | },
503 | "returnParameters": {
504 | "id": 1337,
505 | "nodeType": "ParameterList",
506 | "parameters": [
507 | {
508 | "constant": false,
509 | "id": 1336,
510 | "mutability": "mutable",
511 | "name": "",
512 | "nameLocation": "-1:-1:-1",
513 | "nodeType": "VariableDeclaration",
514 | "scope": 1338,
515 | "src": "490:13:5",
516 | "stateVariable": false,
517 | "storageLocation": "memory",
518 | "typeDescriptions": {
519 | "typeIdentifier": "t_string_memory_ptr",
520 | "typeString": "string"
521 | },
522 | "typeName": {
523 | "id": 1335,
524 | "name": "string",
525 | "nodeType": "ElementaryTypeName",
526 | "src": "490:6:5",
527 | "typeDescriptions": {
528 | "typeIdentifier": "t_string_storage_ptr",
529 | "typeString": "string"
530 | }
531 | },
532 | "visibility": "internal"
533 | }
534 | ],
535 | "src": "489:15:5"
536 | },
537 | "scope": 1347,
538 | "src": "449:56:5",
539 | "stateMutability": "view",
540 | "virtual": false,
541 | "visibility": "external"
542 | },
543 | {
544 | "documentation": {
545 | "id": 1339,
546 | "nodeType": "StructuredDocumentation",
547 | "src": "511:90:5",
548 | "text": " @dev Returns the Uniform Resource Identifier (URI) for `tokenId` token."
549 | },
550 | "functionSelector": "c87b56dd",
551 | "id": 1346,
552 | "implemented": false,
553 | "kind": "function",
554 | "modifiers": [],
555 | "name": "tokenURI",
556 | "nameLocation": "615:8:5",
557 | "nodeType": "FunctionDefinition",
558 | "parameters": {
559 | "id": 1342,
560 | "nodeType": "ParameterList",
561 | "parameters": [
562 | {
563 | "constant": false,
564 | "id": 1341,
565 | "mutability": "mutable",
566 | "name": "tokenId",
567 | "nameLocation": "632:7:5",
568 | "nodeType": "VariableDeclaration",
569 | "scope": 1346,
570 | "src": "624:15:5",
571 | "stateVariable": false,
572 | "storageLocation": "default",
573 | "typeDescriptions": {
574 | "typeIdentifier": "t_uint256",
575 | "typeString": "uint256"
576 | },
577 | "typeName": {
578 | "id": 1340,
579 | "name": "uint256",
580 | "nodeType": "ElementaryTypeName",
581 | "src": "624:7:5",
582 | "typeDescriptions": {
583 | "typeIdentifier": "t_uint256",
584 | "typeString": "uint256"
585 | }
586 | },
587 | "visibility": "internal"
588 | }
589 | ],
590 | "src": "623:17:5"
591 | },
592 | "returnParameters": {
593 | "id": 1345,
594 | "nodeType": "ParameterList",
595 | "parameters": [
596 | {
597 | "constant": false,
598 | "id": 1344,
599 | "mutability": "mutable",
600 | "name": "",
601 | "nameLocation": "-1:-1:-1",
602 | "nodeType": "VariableDeclaration",
603 | "scope": 1346,
604 | "src": "664:13:5",
605 | "stateVariable": false,
606 | "storageLocation": "memory",
607 | "typeDescriptions": {
608 | "typeIdentifier": "t_string_memory_ptr",
609 | "typeString": "string"
610 | },
611 | "typeName": {
612 | "id": 1343,
613 | "name": "string",
614 | "nodeType": "ElementaryTypeName",
615 | "src": "664:6:5",
616 | "typeDescriptions": {
617 | "typeIdentifier": "t_string_storage_ptr",
618 | "typeString": "string"
619 | }
620 | },
621 | "visibility": "internal"
622 | }
623 | ],
624 | "src": "663:15:5"
625 | },
626 | "scope": 1347,
627 | "src": "606:73:5",
628 | "stateMutability": "view",
629 | "virtual": false,
630 | "visibility": "external"
631 | }
632 | ],
633 | "scope": 1348,
634 | "src": "218:463:5",
635 | "usedErrors": []
636 | }
637 | ],
638 | "src": "33:649:5"
639 | },
640 | "legacyAST": {
641 | "absolutePath": "@openzeppelin/contracts/token/ERC721/extensions/IERC721Metadata.sol",
642 | "exportedSymbols": {
643 | "IERC165": [
644 | 1903
645 | ],
646 | "IERC721": [
647 | 933
648 | ],
649 | "IERC721Metadata": [
650 | 1347
651 | ]
652 | },
653 | "id": 1348,
654 | "license": "MIT",
655 | "nodeType": "SourceUnit",
656 | "nodes": [
657 | {
658 | "id": 1322,
659 | "literals": [
660 | "solidity",
661 | "^",
662 | "0.8",
663 | ".0"
664 | ],
665 | "nodeType": "PragmaDirective",
666 | "src": "33:23:5"
667 | },
668 | {
669 | "absolutePath": "@openzeppelin/contracts/token/ERC721/IERC721.sol",
670 | "file": "../IERC721.sol",
671 | "id": 1323,
672 | "nameLocation": "-1:-1:-1",
673 | "nodeType": "ImportDirective",
674 | "scope": 1348,
675 | "sourceUnit": 934,
676 | "src": "58:24:5",
677 | "symbolAliases": [],
678 | "unitAlias": ""
679 | },
680 | {
681 | "abstract": false,
682 | "baseContracts": [
683 | {
684 | "baseName": {
685 | "id": 1325,
686 | "name": "IERC721",
687 | "nodeType": "IdentifierPath",
688 | "referencedDeclaration": 933,
689 | "src": "247:7:5"
690 | },
691 | "id": 1326,
692 | "nodeType": "InheritanceSpecifier",
693 | "src": "247:7:5"
694 | }
695 | ],
696 | "canonicalName": "IERC721Metadata",
697 | "contractDependencies": [],
698 | "contractKind": "interface",
699 | "documentation": {
700 | "id": 1324,
701 | "nodeType": "StructuredDocumentation",
702 | "src": "84:133:5",
703 | "text": " @title ERC-721 Non-Fungible Token Standard, optional metadata extension\n @dev See https://eips.ethereum.org/EIPS/eip-721"
704 | },
705 | "fullyImplemented": false,
706 | "id": 1347,
707 | "linearizedBaseContracts": [
708 | 1347,
709 | 933,
710 | 1903
711 | ],
712 | "name": "IERC721Metadata",
713 | "nameLocation": "228:15:5",
714 | "nodeType": "ContractDefinition",
715 | "nodes": [
716 | {
717 | "documentation": {
718 | "id": 1327,
719 | "nodeType": "StructuredDocumentation",
720 | "src": "261:58:5",
721 | "text": " @dev Returns the token collection name."
722 | },
723 | "functionSelector": "06fdde03",
724 | "id": 1332,
725 | "implemented": false,
726 | "kind": "function",
727 | "modifiers": [],
728 | "name": "name",
729 | "nameLocation": "333:4:5",
730 | "nodeType": "FunctionDefinition",
731 | "parameters": {
732 | "id": 1328,
733 | "nodeType": "ParameterList",
734 | "parameters": [],
735 | "src": "337:2:5"
736 | },
737 | "returnParameters": {
738 | "id": 1331,
739 | "nodeType": "ParameterList",
740 | "parameters": [
741 | {
742 | "constant": false,
743 | "id": 1330,
744 | "mutability": "mutable",
745 | "name": "",
746 | "nameLocation": "-1:-1:-1",
747 | "nodeType": "VariableDeclaration",
748 | "scope": 1332,
749 | "src": "363:13:5",
750 | "stateVariable": false,
751 | "storageLocation": "memory",
752 | "typeDescriptions": {
753 | "typeIdentifier": "t_string_memory_ptr",
754 | "typeString": "string"
755 | },
756 | "typeName": {
757 | "id": 1329,
758 | "name": "string",
759 | "nodeType": "ElementaryTypeName",
760 | "src": "363:6:5",
761 | "typeDescriptions": {
762 | "typeIdentifier": "t_string_storage_ptr",
763 | "typeString": "string"
764 | }
765 | },
766 | "visibility": "internal"
767 | }
768 | ],
769 | "src": "362:15:5"
770 | },
771 | "scope": 1347,
772 | "src": "324:54:5",
773 | "stateMutability": "view",
774 | "virtual": false,
775 | "visibility": "external"
776 | },
777 | {
778 | "documentation": {
779 | "id": 1333,
780 | "nodeType": "StructuredDocumentation",
781 | "src": "384:60:5",
782 | "text": " @dev Returns the token collection symbol."
783 | },
784 | "functionSelector": "95d89b41",
785 | "id": 1338,
786 | "implemented": false,
787 | "kind": "function",
788 | "modifiers": [],
789 | "name": "symbol",
790 | "nameLocation": "458:6:5",
791 | "nodeType": "FunctionDefinition",
792 | "parameters": {
793 | "id": 1334,
794 | "nodeType": "ParameterList",
795 | "parameters": [],
796 | "src": "464:2:5"
797 | },
798 | "returnParameters": {
799 | "id": 1337,
800 | "nodeType": "ParameterList",
801 | "parameters": [
802 | {
803 | "constant": false,
804 | "id": 1336,
805 | "mutability": "mutable",
806 | "name": "",
807 | "nameLocation": "-1:-1:-1",
808 | "nodeType": "VariableDeclaration",
809 | "scope": 1338,
810 | "src": "490:13:5",
811 | "stateVariable": false,
812 | "storageLocation": "memory",
813 | "typeDescriptions": {
814 | "typeIdentifier": "t_string_memory_ptr",
815 | "typeString": "string"
816 | },
817 | "typeName": {
818 | "id": 1335,
819 | "name": "string",
820 | "nodeType": "ElementaryTypeName",
821 | "src": "490:6:5",
822 | "typeDescriptions": {
823 | "typeIdentifier": "t_string_storage_ptr",
824 | "typeString": "string"
825 | }
826 | },
827 | "visibility": "internal"
828 | }
829 | ],
830 | "src": "489:15:5"
831 | },
832 | "scope": 1347,
833 | "src": "449:56:5",
834 | "stateMutability": "view",
835 | "virtual": false,
836 | "visibility": "external"
837 | },
838 | {
839 | "documentation": {
840 | "id": 1339,
841 | "nodeType": "StructuredDocumentation",
842 | "src": "511:90:5",
843 | "text": " @dev Returns the Uniform Resource Identifier (URI) for `tokenId` token."
844 | },
845 | "functionSelector": "c87b56dd",
846 | "id": 1346,
847 | "implemented": false,
848 | "kind": "function",
849 | "modifiers": [],
850 | "name": "tokenURI",
851 | "nameLocation": "615:8:5",
852 | "nodeType": "FunctionDefinition",
853 | "parameters": {
854 | "id": 1342,
855 | "nodeType": "ParameterList",
856 | "parameters": [
857 | {
858 | "constant": false,
859 | "id": 1341,
860 | "mutability": "mutable",
861 | "name": "tokenId",
862 | "nameLocation": "632:7:5",
863 | "nodeType": "VariableDeclaration",
864 | "scope": 1346,
865 | "src": "624:15:5",
866 | "stateVariable": false,
867 | "storageLocation": "default",
868 | "typeDescriptions": {
869 | "typeIdentifier": "t_uint256",
870 | "typeString": "uint256"
871 | },
872 | "typeName": {
873 | "id": 1340,
874 | "name": "uint256",
875 | "nodeType": "ElementaryTypeName",
876 | "src": "624:7:5",
877 | "typeDescriptions": {
878 | "typeIdentifier": "t_uint256",
879 | "typeString": "uint256"
880 | }
881 | },
882 | "visibility": "internal"
883 | }
884 | ],
885 | "src": "623:17:5"
886 | },
887 | "returnParameters": {
888 | "id": 1345,
889 | "nodeType": "ParameterList",
890 | "parameters": [
891 | {
892 | "constant": false,
893 | "id": 1344,
894 | "mutability": "mutable",
895 | "name": "",
896 | "nameLocation": "-1:-1:-1",
897 | "nodeType": "VariableDeclaration",
898 | "scope": 1346,
899 | "src": "664:13:5",
900 | "stateVariable": false,
901 | "storageLocation": "memory",
902 | "typeDescriptions": {
903 | "typeIdentifier": "t_string_memory_ptr",
904 | "typeString": "string"
905 | },
906 | "typeName": {
907 | "id": 1343,
908 | "name": "string",
909 | "nodeType": "ElementaryTypeName",
910 | "src": "664:6:5",
911 | "typeDescriptions": {
912 | "typeIdentifier": "t_string_storage_ptr",
913 | "typeString": "string"
914 | }
915 | },
916 | "visibility": "internal"
917 | }
918 | ],
919 | "src": "663:15:5"
920 | },
921 | "scope": 1347,
922 | "src": "606:73:5",
923 | "stateMutability": "view",
924 | "virtual": false,
925 | "visibility": "external"
926 | }
927 | ],
928 | "scope": 1348,
929 | "src": "218:463:5",
930 | "usedErrors": []
931 | }
932 | ],
933 | "src": "33:649:5"
934 | },
935 | "compiler": {
936 | "name": "solc",
937 | "version": "0.8.11+commit.d7f03943.Emscripten.clang"
938 | },
939 | "networks": {},
940 | "schemaVersion": "3.4.4",
941 | "updatedAt": "2022-01-23T15:58:46.582Z",
942 | "devdoc": {
943 | "details": "See https://eips.ethereum.org/EIPS/eip-721",
944 | "kind": "dev",
945 | "methods": {
946 | "approve(address,uint256)": {
947 | "details": "Gives permission to `to` to transfer `tokenId` token to another account. The approval is cleared when the token is transferred. Only a single account can be approved at a time, so approving the zero address clears previous approvals. Requirements: - The caller must own the token or be an approved operator. - `tokenId` must exist. Emits an {Approval} event."
948 | },
949 | "balanceOf(address)": {
950 | "details": "Returns the number of tokens in ``owner``'s account."
951 | },
952 | "getApproved(uint256)": {
953 | "details": "Returns the account approved for `tokenId` token. Requirements: - `tokenId` must exist."
954 | },
955 | "isApprovedForAll(address,address)": {
956 | "details": "Returns if the `operator` is allowed to manage all of the assets of `owner`. See {setApprovalForAll}"
957 | },
958 | "name()": {
959 | "details": "Returns the token collection name."
960 | },
961 | "ownerOf(uint256)": {
962 | "details": "Returns the owner of the `tokenId` token. Requirements: - `tokenId` must exist."
963 | },
964 | "safeTransferFrom(address,address,uint256)": {
965 | "details": "Safely transfers `tokenId` token from `from` to `to`, checking first that contract recipients are aware of the ERC721 protocol to prevent tokens from being forever locked. Requirements: - `from` cannot be the zero address. - `to` cannot be the zero address. - `tokenId` token must exist and be owned by `from`. - If the caller is not `from`, it must be have been allowed to move this token by either {approve} or {setApprovalForAll}. - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon a safe transfer. Emits a {Transfer} event."
966 | },
967 | "safeTransferFrom(address,address,uint256,bytes)": {
968 | "details": "Safely transfers `tokenId` token from `from` to `to`. Requirements: - `from` cannot be the zero address. - `to` cannot be the zero address. - `tokenId` token must exist and be owned by `from`. - If the caller is not `from`, it must be approved to move this token by either {approve} or {setApprovalForAll}. - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon a safe transfer. Emits a {Transfer} event."
969 | },
970 | "setApprovalForAll(address,bool)": {
971 | "details": "Approve or remove `operator` as an operator for the caller. Operators can call {transferFrom} or {safeTransferFrom} for any token owned by the caller. Requirements: - The `operator` cannot be the caller. Emits an {ApprovalForAll} event."
972 | },
973 | "supportsInterface(bytes4)": {
974 | "details": "Returns true if this contract implements the interface defined by `interfaceId`. See the corresponding https://eips.ethereum.org/EIPS/eip-165#how-interfaces-are-identified[EIP section] to learn more about how these ids are created. This function call must use less than 30 000 gas."
975 | },
976 | "symbol()": {
977 | "details": "Returns the token collection symbol."
978 | },
979 | "tokenURI(uint256)": {
980 | "details": "Returns the Uniform Resource Identifier (URI) for `tokenId` token."
981 | },
982 | "transferFrom(address,address,uint256)": {
983 | "details": "Transfers `tokenId` token from `from` to `to`. WARNING: Usage of this method is discouraged, use {safeTransferFrom} whenever possible. Requirements: - `from` cannot be the zero address. - `to` cannot be the zero address. - `tokenId` token must be owned by `from`. - If the caller is not `from`, it must be approved to move this token by either {approve} or {setApprovalForAll}. Emits a {Transfer} event."
984 | }
985 | },
986 | "title": "ERC-721 Non-Fungible Token Standard, optional metadata extension",
987 | "version": 1
988 | },
989 | "userdoc": {
990 | "kind": "user",
991 | "methods": {},
992 | "version": 1
993 | }
994 | }
--------------------------------------------------------------------------------
/src/abis/IERC721Receiver.json:
--------------------------------------------------------------------------------
1 | {
2 | "contractName": "IERC721Receiver",
3 | "abi": [
4 | {
5 | "inputs": [
6 | {
7 | "internalType": "address",
8 | "name": "operator",
9 | "type": "address"
10 | },
11 | {
12 | "internalType": "address",
13 | "name": "from",
14 | "type": "address"
15 | },
16 | {
17 | "internalType": "uint256",
18 | "name": "tokenId",
19 | "type": "uint256"
20 | },
21 | {
22 | "internalType": "bytes",
23 | "name": "data",
24 | "type": "bytes"
25 | }
26 | ],
27 | "name": "onERC721Received",
28 | "outputs": [
29 | {
30 | "internalType": "bytes4",
31 | "name": "",
32 | "type": "bytes4"
33 | }
34 | ],
35 | "stateMutability": "nonpayable",
36 | "type": "function"
37 | }
38 | ],
39 | "metadata": "{\"compiler\":{\"version\":\"0.8.11+commit.d7f03943\"},\"language\":\"Solidity\",\"output\":{\"abi\":[{\"inputs\":[{\"internalType\":\"address\",\"name\":\"operator\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"from\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"tokenId\",\"type\":\"uint256\"},{\"internalType\":\"bytes\",\"name\":\"data\",\"type\":\"bytes\"}],\"name\":\"onERC721Received\",\"outputs\":[{\"internalType\":\"bytes4\",\"name\":\"\",\"type\":\"bytes4\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"}],\"devdoc\":{\"details\":\"Interface for any contract that wants to support safeTransfers from ERC721 asset contracts.\",\"kind\":\"dev\",\"methods\":{\"onERC721Received(address,address,uint256,bytes)\":{\"details\":\"Whenever an {IERC721} `tokenId` token is transferred to this contract via {IERC721-safeTransferFrom} by `operator` from `from`, this function is called. It must return its Solidity selector to confirm the token transfer. If any other value is returned or the interface is not implemented by the recipient, the transfer will be reverted. The selector can be obtained in Solidity with `IERC721.onERC721Received.selector`.\"}},\"title\":\"ERC721 token receiver interface\",\"version\":1},\"userdoc\":{\"kind\":\"user\",\"methods\":{},\"version\":1}},\"settings\":{\"compilationTarget\":{\"@openzeppelin/contracts/token/ERC721/IERC721Receiver.sol\":\"IERC721Receiver\"},\"evmVersion\":\"london\",\"libraries\":{},\"metadata\":{\"bytecodeHash\":\"ipfs\"},\"optimizer\":{\"enabled\":false,\"runs\":200},\"remappings\":[]},\"sources\":{\"@openzeppelin/contracts/token/ERC721/IERC721Receiver.sol\":{\"keccak256\":\"0xd9517254724276e2e8de3769183c1f738f445f0095c26fd9b86d3c6687e887b9\",\"license\":\"MIT\",\"urls\":[\"bzz-raw://0e604bcdcd5e5b2fb299ad09769cde6db19d5aa1929d1b5e939234a0f10d7eb8\",\"dweb:/ipfs/Qmd8hXE3GZfBHuWx3RNiYgFW2ci7KvHtib8DiwzJ2dgo9V\"]}},\"version\":1}",
40 | "bytecode": "0x",
41 | "deployedBytecode": "0x",
42 | "immutableReferences": {},
43 | "generatedSources": [],
44 | "deployedGeneratedSources": [],
45 | "sourceMap": "",
46 | "deployedSourceMap": "",
47 | "source": "// SPDX-License-Identifier: MIT\n\npragma solidity ^0.8.0;\n\n/**\n * @title ERC721 token receiver interface\n * @dev Interface for any contract that wants to support safeTransfers\n * from ERC721 asset contracts.\n */\ninterface IERC721Receiver {\n /**\n * @dev Whenever an {IERC721} `tokenId` token is transferred to this contract via {IERC721-safeTransferFrom}\n * by `operator` from `from`, this function is called.\n *\n * It must return its Solidity selector to confirm the token transfer.\n * If any other value is returned or the interface is not implemented by the recipient, the transfer will be reverted.\n *\n * The selector can be obtained in Solidity with `IERC721.onERC721Received.selector`.\n */\n function onERC721Received(\n address operator,\n address from,\n uint256 tokenId,\n bytes calldata data\n ) external returns (bytes4);\n}\n",
48 | "sourcePath": "@openzeppelin\\contracts\\token\\ERC721\\IERC721Receiver.sol",
49 | "ast": {
50 | "absolutePath": "@openzeppelin/contracts/token/ERC721/IERC721Receiver.sol",
51 | "exportedSymbols": {
52 | "IERC721Receiver": [
53 | 951
54 | ]
55 | },
56 | "id": 952,
57 | "license": "MIT",
58 | "nodeType": "SourceUnit",
59 | "nodes": [
60 | {
61 | "id": 935,
62 | "literals": [
63 | "solidity",
64 | "^",
65 | "0.8",
66 | ".0"
67 | ],
68 | "nodeType": "PragmaDirective",
69 | "src": "33:23:2"
70 | },
71 | {
72 | "abstract": false,
73 | "baseContracts": [],
74 | "canonicalName": "IERC721Receiver",
75 | "contractDependencies": [],
76 | "contractKind": "interface",
77 | "documentation": {
78 | "id": 936,
79 | "nodeType": "StructuredDocumentation",
80 | "src": "58:152:2",
81 | "text": " @title ERC721 token receiver interface\n @dev Interface for any contract that wants to support safeTransfers\n from ERC721 asset contracts."
82 | },
83 | "fullyImplemented": false,
84 | "id": 951,
85 | "linearizedBaseContracts": [
86 | 951
87 | ],
88 | "name": "IERC721Receiver",
89 | "nameLocation": "221:15:2",
90 | "nodeType": "ContractDefinition",
91 | "nodes": [
92 | {
93 | "documentation": {
94 | "id": 937,
95 | "nodeType": "StructuredDocumentation",
96 | "src": "243:485:2",
97 | "text": " @dev Whenever an {IERC721} `tokenId` token is transferred to this contract via {IERC721-safeTransferFrom}\n by `operator` from `from`, this function is called.\n It must return its Solidity selector to confirm the token transfer.\n If any other value is returned or the interface is not implemented by the recipient, the transfer will be reverted.\n The selector can be obtained in Solidity with `IERC721.onERC721Received.selector`."
98 | },
99 | "functionSelector": "150b7a02",
100 | "id": 950,
101 | "implemented": false,
102 | "kind": "function",
103 | "modifiers": [],
104 | "name": "onERC721Received",
105 | "nameLocation": "742:16:2",
106 | "nodeType": "FunctionDefinition",
107 | "parameters": {
108 | "id": 946,
109 | "nodeType": "ParameterList",
110 | "parameters": [
111 | {
112 | "constant": false,
113 | "id": 939,
114 | "mutability": "mutable",
115 | "name": "operator",
116 | "nameLocation": "776:8:2",
117 | "nodeType": "VariableDeclaration",
118 | "scope": 950,
119 | "src": "768:16:2",
120 | "stateVariable": false,
121 | "storageLocation": "default",
122 | "typeDescriptions": {
123 | "typeIdentifier": "t_address",
124 | "typeString": "address"
125 | },
126 | "typeName": {
127 | "id": 938,
128 | "name": "address",
129 | "nodeType": "ElementaryTypeName",
130 | "src": "768:7:2",
131 | "stateMutability": "nonpayable",
132 | "typeDescriptions": {
133 | "typeIdentifier": "t_address",
134 | "typeString": "address"
135 | }
136 | },
137 | "visibility": "internal"
138 | },
139 | {
140 | "constant": false,
141 | "id": 941,
142 | "mutability": "mutable",
143 | "name": "from",
144 | "nameLocation": "802:4:2",
145 | "nodeType": "VariableDeclaration",
146 | "scope": 950,
147 | "src": "794:12:2",
148 | "stateVariable": false,
149 | "storageLocation": "default",
150 | "typeDescriptions": {
151 | "typeIdentifier": "t_address",
152 | "typeString": "address"
153 | },
154 | "typeName": {
155 | "id": 940,
156 | "name": "address",
157 | "nodeType": "ElementaryTypeName",
158 | "src": "794:7:2",
159 | "stateMutability": "nonpayable",
160 | "typeDescriptions": {
161 | "typeIdentifier": "t_address",
162 | "typeString": "address"
163 | }
164 | },
165 | "visibility": "internal"
166 | },
167 | {
168 | "constant": false,
169 | "id": 943,
170 | "mutability": "mutable",
171 | "name": "tokenId",
172 | "nameLocation": "824:7:2",
173 | "nodeType": "VariableDeclaration",
174 | "scope": 950,
175 | "src": "816:15:2",
176 | "stateVariable": false,
177 | "storageLocation": "default",
178 | "typeDescriptions": {
179 | "typeIdentifier": "t_uint256",
180 | "typeString": "uint256"
181 | },
182 | "typeName": {
183 | "id": 942,
184 | "name": "uint256",
185 | "nodeType": "ElementaryTypeName",
186 | "src": "816:7:2",
187 | "typeDescriptions": {
188 | "typeIdentifier": "t_uint256",
189 | "typeString": "uint256"
190 | }
191 | },
192 | "visibility": "internal"
193 | },
194 | {
195 | "constant": false,
196 | "id": 945,
197 | "mutability": "mutable",
198 | "name": "data",
199 | "nameLocation": "856:4:2",
200 | "nodeType": "VariableDeclaration",
201 | "scope": 950,
202 | "src": "841:19:2",
203 | "stateVariable": false,
204 | "storageLocation": "calldata",
205 | "typeDescriptions": {
206 | "typeIdentifier": "t_bytes_calldata_ptr",
207 | "typeString": "bytes"
208 | },
209 | "typeName": {
210 | "id": 944,
211 | "name": "bytes",
212 | "nodeType": "ElementaryTypeName",
213 | "src": "841:5:2",
214 | "typeDescriptions": {
215 | "typeIdentifier": "t_bytes_storage_ptr",
216 | "typeString": "bytes"
217 | }
218 | },
219 | "visibility": "internal"
220 | }
221 | ],
222 | "src": "758:108:2"
223 | },
224 | "returnParameters": {
225 | "id": 949,
226 | "nodeType": "ParameterList",
227 | "parameters": [
228 | {
229 | "constant": false,
230 | "id": 948,
231 | "mutability": "mutable",
232 | "name": "",
233 | "nameLocation": "-1:-1:-1",
234 | "nodeType": "VariableDeclaration",
235 | "scope": 950,
236 | "src": "885:6:2",
237 | "stateVariable": false,
238 | "storageLocation": "default",
239 | "typeDescriptions": {
240 | "typeIdentifier": "t_bytes4",
241 | "typeString": "bytes4"
242 | },
243 | "typeName": {
244 | "id": 947,
245 | "name": "bytes4",
246 | "nodeType": "ElementaryTypeName",
247 | "src": "885:6:2",
248 | "typeDescriptions": {
249 | "typeIdentifier": "t_bytes4",
250 | "typeString": "bytes4"
251 | }
252 | },
253 | "visibility": "internal"
254 | }
255 | ],
256 | "src": "884:8:2"
257 | },
258 | "scope": 951,
259 | "src": "733:160:2",
260 | "stateMutability": "nonpayable",
261 | "virtual": false,
262 | "visibility": "external"
263 | }
264 | ],
265 | "scope": 952,
266 | "src": "211:684:2",
267 | "usedErrors": []
268 | }
269 | ],
270 | "src": "33:863:2"
271 | },
272 | "legacyAST": {
273 | "absolutePath": "@openzeppelin/contracts/token/ERC721/IERC721Receiver.sol",
274 | "exportedSymbols": {
275 | "IERC721Receiver": [
276 | 951
277 | ]
278 | },
279 | "id": 952,
280 | "license": "MIT",
281 | "nodeType": "SourceUnit",
282 | "nodes": [
283 | {
284 | "id": 935,
285 | "literals": [
286 | "solidity",
287 | "^",
288 | "0.8",
289 | ".0"
290 | ],
291 | "nodeType": "PragmaDirective",
292 | "src": "33:23:2"
293 | },
294 | {
295 | "abstract": false,
296 | "baseContracts": [],
297 | "canonicalName": "IERC721Receiver",
298 | "contractDependencies": [],
299 | "contractKind": "interface",
300 | "documentation": {
301 | "id": 936,
302 | "nodeType": "StructuredDocumentation",
303 | "src": "58:152:2",
304 | "text": " @title ERC721 token receiver interface\n @dev Interface for any contract that wants to support safeTransfers\n from ERC721 asset contracts."
305 | },
306 | "fullyImplemented": false,
307 | "id": 951,
308 | "linearizedBaseContracts": [
309 | 951
310 | ],
311 | "name": "IERC721Receiver",
312 | "nameLocation": "221:15:2",
313 | "nodeType": "ContractDefinition",
314 | "nodes": [
315 | {
316 | "documentation": {
317 | "id": 937,
318 | "nodeType": "StructuredDocumentation",
319 | "src": "243:485:2",
320 | "text": " @dev Whenever an {IERC721} `tokenId` token is transferred to this contract via {IERC721-safeTransferFrom}\n by `operator` from `from`, this function is called.\n It must return its Solidity selector to confirm the token transfer.\n If any other value is returned or the interface is not implemented by the recipient, the transfer will be reverted.\n The selector can be obtained in Solidity with `IERC721.onERC721Received.selector`."
321 | },
322 | "functionSelector": "150b7a02",
323 | "id": 950,
324 | "implemented": false,
325 | "kind": "function",
326 | "modifiers": [],
327 | "name": "onERC721Received",
328 | "nameLocation": "742:16:2",
329 | "nodeType": "FunctionDefinition",
330 | "parameters": {
331 | "id": 946,
332 | "nodeType": "ParameterList",
333 | "parameters": [
334 | {
335 | "constant": false,
336 | "id": 939,
337 | "mutability": "mutable",
338 | "name": "operator",
339 | "nameLocation": "776:8:2",
340 | "nodeType": "VariableDeclaration",
341 | "scope": 950,
342 | "src": "768:16:2",
343 | "stateVariable": false,
344 | "storageLocation": "default",
345 | "typeDescriptions": {
346 | "typeIdentifier": "t_address",
347 | "typeString": "address"
348 | },
349 | "typeName": {
350 | "id": 938,
351 | "name": "address",
352 | "nodeType": "ElementaryTypeName",
353 | "src": "768:7:2",
354 | "stateMutability": "nonpayable",
355 | "typeDescriptions": {
356 | "typeIdentifier": "t_address",
357 | "typeString": "address"
358 | }
359 | },
360 | "visibility": "internal"
361 | },
362 | {
363 | "constant": false,
364 | "id": 941,
365 | "mutability": "mutable",
366 | "name": "from",
367 | "nameLocation": "802:4:2",
368 | "nodeType": "VariableDeclaration",
369 | "scope": 950,
370 | "src": "794:12:2",
371 | "stateVariable": false,
372 | "storageLocation": "default",
373 | "typeDescriptions": {
374 | "typeIdentifier": "t_address",
375 | "typeString": "address"
376 | },
377 | "typeName": {
378 | "id": 940,
379 | "name": "address",
380 | "nodeType": "ElementaryTypeName",
381 | "src": "794:7:2",
382 | "stateMutability": "nonpayable",
383 | "typeDescriptions": {
384 | "typeIdentifier": "t_address",
385 | "typeString": "address"
386 | }
387 | },
388 | "visibility": "internal"
389 | },
390 | {
391 | "constant": false,
392 | "id": 943,
393 | "mutability": "mutable",
394 | "name": "tokenId",
395 | "nameLocation": "824:7:2",
396 | "nodeType": "VariableDeclaration",
397 | "scope": 950,
398 | "src": "816:15:2",
399 | "stateVariable": false,
400 | "storageLocation": "default",
401 | "typeDescriptions": {
402 | "typeIdentifier": "t_uint256",
403 | "typeString": "uint256"
404 | },
405 | "typeName": {
406 | "id": 942,
407 | "name": "uint256",
408 | "nodeType": "ElementaryTypeName",
409 | "src": "816:7:2",
410 | "typeDescriptions": {
411 | "typeIdentifier": "t_uint256",
412 | "typeString": "uint256"
413 | }
414 | },
415 | "visibility": "internal"
416 | },
417 | {
418 | "constant": false,
419 | "id": 945,
420 | "mutability": "mutable",
421 | "name": "data",
422 | "nameLocation": "856:4:2",
423 | "nodeType": "VariableDeclaration",
424 | "scope": 950,
425 | "src": "841:19:2",
426 | "stateVariable": false,
427 | "storageLocation": "calldata",
428 | "typeDescriptions": {
429 | "typeIdentifier": "t_bytes_calldata_ptr",
430 | "typeString": "bytes"
431 | },
432 | "typeName": {
433 | "id": 944,
434 | "name": "bytes",
435 | "nodeType": "ElementaryTypeName",
436 | "src": "841:5:2",
437 | "typeDescriptions": {
438 | "typeIdentifier": "t_bytes_storage_ptr",
439 | "typeString": "bytes"
440 | }
441 | },
442 | "visibility": "internal"
443 | }
444 | ],
445 | "src": "758:108:2"
446 | },
447 | "returnParameters": {
448 | "id": 949,
449 | "nodeType": "ParameterList",
450 | "parameters": [
451 | {
452 | "constant": false,
453 | "id": 948,
454 | "mutability": "mutable",
455 | "name": "",
456 | "nameLocation": "-1:-1:-1",
457 | "nodeType": "VariableDeclaration",
458 | "scope": 950,
459 | "src": "885:6:2",
460 | "stateVariable": false,
461 | "storageLocation": "default",
462 | "typeDescriptions": {
463 | "typeIdentifier": "t_bytes4",
464 | "typeString": "bytes4"
465 | },
466 | "typeName": {
467 | "id": 947,
468 | "name": "bytes4",
469 | "nodeType": "ElementaryTypeName",
470 | "src": "885:6:2",
471 | "typeDescriptions": {
472 | "typeIdentifier": "t_bytes4",
473 | "typeString": "bytes4"
474 | }
475 | },
476 | "visibility": "internal"
477 | }
478 | ],
479 | "src": "884:8:2"
480 | },
481 | "scope": 951,
482 | "src": "733:160:2",
483 | "stateMutability": "nonpayable",
484 | "virtual": false,
485 | "visibility": "external"
486 | }
487 | ],
488 | "scope": 952,
489 | "src": "211:684:2",
490 | "usedErrors": []
491 | }
492 | ],
493 | "src": "33:863:2"
494 | },
495 | "compiler": {
496 | "name": "solc",
497 | "version": "0.8.11+commit.d7f03943.Emscripten.clang"
498 | },
499 | "networks": {},
500 | "schemaVersion": "3.4.4",
501 | "updatedAt": "2022-01-23T15:58:46.565Z",
502 | "devdoc": {
503 | "details": "Interface for any contract that wants to support safeTransfers from ERC721 asset contracts.",
504 | "kind": "dev",
505 | "methods": {
506 | "onERC721Received(address,address,uint256,bytes)": {
507 | "details": "Whenever an {IERC721} `tokenId` token is transferred to this contract via {IERC721-safeTransferFrom} by `operator` from `from`, this function is called. It must return its Solidity selector to confirm the token transfer. If any other value is returned or the interface is not implemented by the recipient, the transfer will be reverted. The selector can be obtained in Solidity with `IERC721.onERC721Received.selector`."
508 | }
509 | },
510 | "title": "ERC721 token receiver interface",
511 | "version": 1
512 | },
513 | "userdoc": {
514 | "kind": "user",
515 | "methods": {},
516 | "version": 1
517 | }
518 | }
--------------------------------------------------------------------------------
/src/components/Content/Main.js:
--------------------------------------------------------------------------------
1 | import { useContext } from 'react';
2 |
3 | import MintForm from './MintNFT/MintForm';
4 | import NFTCollection from './NFTCollection/NFTCollection';
5 | import CollectionContext from '../../store/collection-context';
6 | import MarketplaceContext from '../../store/marketplace-context';
7 | import Spinner from '../Layout/Spinner';
8 | import logo from '../../img/logo2.PNG'
9 |
10 | const Main = () => {
11 | const collectionCtx = useContext(CollectionContext);
12 | const marketplaceCtx = useContext(MarketplaceContext);
13 |
14 | return(
15 |
16 |
17 |
18 |
19 |
20 | {!collectionCtx.nftIsLoading &&
}
21 | {collectionCtx.nftIsLoading &&
}
22 |
23 |
24 |
25 |
26 | {!marketplaceCtx.mktIsLoading &&
}
27 | {marketplaceCtx.mktIsLoading &&
}
28 |
29 | );
30 | };
31 |
32 | export default Main;
--------------------------------------------------------------------------------
/src/components/Content/MintNFT/MintForm.js:
--------------------------------------------------------------------------------
1 | import { useState, useContext } from 'react';
2 |
3 | import Web3Context from '../../../store/web3-context';
4 | import CollectionContext from '../../../store/collection-context';
5 |
6 | const ipfsClient = require('ipfs-http-client');
7 | const ipfs = ipfsClient.create({ host: 'ipfs.infura.io', port: 5001, protocol: 'https' });
8 |
9 | const MintForm = () => {
10 | const [enteredName, setEnteredName] = useState('');
11 | const [descriptionIsValid, setDescriptionIsValid] = useState(true);
12 |
13 | const [enteredDescription, setEnteredDescription] = useState('');
14 | const [nameIsValid, setNameIsValid] = useState(true);
15 |
16 | const [capturedFileBuffer, setCapturedFileBuffer] = useState(null);
17 | const [fileIsValid, setFileIsValid] = useState(true);
18 |
19 | const web3Ctx = useContext(Web3Context);
20 | const collectionCtx = useContext(CollectionContext);
21 |
22 | const enteredNameHandler = (event) => {
23 | setEnteredName(event.target.value);
24 | };
25 |
26 | const enteredDescriptionHandler = (event) => {
27 | setEnteredDescription(event.target.value);
28 | };
29 |
30 | const captureFile = (event) => {
31 | event.preventDefault();
32 |
33 | const file = event.target.files[0];
34 |
35 | const reader = new window.FileReader();
36 | reader.readAsArrayBuffer(file);
37 | reader.onloadend = () => {
38 | setCapturedFileBuffer(Buffer(reader.result));
39 | }
40 | };
41 |
42 | const submissionHandler = (event) => {
43 | event.preventDefault();
44 |
45 | enteredName ? setNameIsValid(true) : setNameIsValid(false);
46 | enteredDescription ? setDescriptionIsValid(true) : setDescriptionIsValid(false);
47 | capturedFileBuffer ? setFileIsValid(true) : setFileIsValid(false);
48 |
49 | const formIsValid = enteredName && enteredDescription && capturedFileBuffer;
50 | console.log(formIsValid);
51 | // Upload file to IPFS and push to the blockchain
52 | const mintNFT = async () => {
53 | // Add file to the IPFS
54 | const fileAdded = await ipfs.add(capturedFileBuffer);
55 | if (!fileAdded) {
56 | console.error('Something went wrong when updloading the file');
57 | return;
58 | }
59 |
60 | const metadata = {
61 | title: "Asset Metadata",
62 | type: "object",
63 | properties: {
64 | name: {
65 | type: "string",
66 | description: enteredName
67 | },
68 | description: {
69 | type: "string",
70 | description: enteredDescription
71 | },
72 | image: {
73 | type: "string",
74 | description: fileAdded.path
75 | }
76 | }
77 | };
78 |
79 | const metadataAdded = await ipfs.add(JSON.stringify(metadata));
80 | console.log('meta:', metadataAdded);
81 | if (!metadataAdded) {
82 | console.error('Something went wrong when updloading the file');
83 | return;
84 | }
85 |
86 | collectionCtx.contract.methods.safeMint(metadataAdded.path).send({ from: web3Ctx.account })
87 | .on('transactionHash', (hash) => {
88 | collectionCtx.setNftIsLoading(true);
89 | })
90 | .on('error', (e) => {
91 | window.alert('Something went wrong when pushing to the blockchain');
92 | collectionCtx.setNftIsLoading(false);
93 | })
94 | };
95 |
96 | formIsValid && mintNFT();
97 | };
98 |
99 | const nameClass = nameIsValid ? "form-control" : "form-control is-invalid";
100 | const descriptionClass = descriptionIsValid ? "form-control" : "form-control is-invalid";
101 | const fileClass = fileIsValid ? "form-control" : "form-control is-invalid";
102 |
103 | return (
104 |
134 | );
135 | };
136 |
137 | export default MintForm;
--------------------------------------------------------------------------------
/src/components/Content/NFTCollection/NFTCollection.js:
--------------------------------------------------------------------------------
1 | import { useContext, useRef, createRef } from 'react';
2 |
3 | import web3 from '../../../connection/web3';
4 | import Web3Context from '../../../store/web3-context';
5 | import CollectionContext from '../../../store/collection-context';
6 | import MarketplaceContext from '../../../store/marketplace-context';
7 | import { formatPrice } from '../../../helpers/utils';
8 | import eth from '../../../img/eth.png';
9 |
10 | const NFTCollection = () => {
11 | const web3Ctx = useContext(Web3Context);
12 | const collectionCtx = useContext(CollectionContext);
13 | const marketplaceCtx = useContext(MarketplaceContext);
14 |
15 | const priceRefs = useRef([]);
16 | if (priceRefs.current.length !== collectionCtx.collection.length) {
17 | priceRefs.current = Array(collectionCtx.collection.length).fill().map((_, i) => priceRefs.current[i] || createRef());
18 | }
19 |
20 | const makeOfferHandler = (event, id, key) => {
21 | event.preventDefault();
22 |
23 | const enteredPrice = web3.utils.toWei(priceRefs.current[key].current.value, 'ether');
24 |
25 | collectionCtx.contract.methods.approve(marketplaceCtx.contract.options.address, id).send({ from: web3Ctx.account })
26 | .on('transactionHash', (hash) => {
27 | marketplaceCtx.setMktIsLoading(true);
28 | })
29 | .on('receipt', (receipt) => {
30 | marketplaceCtx.contract.methods.makeOffer(id, enteredPrice).send({ from: web3Ctx.account })
31 | .on('error', (error) => {
32 | window.alert('Something went wrong when pushing to the blockchain');
33 | marketplaceCtx.setMktIsLoading(false);
34 | });
35 | });
36 | };
37 |
38 | const buyHandler = (event) => {
39 | const buyIndex = parseInt(event.target.value);
40 | marketplaceCtx.contract.methods.fillOffer(marketplaceCtx.offers[buyIndex].offerId).send({ from: web3Ctx.account, value: marketplaceCtx.offers[buyIndex].price })
41 | .on('transactionHash', (hash) => {
42 | marketplaceCtx.setMktIsLoading(true);
43 | })
44 | .on('error', (error) => {
45 | window.alert('Something went wrong when pushing to the blockchain');
46 | marketplaceCtx.setMktIsLoading(false);
47 | });
48 | };
49 |
50 | const cancelHandler = (event) => {
51 | const cancelIndex = parseInt(event.target.value);
52 | marketplaceCtx.contract.methods.cancelOffer(marketplaceCtx.offers[cancelIndex].offerId).send({ from: web3Ctx.account })
53 | .on('transactionHash', (hash) => {
54 | marketplaceCtx.setMktIsLoading(true);
55 | })
56 | .on('error', (error) => {
57 | window.alert('Something went wrong when pushing to the blockchain');
58 | marketplaceCtx.setMktIsLoading(false);
59 | });
60 | };
61 |
62 | return(
63 |
64 | {collectionCtx.collection.map((NFT, key) => {
65 | const index = marketplaceCtx.offers ? marketplaceCtx.offers.findIndex(offer => offer.id === NFT.id) : -1;
66 | const owner = index === -1 ? NFT.owner : marketplaceCtx.offers[index].user;
67 | const price = index !== -1 ? formatPrice(marketplaceCtx.offers[index].price).toFixed(2) : null;
68 |
69 | return(
70 |
71 |
72 |
{NFT.title}
73 |
74 |
75 |
{`${owner.substr(0,7)}...${owner.substr(owner.length - 7)}`}
76 | {index !== -1 ?
77 | owner !== web3Ctx.account ?
78 |
79 |
80 | BUY
81 |
82 |
83 |
84 |
{`${price}`}
85 |
86 |
:
87 |
88 |
89 | CANCEL
90 |
91 |
92 |
93 |
{`${price}`}
94 |
95 |
:
96 | owner === web3Ctx.account ?
97 |
:
111 |
}
112 |
113 | );
114 | })}
115 |
116 | );
117 | };
118 |
119 | export default NFTCollection;
--------------------------------------------------------------------------------
/src/components/Layout/Navbar.js:
--------------------------------------------------------------------------------
1 | import { useContext, useState } from 'react';
2 |
3 | import Web3Context from '../../store/web3-context';
4 | import MarketplaceContext from '../../store/marketplace-context';
5 | import web3 from '../../connection/web3';
6 | import { formatPrice } from '../../helpers/utils';
7 |
8 | const Navbar = () => {
9 | const [fundsLoading, setFundsLoading] = useState(false);
10 |
11 | const web3Ctx = useContext(Web3Context);
12 | const marketplaceCtx = useContext(MarketplaceContext);
13 |
14 | const connectWalletHandler = async() => {
15 | try {
16 | // Request account access
17 | await window.ethereum.request({ method: 'eth_requestAccounts' });
18 | } catch(error) {
19 | console.error(error);
20 | }
21 |
22 | // Load accounts
23 | web3Ctx.loadAccount(web3);
24 | };
25 |
26 | const claimFundsHandler = () => {
27 | marketplaceCtx.contract.methods.claimFunds().send({ from: web3Ctx.account })
28 | .on('transactionHash', (hash) => {
29 | setFundsLoading(true);
30 | })
31 | .on('error', (error) => {
32 | window.alert('Something went wrong when pushing to the blockchain');
33 | setFundsLoading(false);
34 | });
35 | };
36 |
37 | // Event ClaimFunds subscription
38 | marketplaceCtx.contract.events.ClaimFunds()
39 | .on('data', (event) => {
40 | marketplaceCtx.loadUserFunds(marketplaceCtx.contract, web3Ctx.account);
41 | setFundsLoading(false);
42 | })
43 | .on('error', (error) => {
44 | console.log(error);
45 | });
46 |
47 | let etherscanUrl;
48 |
49 | if(web3Ctx.networkId === 3) {
50 | etherscanUrl = 'https://ropsten.etherscan.io'
51 | } else if(web3Ctx.networkId === 4) {
52 | etherscanUrl = 'https://rinkeby.etherscan.io'
53 | } else if(web3Ctx.networkId === 5) {
54 | etherscanUrl = 'https://goerli.etherscan.io'
55 | } else {
56 | etherscanUrl = 'https://etherscan.io'
57 | }
58 |
59 | return (
60 |
61 |
62 |
63 | {marketplaceCtx.userFunds > 0 && !fundsLoading &&
64 |
69 | {`CLAIM ${formatPrice(marketplaceCtx.userFunds)} ETH`}
70 | }
71 | {fundsLoading &&
72 | }
77 |
78 |
79 | {web3Ctx.account &&
80 |
86 | {web3Ctx.account}
87 | }
88 | {!web3Ctx.account &&
89 |
94 | Connect your wallet
95 | }
96 |
97 |
98 |
99 | );
100 | };
101 |
102 | export default Navbar;
--------------------------------------------------------------------------------
/src/components/Layout/Spinner.js:
--------------------------------------------------------------------------------
1 | const Spinner = () => {
2 | return(
3 |
6 | );
7 | };
8 |
9 | export default Spinner;
--------------------------------------------------------------------------------
/src/connection/web3.js:
--------------------------------------------------------------------------------
1 | import Web3 from 'web3';
2 |
3 | // Web 3 connection
4 | const web3 = window.ethereum ? new Web3(window.ethereum) : null;
5 |
6 | export default web3;
--------------------------------------------------------------------------------
/src/contracts/Migrations.sol:
--------------------------------------------------------------------------------
1 | // SPDX-License-Identifier: MIT
2 | pragma solidity >=0.4.22 <0.9.0;
3 |
4 | contract Migrations {
5 | address public owner = msg.sender;
6 | uint public last_completed_migration;
7 |
8 | modifier restricted() {
9 | require(
10 | msg.sender == owner,
11 | "This function is restricted to the contract's owner"
12 | );
13 | _;
14 | }
15 |
16 | function setCompleted(uint completed) public restricted {
17 | last_completed_migration = completed;
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/src/contracts/NFTCollection.sol:
--------------------------------------------------------------------------------
1 | // SPDX-License-Identifier: MIT
2 | pragma solidity ^0.8.0;
3 |
4 | import "@openzeppelin/contracts/token/ERC721/ERC721.sol";
5 | import "@openzeppelin/contracts/token/ERC721/extensions/ERC721Enumerable.sol";
6 |
7 | contract NFTCollection is ERC721, ERC721Enumerable {
8 | string[] public tokenURIs;
9 | mapping(string => bool) _tokenURIExists;
10 | mapping(uint => string) _tokenIdToTokenURI;
11 |
12 | constructor()
13 | ERC721("link Collection", "link")
14 | {
15 | }
16 |
17 | function _beforeTokenTransfer(address from, address to, uint256 tokenId) internal override(ERC721, ERC721Enumerable) {
18 | super._beforeTokenTransfer(from, to, tokenId);
19 | }
20 |
21 | function supportsInterface(bytes4 interfaceId) public view override(ERC721, ERC721Enumerable) returns (bool) {
22 | return super.supportsInterface(interfaceId);
23 | }
24 |
25 | function tokenURI(uint256 tokenId) public override view returns (string memory) {
26 | require(_exists(tokenId), 'ERC721Metadata: URI query for nonexistent token');
27 | return _tokenIdToTokenURI[tokenId];
28 | }
29 |
30 | function safeMint(string memory _tokenURI) public {
31 | require(!_tokenURIExists[_tokenURI], 'The token URI should be unique');
32 | tokenURIs.push(_tokenURI);
33 | uint _id = tokenURIs.length;
34 | _tokenIdToTokenURI[_id] = _tokenURI;
35 | _safeMint(msg.sender, _id);
36 | _tokenURIExists[_tokenURI] = true;
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/src/contracts/NFTMarketplace.sol:
--------------------------------------------------------------------------------
1 | // SPDX-License-Identifier: MIT
2 | pragma solidity ^0.8.0;
3 |
4 | import "./NFTCollection.sol";
5 |
6 | contract NFTMarketplace {
7 | uint public offerCount;
8 | mapping (uint => _Offer) public offers;
9 | mapping (address => uint) public userFunds;
10 | NFTCollection nftCollection;
11 |
12 | struct _Offer {
13 | uint offerId;
14 | uint id;
15 | address user;
16 | uint price;
17 | bool fulfilled;
18 | bool cancelled;
19 | }
20 |
21 | event Offer(
22 | uint offerId,
23 | uint id,
24 | address user,
25 | uint price,
26 | bool fulfilled,
27 | bool cancelled
28 | );
29 |
30 | event OfferFilled(uint offerId, uint id, address newOwner);
31 | event OfferCancelled(uint offerId, uint id, address owner);
32 | event ClaimFunds(address user, uint amount);
33 |
34 | constructor(address _nftCollection) {
35 | nftCollection = NFTCollection(_nftCollection);
36 | }
37 |
38 | function makeOffer(uint _id, uint _price) public {
39 | nftCollection.transferFrom(msg.sender, address(this), _id);
40 | offerCount ++;
41 | offers[offerCount] = _Offer(offerCount, _id, msg.sender, _price, false, false);
42 | emit Offer(offerCount, _id, msg.sender, _price, false, false);
43 | }
44 |
45 | function fillOffer(uint _offerId) public payable {
46 | _Offer storage _offer = offers[_offerId];
47 | require(_offer.offerId == _offerId, 'The offer must exist');
48 | require(_offer.user != msg.sender, 'The owner of the offer cannot fill it');
49 | require(!_offer.fulfilled, 'An offer cannot be fulfilled twice');
50 | require(!_offer.cancelled, 'A cancelled offer cannot be fulfilled');
51 | require(msg.value == _offer.price, 'The ETH amount should match with the NFT Price');
52 | nftCollection.transferFrom(address(this), msg.sender, _offer.id);
53 | _offer.fulfilled = true;
54 | userFunds[_offer.user] += msg.value;
55 | emit OfferFilled(_offerId, _offer.id, msg.sender);
56 | }
57 |
58 | function cancelOffer(uint _offerId) public {
59 | _Offer storage _offer = offers[_offerId];
60 | require(_offer.offerId == _offerId, 'The offer must exist');
61 | require(_offer.user == msg.sender, 'The offer can only be canceled by the owner');
62 | require(_offer.fulfilled == false, 'A fulfilled offer cannot be cancelled');
63 | require(_offer.cancelled == false, 'An offer cannot be cancelled twice');
64 | nftCollection.transferFrom(address(this), msg.sender, _offer.id);
65 | _offer.cancelled = true;
66 | emit OfferCancelled(_offerId, _offer.id, msg.sender);
67 | }
68 |
69 | function claimFunds() public {
70 | require(userFunds[msg.sender] > 0, 'This user has no funds to be claimed');
71 | payable(msg.sender).transfer(userFunds[msg.sender]);
72 | emit ClaimFunds(msg.sender, userFunds[msg.sender]);
73 | userFunds[msg.sender] = 0;
74 | }
75 |
76 | // Fallback: reverts if Ether is sent to this smart-contract by mistake
77 | fallback () external {
78 | revert();
79 | }
80 | }
--------------------------------------------------------------------------------
/src/helpers/utils.js:
--------------------------------------------------------------------------------
1 | export const DECIMALS = (10**18);
2 |
3 | export const ether = wei => wei / DECIMALS;
4 |
5 | export const formatPrice = (price) => {
6 | const precision = 100; // Use 2 decimal places
7 |
8 | price = ether(price);
9 | price = Math.round(price * precision) / precision;
10 |
11 | return price;
12 | };
--------------------------------------------------------------------------------
/src/img/eth.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/devmad119/NFTProject/49e65174c550f4a612e4a14c1c0646c7b820a258/src/img/eth.png
--------------------------------------------------------------------------------
/src/img/logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/devmad119/NFTProject/49e65174c550f4a612e4a14c1c0646c7b820a258/src/img/logo.png
--------------------------------------------------------------------------------
/src/img/logo2.PNG:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/devmad119/NFTProject/49e65174c550f4a612e4a14c1c0646c7b820a258/src/img/logo2.PNG
--------------------------------------------------------------------------------
/src/index.js:
--------------------------------------------------------------------------------
1 | import ReactDOM from 'react-dom';
2 | import 'bootstrap/dist/css/bootstrap.css';
3 |
4 | import Web3Provider from './store/Web3Provider';
5 | import CollectionProvider from './store/CollectionProvider';
6 | import MarketplaceProvider from './store/MarketplaceProvider';
7 | import App from './App';
8 |
9 | ReactDOM.render(
10 |
11 |
12 |
13 |
14 |
15 |
16 | ,
17 | document.getElementById('root')
18 | );
--------------------------------------------------------------------------------
/src/store/CollectionProvider.js:
--------------------------------------------------------------------------------
1 | import { useReducer } from 'react';
2 |
3 | import CollectionContext from './collection-context';
4 |
5 | const defaultCollectionState = {
6 | contract: null,
7 | totalSupply: null,
8 | collection: [],
9 | nftIsLoading: true
10 | };
11 |
12 | const collectionReducer = (state, action) => {
13 | if(action.type === 'CONTRACT') {
14 | return {
15 | contract: action.contract,
16 | totalSupply: state.totalSupply,
17 | collection: state.collection,
18 | nftIsLoading: state.nftIsLoading
19 | };
20 | }
21 |
22 | if(action.type === 'LOADSUPPLY') {
23 | return {
24 | contract: state.contract,
25 | totalSupply: action.totalSupply,
26 | collection: state.collection,
27 | nftIsLoading: state.nftIsLoading
28 | };
29 | }
30 |
31 | if(action.type === 'LOADCOLLECTION') {
32 | return {
33 | contract: state.contract,
34 | totalSupply: state.totalSupply,
35 | collection: action.collection,
36 | nftIsLoading: state.nftIsLoading
37 | };
38 | }
39 |
40 | if(action.type === 'UPDATECOLLECTION') {
41 | const index = state.collection.findIndex(NFT => NFT.id === parseInt(action.NFT.id));
42 | let collection = [];
43 |
44 | if(index === -1) {
45 | collection = [action.NFT, ...state.collection];
46 | } else {
47 | collection = [...state.collection];
48 | }
49 |
50 | return {
51 | contract: state.contract,
52 | totalSupply: state.totalSupply,
53 | collection: collection,
54 | nftIsLoading: state.nftIsLoading
55 | };
56 | }
57 |
58 | if(action.type === 'UPDATEOWNER') {
59 | const index = state.collection.findIndex(NFT => NFT.id === parseInt(action.id));
60 | let collection = [...state.collection];
61 | collection[index].owner = action.newOwner;
62 |
63 | return {
64 | contract: state.contract,
65 | totalSupply: state.totalSupply,
66 | collection: collection,
67 | nftIsLoading: state.nftIsLoading
68 | };
69 | }
70 |
71 | if(action.type === 'LOADING') {
72 | return {
73 | contract: state.contract,
74 | totalSupply: state.totalSupply,
75 | collection: state.collection,
76 | nftIsLoading: action.loading
77 | };
78 | }
79 |
80 | return defaultCollectionState;
81 | };
82 |
83 | const CollectionProvider = props => {
84 | const [CollectionState, dispatchCollectionAction] = useReducer(collectionReducer, defaultCollectionState);
85 |
86 | const loadContractHandler = (web3, NFTCollection, deployedNetwork) => {
87 | const contract = deployedNetwork ? new web3.eth.Contract(NFTCollection.abi, deployedNetwork.address): '';
88 | dispatchCollectionAction({type: 'CONTRACT', contract: contract});
89 | return contract;
90 | };
91 |
92 | const loadTotalSupplyHandler = async(contract) => {
93 | const totalSupply = await contract.methods.totalSupply().call();
94 | dispatchCollectionAction({type: 'LOADSUPPLY', totalSupply: totalSupply});
95 | return totalSupply;
96 | };
97 |
98 | const loadCollectionHandler = async(contract, totalSupply) => {
99 | let collection = [];
100 |
101 | for(let i = 0; i < totalSupply; i++) {
102 | const hash = await contract.methods.tokenURIs(i).call();
103 | console.log(hash);
104 | try {
105 | const response = await fetch(`https://ipfs.infura.io/ipfs/${hash}?clear`);
106 | if(!response.ok) {
107 | throw new Error('Something went wrong');
108 | }
109 |
110 | const metadata = await response.json();
111 | const owner = await contract.methods.ownerOf(i + 1).call();
112 |
113 | collection = [{
114 | id: i + 1,
115 | title: metadata.properties.name.description,
116 | img: metadata.properties.image.description,
117 | owner: owner
118 | }, ...collection];
119 | }catch {
120 | console.error('Something went wrong');
121 | }
122 | }
123 | dispatchCollectionAction({type: 'LOADCOLLECTION', collection: collection});
124 | };
125 |
126 | const updateCollectionHandler = async(contract, id, owner) => {
127 | let NFT;
128 | const hash = await contract.methods.tokenURI(id).call();
129 | try {
130 | const response = await fetch(`https://ipfs.infura.io/ipfs/${hash}?clear`);
131 | if(!response.ok) {
132 | throw new Error('Something went wrong'); }
133 |
134 | const metadata = await response.json();
135 |
136 | NFT = {
137 | id: parseInt(id),
138 | title: metadata.properties.name.description,
139 | img: metadata.properties.image.description,
140 | owner: owner
141 | };
142 | }catch {
143 | console.error('Something went wrong');
144 | }
145 | dispatchCollectionAction({type: 'UPDATECOLLECTION', NFT: NFT});
146 | };
147 |
148 | const updateOwnerHandler = (id, newOwner) => {
149 | dispatchCollectionAction({type: 'UPDATEOWNER', id: id, newOwner: newOwner});
150 | };
151 |
152 | const setNftIsLoadingHandler = (loading) => {
153 | dispatchCollectionAction({type: 'LOADING', loading: loading});
154 | };
155 |
156 | const collectionContext = {
157 | contract: CollectionState.contract,
158 | totalSupply: CollectionState.totalSupply,
159 | collection: CollectionState.collection,
160 | nftIsLoading:CollectionState.nftIsLoading,
161 | loadContract: loadContractHandler,
162 | loadTotalSupply: loadTotalSupplyHandler,
163 | loadCollection: loadCollectionHandler,
164 | updateCollection: updateCollectionHandler,
165 | updateOwner: updateOwnerHandler,
166 | setNftIsLoading: setNftIsLoadingHandler
167 | };
168 |
169 | return (
170 |
171 | {props.children}
172 |
173 | );
174 | };
175 |
176 | export default CollectionProvider;
--------------------------------------------------------------------------------
/src/store/MarketplaceProvider.js:
--------------------------------------------------------------------------------
1 | import { useReducer } from 'react';
2 |
3 | import MarketplaceContext from './marketplace-context';
4 |
5 | const defaultMarketplaceState = {
6 | contract: null,
7 | offerCount: null,
8 | offers: [],
9 | userFunds: null,
10 | mktIsLoading: true
11 | };
12 |
13 | const marketplaceReducer = (state, action) => {
14 | if(action.type === 'CONTRACT') {
15 | return {
16 | contract: action.contract,
17 | offerCount: state.offerCount,
18 | offers: state.offers,
19 | userFunds: state.userFunds,
20 | mktIsLoading: state.mktIsLoading
21 | };
22 | }
23 |
24 | if(action.type === 'LOADOFFERCOUNT') {
25 | return {
26 | contract: state.contract,
27 | offerCount: action.offerCount,
28 | offers: state.offers,
29 | userFunds: state.userFunds,
30 | mktIsLoading: state.mktIsLoading
31 | };
32 | }
33 |
34 | if(action.type === 'LOADOFFERS') {
35 | return {
36 | contract: state.contract,
37 | offerCount: state.offerCount,
38 | offers: action.offers,
39 | userFunds: state.userFunds,
40 | mktIsLoading: state.mktIsLoading
41 | };
42 | }
43 |
44 | if(action.type === 'UPDATEOFFER') {
45 | const offers = state.offers.filter(offer => offer.offerId !== parseInt(action.offerId));
46 |
47 | return {
48 | contract: state.contract,
49 | offerCount: state.offerCount,
50 | offers: offers,
51 | userFunds: state.userFunds,
52 | mktIsLoading: state.mktIsLoading
53 | };
54 | }
55 |
56 | if(action.type === 'ADDOFFER') {
57 | const index = state.offers.findIndex(offer => offer.offerId === parseInt(action.offer.offerId));
58 | let offers = [];
59 |
60 | if(index === -1) {
61 | offers = [...state.offers, {
62 | offerId: parseInt(action.offer.offerId),
63 | id: parseInt(action.offer.id),
64 | user: (action.offer.user),
65 | price: parseInt(action.offer.price),
66 | fulfilled: false,
67 | cancelled: false
68 | }];
69 | } else {
70 | offers = [...state.offers];
71 | }
72 |
73 | return {
74 | contract: state.contract,
75 | offerCount: state.offerCount,
76 | offers: offers,
77 | userFunds: state.userFunds,
78 | mktIsLoading: state.mktIsLoading
79 | };
80 | }
81 |
82 | if(action.type === 'LOADFUNDS') {
83 | return {
84 | contract: state.contract,
85 | offerCount: state.offerCount,
86 | offers: state.offers,
87 | userFunds: action.userFunds,
88 | mktIsLoading: state.mktIsLoading
89 | };
90 | }
91 |
92 | if(action.type === 'LOADING') {
93 | return {
94 | contract: state.contract,
95 | offerCount: state.offerCount,
96 | offers: state.offers,
97 | userFunds: state.userFunds,
98 | mktIsLoading: action.loading
99 | };
100 | }
101 |
102 | return defaultMarketplaceState;
103 | };
104 |
105 | const MarketplaceProvider = props => {
106 | const [MarketplaceState, dispatchMarketplaceAction] = useReducer(marketplaceReducer, defaultMarketplaceState);
107 |
108 | const loadContractHandler = (web3, NFTMarketplace, deployedNetwork) => {
109 | const contract = deployedNetwork ? new web3.eth.Contract(NFTMarketplace.abi, deployedNetwork.address): '';
110 | dispatchMarketplaceAction({type: 'CONTRACT', contract: contract});
111 | return contract;
112 | };
113 |
114 | const loadOfferCountHandler = async(contract) => {
115 | const offerCount = await contract.methods.offerCount().call();
116 | dispatchMarketplaceAction({type: 'LOADOFFERCOUNT', offerCount: offerCount});
117 | return offerCount;
118 | };
119 |
120 | const loadOffersHandler = async(contract, offerCount) => {
121 | let offers = [];
122 | for(let i = 0; i < offerCount; i++) {
123 | const offer = await contract.methods.offers(i + 1).call();
124 | offers.push(offer);
125 | }
126 | offers = offers
127 | .map(offer => {
128 | offer.offerId = parseInt(offer.offerId);
129 | offer.id = parseInt(offer.id);
130 | offer.price = parseInt(offer.price);
131 | return offer;
132 | })
133 | .filter(offer => offer.fulfilled === false && offer.cancelled === false);
134 | dispatchMarketplaceAction({type: 'LOADOFFERS', offers: offers});
135 | };
136 |
137 | const updateOfferHandler = (offerId) => {
138 | dispatchMarketplaceAction({type: 'UPDATEOFFER', offerId: offerId});
139 | };
140 |
141 | const addOfferHandler = (offer) => {
142 | dispatchMarketplaceAction({type: 'ADDOFFER', offer: offer});
143 | };
144 |
145 | const loadUserFundsHandler = async(contract, account) => {
146 | const userFunds = await contract.methods.userFunds(account).call();
147 | dispatchMarketplaceAction({type: 'LOADFUNDS', userFunds: userFunds});
148 | return userFunds;
149 | };
150 |
151 | const setMktIsLoadingHandler = (loading) => {
152 | dispatchMarketplaceAction({type: 'LOADING', loading: loading});
153 | };
154 |
155 | const marketplaceContext = {
156 | contract: MarketplaceState.contract,
157 | offerCount: MarketplaceState.offerCount,
158 | offers: MarketplaceState.offers,
159 | userFunds: MarketplaceState.userFunds,
160 | mktIsLoading: MarketplaceState.mktIsLoading,
161 | loadContract: loadContractHandler,
162 | loadOfferCount: loadOfferCountHandler,
163 | loadOffers: loadOffersHandler,
164 | updateOffer: updateOfferHandler,
165 | addOffer: addOfferHandler,
166 | loadUserFunds: loadUserFundsHandler,
167 | setMktIsLoading: setMktIsLoadingHandler
168 | };
169 |
170 | return (
171 |
172 | {props.children}
173 |
174 | );
175 | };
176 |
177 | export default MarketplaceProvider;
--------------------------------------------------------------------------------
/src/store/Web3Provider.js:
--------------------------------------------------------------------------------
1 | import { useReducer } from 'react';
2 |
3 | import Web3Context from './web3-context';
4 |
5 | const defaultWeb3State = {
6 | account: null,
7 | networkId: null
8 | };
9 |
10 | const web3Reducer = (state, action) => {
11 | if(action.type === 'ACCOUNT') {
12 | return {
13 | account: action.account,
14 | networkId: state.networkId
15 | };
16 | }
17 |
18 | if(action.type === 'NETWORKID') {
19 | return {
20 | account: state.account,
21 | networkId: action.networkId
22 | };
23 | }
24 |
25 | return defaultWeb3State;
26 | };
27 |
28 | const Web3Provider = props => {
29 | const [web3State, dispatchWeb3Action] = useReducer(web3Reducer, defaultWeb3State);
30 |
31 | const loadAccountHandler = async(web3) => {
32 | const accounts = await web3.eth.getAccounts();
33 | const account = accounts[0];
34 | dispatchWeb3Action({type: 'ACCOUNT', account: account});
35 | return account;
36 | };
37 |
38 | const loadNetworkIdHandler = async(web3) => {
39 | const networkId = await web3.eth.net.getId();
40 | dispatchWeb3Action({type: 'NETWORKID', networkId: networkId});
41 | return networkId;
42 | };
43 |
44 | const web3Context = {
45 | account:web3State.account,
46 | networkId: web3State.networkId,
47 | loadAccount: loadAccountHandler,
48 | loadNetworkId: loadNetworkIdHandler
49 | };
50 |
51 | return (
52 |
53 | {props.children}
54 |
55 | );
56 | };
57 |
58 | export default Web3Provider;
--------------------------------------------------------------------------------
/src/store/collection-context.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 |
3 | const CollectionContext = React.createContext({
4 | contract: null,
5 | totalSupply: null,
6 | collection: [],
7 | nftIsLoading: true,
8 | loadContract: () => { },
9 | loadTotalSupply: () => { },
10 | loadCollection: () => { },
11 | updateTotalSupply: () => { },
12 | updateCollection: () => { },
13 | updateOwner: () => { },
14 | setNftIsLoading: () => { }
15 | });
16 |
17 | export default CollectionContext;
--------------------------------------------------------------------------------
/src/store/marketplace-context.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 |
3 | const MarketplaceContext = React.createContext({
4 | contract: null,
5 | offerCount: null,
6 | offers: [],
7 | userFunds: null,
8 | mktIsLoading: true,
9 | loadContract: () => {},
10 | loadOfferCount: () => {},
11 | loadOffers: () => {},
12 | updateOffer: () => {},
13 | addOffer: () => {},
14 | loadUserFunds: () => {},
15 | setMktIsLoading: () => {}
16 | });
17 |
18 | export default MarketplaceContext;
--------------------------------------------------------------------------------
/src/store/web3-context.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 |
3 | const Web3Context = React.createContext({
4 | account: null,
5 | networkId: null,
6 | loadAccount: () => {},
7 | loadNetworkId: () => {}
8 | });
9 |
10 | export default Web3Context;
--------------------------------------------------------------------------------
/test/NFTCollection.test.js:
--------------------------------------------------------------------------------
1 | const { expectRevert } = require('@openzeppelin/test-helpers');
2 |
3 | const NFTCollection = artifacts.require('./NFTCollection.sol');
4 |
5 | contract('NFTCollection', (accounts) => {
6 | let contract;
7 |
8 | before(async () => {
9 | contract = await NFTCollection.new();
10 | });
11 |
12 | describe('deployment', () => {
13 | it('deploys successfully', async () => {
14 | const address = contract.address;
15 | assert.notEqual(address, 0x0);
16 | assert.notEqual(address, '');
17 | assert.notEqual(address, null);
18 | assert.notEqual(address, undefined);
19 | });
20 |
21 | it('has a name', async() => {
22 | const name = await contract.name();
23 | assert.equal(name, 'mTC Collection');
24 | });
25 |
26 | it('has a symbol', async() => {
27 | const symbol = await contract.symbol();
28 | assert.equal(symbol, 'mTC');
29 | });
30 | });
31 |
32 | describe('minting', () => {
33 | it('creates a new token', async () => {
34 | const result = await contract.safeMint('testURI');
35 | const totalSupply = await contract.totalSupply();
36 |
37 | // SUCCESS
38 | assert.equal(totalSupply, 1);
39 | const event = result.logs[0].args;
40 | assert.equal(event.tokenId.toNumber(), 1, 'id is correct');
41 | assert.equal(event.from, '0x0000000000000000000000000000000000000000', 'from is correct');
42 | assert.equal(event.to, accounts[0], 'to is correct')
43 |
44 | // FAILURE: cannot mint same color twice
45 | await expectRevert(contract.safeMint('testURI'), 'The token URI should be unique');
46 | });
47 |
48 | it('token URI is correctly assigned', async() => {
49 | // SUCCESS
50 | const tokenURI = await contract.tokenURI(1);
51 | assert.equal(tokenURI, 'testURI');
52 |
53 | // FAILURE
54 | await expectRevert(contract.tokenURI(2), 'ERC721Metadata: URI query for nonexistent token');
55 | });
56 | });
57 | });
--------------------------------------------------------------------------------
/test/NFTMarketplace.test.js:
--------------------------------------------------------------------------------
1 | const { expectRevert } = require('@openzeppelin/test-helpers');
2 | const { assertion } = require('@openzeppelin/test-helpers/src/expectRevert');
3 |
4 | const NFTCollection = artifacts.require('./NFTCollection.sol');
5 | const NFTMarketplace = artifacts.require('./NFTMarketplace.sol');
6 |
7 | contract('NFTMarketplace', (accounts) => {
8 | let nftContract;
9 | let mktContract;
10 |
11 | before(async () => {
12 | nftContract = await NFTCollection.new();
13 |
14 | const NFTaddress = nftContract.address;
15 | mktContract = await NFTMarketplace.new(NFTaddress);
16 |
17 | await nftContract.safeMint('testURI');
18 | await nftContract.safeMint('testURI2');
19 | });
20 |
21 | describe('Make Offer', () => {
22 | it('Requires the approval from the user', async () => {
23 | await expectRevert(mktContract.makeOffer(1, 10), 'ERC721: transfer caller is not owner nor approved');
24 | });
25 |
26 | before(async() => {
27 | await nftContract.approve(mktContract.address, 2);
28 | await mktContract.makeOffer(2, 10);
29 | })
30 |
31 | it('Transfers the ownership to this contract', async() => {
32 | const owner = await nftContract.ownerOf(2);
33 | assert.equal(owner, mktContract.address);
34 | });
35 |
36 | it('Creates an offer', async() => {
37 | const offer = await mktContract.offers(1);
38 | assert.equal(offer.offerId.toNumber(), 1);
39 | assert.equal(offer.id.toNumber(), 2);
40 | assert.equal(offer.user, accounts[0]);
41 | assert.equal(offer.price.toNumber(), 10);
42 | assert.equal(offer.fulfilled, false);
43 | assert.equal(offer.cancelled, false);
44 | });
45 |
46 | it('Emits an Event Offer', async() => {
47 | await nftContract.approve(mktContract.address, 1);
48 | const result = await mktContract.makeOffer(1, 20);
49 | const log = result.logs[0];
50 | assert.equal(log.event, 'Offer');
51 | const event = log.args;
52 | assert.equal(event.offerId.toNumber(), 2);
53 | assert.equal(event.id.toNumber(), 1);
54 | assert.equal(event.user, accounts[0]);
55 | assert.equal(event.price.toNumber(), 20);
56 | assert.equal(event.fulfilled, false);
57 | assert.equal(event.cancelled, false);
58 | });
59 | });
60 |
61 | describe('Fill Offer', () => {
62 | it('fills the offer and emits Event', async() => {
63 | const result = await mktContract.fillOffer(1, { from: accounts[1], value: 10 });
64 | const offer = await mktContract.offers(1);
65 | assert.equal(offer.fulfilled, true);
66 | const userFunds = await mktContract.userFunds(offer.user);
67 | assert.equal(userFunds.toNumber(), 10);
68 |
69 | const log = result.logs[0];
70 | assert.equal(log.event, 'OfferFilled');
71 | const event = log.args;
72 | assert.equal(event.offerId.toNumber(), 1);
73 | });
74 |
75 | it('The offer must exist', async() => {
76 | await expectRevert(mktContract.fillOffer(3, { from: accounts[1] }), 'The offer must exist');
77 | });
78 |
79 | it('The owner cannot fill it', async() => {
80 | await expectRevert(mktContract.fillOffer(2, { from: accounts[0] }), 'The owner of the offer cannot fill it');
81 | });
82 |
83 | it('Cannot be fulfilled twice', async() => {
84 | await expectRevert(mktContract.fillOffer(1, { from: accounts[1] }), 'An offer cannot be fulfilled twice');
85 | });
86 |
87 | it('A fulfilled order cannot be cancelled', async() => {
88 | await expectRevert(mktContract.cancelOffer(1, { from: accounts[0] }), 'A fulfilled offer cannot be cancelled');
89 | });
90 |
91 | it('The ETH sent should match the price', async() => {
92 | await expectRevert(mktContract.fillOffer(2, { from: accounts[1], value: 5 }), 'The ETH amount should match with the NFT Price');
93 | });
94 | });
95 |
96 | describe('Cancel Offer', () => {
97 | it('Only the owner can cancel', async() => {
98 | await expectRevert(mktContract.cancelOffer(2, { from: accounts[1] }), 'The offer can only be canceled by the owner');
99 | });
100 |
101 | it('Cancels the offer and emits Event', async() => {
102 | const result = await mktContract.cancelOffer(2, { from: accounts[0] });
103 | const offer = await mktContract.offers(2);
104 | assert.equal(offer.cancelled, true);
105 |
106 | const log = result.logs[0];
107 | assert.equal(log.event, 'OfferCancelled');
108 | const event = log.args;
109 | assert.equal(event.offerId.toNumber(), 2);
110 | });
111 |
112 | it('The offer must exist', async() => {
113 | await expectRevert(mktContract.cancelOffer(3, { from: accounts[0] }), 'The offer must exist');
114 | });
115 |
116 | it('Cannot be cancelled twice', async() => {
117 | await expectRevert(mktContract.cancelOffer(2, { from: accounts[0] }), 'An offer cannot be cancelled twice');
118 | });
119 |
120 | it('A cancelled offer cannot be fulfilled', async() => {
121 | await expectRevert(mktContract.fillOffer(2, { from: accounts[1] }), 'A cancelled offer cannot be fulfilled');
122 | });
123 | });
124 |
125 | describe('Claim funds', () => {
126 | it('Rejects users without funds to claim', async() => {
127 | await expectRevert(mktContract.claimFunds({ from: accounts[1] }), 'This user has no funds to be claimed');
128 | });
129 |
130 | it('Pays the correct amount and emits Event', async() => {
131 | const fundsBefore = await mktContract.userFunds(accounts[0]);
132 | const result = await mktContract.claimFunds({ from: accounts[0] });
133 | const fundsAfter = await mktContract.userFunds(accounts[0]);
134 | assert.equal(fundsBefore.toNumber(), 10);
135 | assert.equal(fundsAfter.toNumber(), 0);
136 |
137 | const log = result.logs[0];
138 | assert.equal(log.event, 'ClaimFunds');
139 | const event = log.args;
140 | assert.equal(event.user, accounts[0]);
141 | assert.equal(event.amount.toNumber(), 10);
142 | });
143 | });
144 | });
--------------------------------------------------------------------------------
/truffle-config.js:
--------------------------------------------------------------------------------
1 | require('dotenv').config();
2 |
3 | const HDWalletProvider = require('@truffle/hdwallet-provider');
4 | const fs = require("fs");
5 | const mnemonic = fs.readFileSync(".secret").toString().trim();
6 | console.log(mnemonic);
7 |
8 | module.exports = {
9 | mocha: {
10 | enableTimeouts: 120000
11 | },
12 |
13 | networks: {
14 | development: {
15 | host: "127.0.0.1",
16 | port: 7545,
17 | network_id: "*" //match any network id
18 | },
19 | // rinkeby: {
20 | // provider: function () {
21 | // return new HDWalletProvider(
22 | // privateKeys.split(','), // array of private keys
23 | // `https://rinkeby.infura.io/v3/${process.env.INFURA_API_KEY}` // Url to an Ethereum node
24 | // )
25 | // },
26 | // gas: 6500000,
27 | // gasPrice: 10000000000,
28 | // network_id: 4,
29 | // networkCheckTimeout: 120000,
30 | // timeoutBlocks: 200,
31 | // confirmations: 5,
32 | // skipDryRun: true
33 | // },
34 | // ropsten: {
35 | // provider() {
36 | // return new HDWalletProvider(
37 | // process.env.TESTNET_MNEMONIC,
38 | // "https://ropsten.infura.io/v3/" + process.env.INFURA_API_KEY
39 | // );
40 | // },
41 | // network_id: 2
42 | // // gas: 4700000
43 | // },
44 | bsctest: {
45 | provider: () => new HDWalletProvider(mnemonic, `https://data-seed-prebsc-1-s1.binance.org:8545/`),
46 | network_id: 97,
47 | skipDryRun: true,
48 | networkCheckTimeout: 1000000,
49 | timeoutBlocks: 200,
50 | }
51 | },
52 |
53 | contracts_directory: './src/contracts',
54 | contracts_build_directory: './src/abis',
55 |
56 | // Configure your compilers
57 | compilers: {
58 | solc: {
59 | optimizer: {
60 | enabled: true,
61 | runs: 200
62 | },
63 | version: "^0.8.0"
64 | }
65 | },
66 | plugins: [
67 | 'truffle-plugin-verify'
68 | ],
69 | api_keys: {
70 | etherscan: 'R93VASIPJPBCAS9Z27DSUDWX518SEY9QPK',
71 | bscscan: 'A2HNWK3VKZNQFAGU254HW1DAG4RPB8FI8T'
72 | }
73 | };
--------------------------------------------------------------------------------