├── test └── .gitkeep ├── migrations ├── 2_initial_migration.js └── 1_initial_migration.js ├── metadata ├── 2.json ├── 1.json ├── 4.json ├── 5.json ├── 6.json ├── 7.json ├── 8.json ├── 9.json ├── 10.json ├── 3.json └── 0.json ├── package.json ├── README.md ├── .gitignore ├── contracts ├── Migrations.sol ├── WolfNFT.sol └── flatWolfNFT.sol ├── scripts ├── deploy.js └── mint.js └── truffle-config.js /test/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /migrations/2_initial_migration.js: -------------------------------------------------------------------------------- 1 | const WolfNFT = artifacts.require("WolfNFT"); 2 | 3 | module.exports = function (deployer) { 4 | deployer.deploy(WolfNFT); 5 | }; 6 | -------------------------------------------------------------------------------- /migrations/1_initial_migration.js: -------------------------------------------------------------------------------- 1 | const Migrations = artifacts.require("Migrations"); 2 | 3 | module.exports = function (deployer) { 4 | deployer.deploy(Migrations); 5 | }; 6 | -------------------------------------------------------------------------------- /metadata/2.json: -------------------------------------------------------------------------------- 1 | {"name":"2","image":"https://ipfs.io/ipfs/QmQjDhhCmPLo6G3zJBmsftihGV17tb1STBy7DWYMNwDtvH/2.png","attributes":[{"trait_type":"Breed","value":"Doberman"},{"trait_type":"Color","value":"Brown"},{"trait_type":"Apparel","value":"Tunic"},{"trait_type":"Headgear","value":"No"}]} 2 | -------------------------------------------------------------------------------- /metadata/1.json: -------------------------------------------------------------------------------- 1 | {"name":"1","image":"https://ipfs.io/ipfs/QmQjDhhCmPLo6G3zJBmsftihGV17tb1STBy7DWYMNwDtvH/1.png","attributes":[{"trait_type":"Breed","value":"Pitbull"},{"trait_type":"Color","value":"Gold"},{"trait_type":"Apparel","value":"Mech Suit"},{"trait_type":"Headgear","value":"No"}]} 2 | -------------------------------------------------------------------------------- /metadata/4.json: -------------------------------------------------------------------------------- 1 | {"name":"4","image":"https://ipfs.io/ipfs/QmQjDhhCmPLo6G3zJBmsftihGV17tb1STBy7DWYMNwDtvH/4.png","attributes":[{"trait_type":"Breed","value":"Doberman"},{"trait_type":"Color","value":"Brown"},{"trait_type":"Apparel","value":"Tunic"},{"trait_type":"Headgear","value":"Armor"}]} 2 | -------------------------------------------------------------------------------- /metadata/5.json: -------------------------------------------------------------------------------- 1 | {"name":"5","image":"https://ipfs.io/ipfs/QmQjDhhCmPLo6G3zJBmsftihGV17tb1STBy7DWYMNwDtvH/5.png","attributes":[{"trait_type":"Breed","value":"Doberman"},{"trait_type":"Color","value":"Brown"},{"trait_type":"Apparel","value":"Mech Suit"},{"trait_type":"Headgear","value":"No"}]} 2 | -------------------------------------------------------------------------------- /metadata/6.json: -------------------------------------------------------------------------------- 1 | {"name":"6","image":"https://ipfs.io/ipfs/QmQjDhhCmPLo6G3zJBmsftihGV17tb1STBy7DWYMNwDtvH/6.png","attributes":[{"trait_type":"Breed","value":"Unknown"},{"trait_type":"Color","value":"Brown"},{"trait_type":"Apparel","value":"Mech Suit"},{"trait_type":"Headgear","value":"Armor"}]} 2 | -------------------------------------------------------------------------------- /metadata/7.json: -------------------------------------------------------------------------------- 1 | {"name":"7","image":"https://ipfs.io/ipfs/QmQjDhhCmPLo6G3zJBmsftihGV17tb1STBy7DWYMNwDtvH/7.png","attributes":[{"trait_type":"Breed","value":"Pitbull"},{"trait_type":"Color","value":"Black"},{"trait_type":"Apparel","value":"Tunic"},{"trait_type":"Headgear","value":"Armor"}]} 2 | -------------------------------------------------------------------------------- /metadata/8.json: -------------------------------------------------------------------------------- 1 | {"name":"8","image":"https://ipfs.io/ipfs/QmQjDhhCmPLo6G3zJBmsftihGV17tb1STBy7DWYMNwDtvH/8.png","attributes":[{"trait_type":"Breed","value":"Pitbull"},{"trait_type":"Color","value":"Brown"},{"trait_type":"Apparel","value":"Tank Top"},{"trait_type":"Headgear","value":"No"}]} 2 | -------------------------------------------------------------------------------- /metadata/9.json: -------------------------------------------------------------------------------- 1 | {"name":"9","image":"https://ipfs.io/ipfs/QmQjDhhCmPLo6G3zJBmsftihGV17tb1STBy7DWYMNwDtvH/9.png","attributes":[{"trait_type":"Breed","value":"Doberman"},{"trait_type":"Color","value":"Brown"},{"trait_type":"Apparel","value":"Mech Suit"},{"trait_type":"Headgear","value":"No"}]} 2 | -------------------------------------------------------------------------------- /metadata/10.json: -------------------------------------------------------------------------------- 1 | {"name":"10","image":"https://ipfs.io/ipfs/QmQjDhhCmPLo6G3zJBmsftihGV17tb1STBy7DWYMNwDtvH/10.png","attributes":[{"trait_type":"Breed","value":"Husky"},{"trait_type":"Color","value":"Brown"},{"trait_type":"Apparel","value":"Mantle"},{"trait_type":"Headgear","value":"Ceremonial"}]} 2 | -------------------------------------------------------------------------------- /metadata/3.json: -------------------------------------------------------------------------------- 1 | {"name":"3","image":"https://ipfs.io/ipfs/QmQjDhhCmPLo6G3zJBmsftihGV17tb1STBy7DWYMNwDtvH/3.png","attributes":[{"trait_type":"Breed","value":"German Shepherd"},{"trait_type":"Color","value":"Brown"},{"trait_type":"Apparel","value":"Uniform"},{"trait_type":"Headgear","value":"No"}]} 2 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "dependencies": { 3 | "@openzeppelin/contracts": "^4.3.2", 4 | "@project-serum/anchor": "^0.18.0", 5 | "@truffle/hdwallet-provider": "^1.5.1", 6 | "axios": "^0.24.0", 7 | "dotenv": "^10.0.0", 8 | "web3": "^1.6.0", 9 | "web3-eth-contract": "^1.6.0" 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## ERC721URIStorage 2 | ## Batch Minting NFTs with uploading assets to Ipfs 3 | ## Compile with truffle 4 | ## Deploying and Minting with web3.js 5 | ## Deployed on Rinkeby Testnet. 6 | ## OpenSea 7 | https://testnets.opensea.io/collection/wolfnft 8 | ### Contract address is 0x882031b258829FC6ff0c950eD2F6e69B5d5066EF, verified. -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | /.pnp 6 | .pnp.js 7 | 8 | # testing 9 | /coverage 10 | 11 | # production 12 | /build 13 | 14 | # misc 15 | .DS_Store 16 | .env 17 | .env.local 18 | .env.development.local 19 | .env.test.local 20 | .env.production.local 21 | .secret 22 | 23 | npm-debug.log* 24 | yarn-debug.log* 25 | yarn-error.log* 26 | -------------------------------------------------------------------------------- /metadata/0.json: -------------------------------------------------------------------------------- 1 | {"name":"FROGBOIZ NFT 1","image":"https://ipfs.io/ipfs/QmQjDhhCmPLo6G3zJBmsftihGV17tb1STBy7DWYMNwDtvH/1.png","symbol":"","properties":{"files":[{"uri":"https://ipfs.io/ipfs/QmQjDhhCmPLo6G3zJBmsftihGV17tb1STBy7DWYMNwDtvH/1.png","type":"image/png"}],"category":"image","creators":[{"address":"Eey3zH3waRKoMY68ygQiiwE23h79W8ZyNNxKDTQhjuEM","share":100}]},"description":"FROGBOIZ NFT price: 0.5 sol","seller_fee_basis_points":5,"external_url":"google.com","collection":{"name":"Dreamland Test","family":"Dreamland"}} -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /contracts/WolfNFT.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.8.0; 2 | 3 | import "@openzeppelin/contracts/token/ERC721/extensions/ERC721URIStorage.sol"; 4 | import "@openzeppelin/contracts/utils/Counters.sol"; 5 | import "@openzeppelin/contracts/access/Ownable.sol"; 6 | 7 | contract WolfNFT is ERC721URIStorage, Ownable { 8 | using Counters for Counters.Counter; 9 | Counters.Counter private _tokenIds; 10 | 11 | constructor() ERC721("WolfNFT", "WFT") { 12 | 13 | } 14 | 15 | function setTokenURI(uint256 tokenId, string memory _tokenURI) public { 16 | super._setTokenURI(tokenId, _tokenURI); 17 | } 18 | 19 | function mint(address recipient, string memory tokenURI) public onlyOwner returns (uint256) { 20 | _tokenIds.increment(); 21 | 22 | uint256 nftId = _tokenIds.current(); 23 | _mint(recipient, nftId); 24 | setTokenURI(nftId, tokenURI); 25 | 26 | return nftId; 27 | } 28 | } -------------------------------------------------------------------------------- /scripts/deploy.js: -------------------------------------------------------------------------------- 1 | 2 | require('dotenv').config(); 3 | 4 | let wolf_nft_json = require('../build/contracts/WolfNFT.json'); 5 | let Web3 = require('web3'); 6 | const web3 = new Web3("https://rinkeby.infura.io/v3/9aa3d95b3bc440fa88ea12eaa4456161"); 7 | const myWallet = process.env.WALLET_PUBLIC_KEY; 8 | const privKey = process.env.WALLET_PRIVATE_KEY; 9 | 10 | const deploy = async () => { 11 | const wolfContract = new web3.eth.Contract(wolf_nft_json.abi); 12 | deployTx = wolfContract.deploy({ 13 | data: wolf_nft_json.bytecode, 14 | arguments: [] 15 | }); 16 | deployTx.estimateGas((err, gas) => { 17 | console.log("gas =", gas); 18 | }) 19 | /* 20 | const addressArray = await window.ethereum.request({ 21 | method: "eth_requestAccounts", 22 | });*/ 23 | const createTransaction = await web3.eth.accounts.signTransaction( 24 | { 25 | from: myWallet, 26 | data: deployTx.encodeABI(), 27 | gas: 3925192, 28 | }, 29 | privKey 30 | ); 31 | const createReceipt = await web3.eth.sendSignedTransaction( 32 | createTransaction.rawTransaction 33 | ); 34 | console.log('Contract deployed at address', createReceipt.contractAddress); 35 | } 36 | 37 | deploy(); 38 | -------------------------------------------------------------------------------- /scripts/mint.js: -------------------------------------------------------------------------------- 1 | require('dotenv').config(); 2 | 3 | const Web3 = require('web3'); 4 | const web3 = new Web3("https://rinkeby.infura.io/v3/9aa3d95b3bc440fa88ea12eaa4456161"); 5 | const axios = require('axios'); 6 | 7 | const pinata_api_key = "e5e08d64baeead28a9c7"; 8 | const secret = "d1e7ecbfdcdd39ea8488408af67a973abcfda67ba72efe438cc0c0ef03a71a7f"; 9 | const myWallet = process.env.WALLET_PUBLIC_KEY; 10 | const privKey = process.env.WALLET_PRIVATE_KEY; 11 | 12 | let wolf_nft_json = require('../build/contracts/WolfNFT.json'); 13 | let wolfContractAddress = "0x882031b258829FC6ff0c950eD2F6e69B5d5066EF" 14 | 15 | const mintNFT = async (count) => { 16 | let wolfContract = new web3.eth.Contract(wolf_nft_json.abi, wolfContractAddress); 17 | let metadata = require(`../metadata/${count}.json`); 18 | let tokenURI = await pinJSONToIPFS(metadata); 19 | const mintTransaction = await web3.eth.accounts.signTransaction({ 20 | from: myWallet, 21 | to: wolfContractAddress, 22 | data: wolfContract.methods.mint(myWallet, tokenURI.pinataUrl).encodeABI(), 23 | gas: 3925192 24 | }, privKey); 25 | const receipt = await web3.eth.sendSignedTransaction( 26 | mintTransaction.rawTransaction 27 | ); 28 | console.log("receipt = ", receipt); 29 | } 30 | 31 | 32 | const pinJSONToIPFS = async (JSONBody) => { 33 | console.log("pinata_api_key = ", pinata_api_key); 34 | const url = `https://api.pinata.cloud/pinning/pinJSONToIPFS`; 35 | return axios 36 | .post(url, JSONBody, { 37 | headers: { 38 | pinata_api_key: pinata_api_key, 39 | pinata_secret_api_key: secret 40 | } 41 | }) 42 | .then((response) => { 43 | console.log("hash = " + response.data.IpfsHash); 44 | return { 45 | success: true, 46 | pinataUrl: "https://gateway.pinata.cloud/ipfs/" + response.data.IpfsHash 47 | } 48 | }) 49 | .catch(error => { 50 | console.error(error); 51 | return { 52 | success: false, 53 | message: error.message 54 | } 55 | }) 56 | } 57 | 58 | /* 59 | (async () => { 60 | for (let i = 1; i <= 10; i ++) { 61 | await mintNFT(i); 62 | } 63 | })(); 64 | */ 65 | 66 | (async () => { 67 | console.log("pinning"); 68 | for (let i = 0; i <= 0; i ++) { 69 | let metadata = require(`../metadata/${i}.json`); 70 | let tokenURI = await pinJSONToIPFS(metadata); 71 | 72 | console.log("pinning"); 73 | console.log("tokenURI = ", tokenURI); 74 | } 75 | })(); 76 | -------------------------------------------------------------------------------- /truffle-config.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Use this file to configure your truffle project. It's seeded with some 3 | * common settings for different networks and features like migrations, 4 | * compilation and testing. Uncomment the ones you need or modify 5 | * them to suit your project as necessary. 6 | * 7 | * More information about configuration can be found at: 8 | * 9 | * trufflesuite.com/docs/advanced/configuration 10 | * 11 | * To deploy via Infura you'll need a wallet provider (like @truffle/hdwallet-provider) 12 | * to sign your transactions before they're sent to a remote public node. Infura accounts 13 | * are available for free at: infura.io/register. 14 | * 15 | * You'll also need a mnemonic - the twelve word phrase the wallet uses to generate 16 | * public/private key pairs. If you're publishing your code to GitHub make sure you load this 17 | * phrase from a file you've .gitignored so it doesn't accidentally become public. 18 | * 19 | */ 20 | 21 | const HDWalletProvider = require('@truffle/hdwallet-provider'); 22 | // 23 | const fs = require('fs'); 24 | const mnemonic = fs.readFileSync(".secret").toString().trim(); 25 | 26 | module.exports = { 27 | /** 28 | * Networks define how you connect to your ethereum client and let you set the 29 | * defaults web3 uses to send transactions. If you don't specify one truffle 30 | * will spin up a development blockchain for you on port 9545 when you 31 | * run `develop` or `test`. You can ask a truffle command to use a specific 32 | * network from the command line, e.g 33 | * 34 | * $ truffle test --network 35 | */ 36 | 37 | networks: { 38 | // Useful for testing. The `development` name is special - truffle uses it by default 39 | // if it's defined here and no other network is specified at the command line. 40 | // You should run a client (like ganache-cli, geth or parity) in a separate terminal 41 | // tab if you use this network and you must also set the `host`, `port` and `network_id` 42 | // options below to some value. 43 | // 44 | // development: { 45 | // host: "127.0.0.1", // Localhost (default: none) 46 | // port: 8545, // Standard Ethereum port (default: none) 47 | // network_id: "*", // Any network (default: none) 48 | // }, 49 | // Another network with more advanced options... 50 | // advanced: { 51 | // port: 8777, // Custom port 52 | // network_id: 1342, // Custom network 53 | // gas: 8500000, // Gas sent with each transaction (default: ~6700000) 54 | // gasPrice: 20000000000, // 20 gwei (in wei) (default: 100 gwei) 55 | // from:
, // Account to send txs from (default: accounts[0]) 56 | // websocket: true // Enable EventEmitter interface for web3 (default: false) 57 | // }, 58 | // Useful for deploying to a public network. 59 | // NB: It's important to wrap the provider as a function. 60 | // ropsten: { 61 | // provider: () => new HDWalletProvider(mnemonic, `https://ropsten.infura.io/v3/YOUR-PROJECT-ID`), 62 | // network_id: 3, // Ropsten's id 63 | // gas: 5500000, // Ropsten has a lower block limit than mainnet 64 | // confirmations: 2, // # of confs to wait between deployments. (default: 0) 65 | // timeoutBlocks: 200, // # of blocks before a deployment times out (minimum/default: 50) 66 | // skipDryRun: true // Skip dry run before migrations? (default: false for public nets ) 67 | // }, 68 | 69 | rinkeby: { 70 | provider: () => new HDWalletProvider(mnemonic, `https://rinkeby.infura.io/v3/f1eb9fea209e4e0ba543f866b3aef962`), 71 | network_id: 4, // Ropsten's id 72 | gas: 5500000, // Ropsten has a lower block limit than mainnet 73 | confirmations: 2, // # of confs to wait between deployments. (default: 0) 74 | timeoutBlocks: 200, // # of blocks before a deployment times out (minimum/default: 50) 75 | skipDryRun: true // Skip dry run before migrations? (default: false for public nets ) 76 | }, 77 | 78 | // Useful for private networks 79 | // private: { 80 | // provider: () => new HDWalletProvider(mnemonic, `https://network.io`), 81 | // network_id: 2111, // This network is yours, in the cloud. 82 | // production: true // Treats this network as if it was a public net. (default: false) 83 | // } 84 | }, 85 | 86 | // Set default mocha options here, use special reporters etc. 87 | mocha: { 88 | // timeout: 100000 89 | }, 90 | 91 | // Configure your compilers 92 | compilers: { 93 | solc: { 94 | version: "0.8.0", // Fetch exact version from solc-bin (default: truffle's version) 95 | // docker: true, // Use "0.5.1" you've installed locally with docker (default: false) 96 | // settings: { // See the solidity docs for advice about optimization and evmVersion 97 | // optimizer: { 98 | // enabled: false, 99 | // runs: 200 100 | // }, 101 | // evmVersion: "byzantium" 102 | // } 103 | } 104 | }, 105 | 106 | // Truffle DB is currently disabled by default; to enable it, change enabled: 107 | // false to enabled: true. The default storage location can also be 108 | // overridden by specifying the adapter settings, as shown in the commented code below. 109 | // 110 | // NOTE: It is not possible to migrate your contracts to truffle DB and you should 111 | // make a backup of your artifacts to a safe location before enabling this feature. 112 | // 113 | // After you backed up your artifacts you can utilize db by running migrate as follows: 114 | // $ truffle migrate --reset --compile-all 115 | // 116 | // db: { 117 | // enabled: false, 118 | // host: "127.0.0.1", 119 | // adapter: { 120 | // name: "sqlite", 121 | // settings: { 122 | // directory: ".db" 123 | // } 124 | // } 125 | // } 126 | }; 127 | -------------------------------------------------------------------------------- /contracts/flatWolfNFT.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.8.0; 2 | // SPDX-License-Identifier: MIT 3 | 4 | interface IERC165 { 5 | /** 6 | * @dev Returns true if this contract implements the interface defined by 7 | * `interfaceId`. See the corresponding 8 | * https://eips.ethereum.org/EIPS/eip-165#how-interfaces-are-identified[EIP section] 9 | * to learn more about how these ids are created. 10 | * 11 | * This function call must use less than 30 000 gas. 12 | */ 13 | function supportsInterface(bytes4 interfaceId) external view returns (bool); 14 | } 15 | 16 | /** 17 | * @dev Required interface of an ERC721 compliant contract. 18 | */ 19 | interface IERC721 is IERC165 { 20 | /** 21 | * @dev Emitted when `tokenId` token is transferred from `from` to `to`. 22 | */ 23 | event Transfer(address indexed from, address indexed to, uint256 indexed tokenId); 24 | 25 | /** 26 | * @dev Emitted when `owner` enables `approved` to manage the `tokenId` token. 27 | */ 28 | event Approval(address indexed owner, address indexed approved, uint256 indexed tokenId); 29 | 30 | /** 31 | * @dev Emitted when `owner` enables or disables (`approved`) `operator` to manage all of its assets. 32 | */ 33 | event ApprovalForAll(address indexed owner, address indexed operator, bool approved); 34 | 35 | /** 36 | * @dev Returns the number of tokens in ``owner``'s account. 37 | */ 38 | function balanceOf(address owner) external view returns (uint256 balance); 39 | 40 | /** 41 | * @dev Returns the owner of the `tokenId` token. 42 | * 43 | * Requirements: 44 | * 45 | * - `tokenId` must exist. 46 | */ 47 | function ownerOf(uint256 tokenId) external view returns (address owner); 48 | 49 | /** 50 | * @dev Safely transfers `tokenId` token from `from` to `to`, checking first that contract recipients 51 | * are aware of the ERC721 protocol to prevent tokens from being forever locked. 52 | * 53 | * Requirements: 54 | * 55 | * - `from` cannot be the zero address. 56 | * - `to` cannot be the zero address. 57 | * - `tokenId` token must exist and be owned by `from`. 58 | * - If the caller is not `from`, it must be have been allowed to move this token by either {approve} or {setApprovalForAll}. 59 | * - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon a safe transfer. 60 | * 61 | * Emits a {Transfer} event. 62 | */ 63 | function safeTransferFrom( 64 | address from, 65 | address to, 66 | uint256 tokenId 67 | ) external; 68 | 69 | /** 70 | * @dev Transfers `tokenId` token from `from` to `to`. 71 | * 72 | * WARNING: Usage of this method is discouraged, use {safeTransferFrom} whenever possible. 73 | * 74 | * Requirements: 75 | * 76 | * - `from` cannot be the zero address. 77 | * - `to` cannot be the zero address. 78 | * - `tokenId` token must be owned by `from`. 79 | * - If the caller is not `from`, it must be approved to move this token by either {approve} or {setApprovalForAll}. 80 | * 81 | * Emits a {Transfer} event. 82 | */ 83 | function transferFrom( 84 | address from, 85 | address to, 86 | uint256 tokenId 87 | ) external; 88 | 89 | /** 90 | * @dev Gives permission to `to` to transfer `tokenId` token to another account. 91 | * The approval is cleared when the token is transferred. 92 | * 93 | * Only a single account can be approved at a time, so approving the zero address clears previous approvals. 94 | * 95 | * Requirements: 96 | * 97 | * - The caller must own the token or be an approved operator. 98 | * - `tokenId` must exist. 99 | * 100 | * Emits an {Approval} event. 101 | */ 102 | function approve(address to, uint256 tokenId) external; 103 | 104 | /** 105 | * @dev Returns the account approved for `tokenId` token. 106 | * 107 | * Requirements: 108 | * 109 | * - `tokenId` must exist. 110 | */ 111 | function getApproved(uint256 tokenId) external view returns (address operator); 112 | 113 | /** 114 | * @dev Approve or remove `operator` as an operator for the caller. 115 | * Operators can call {transferFrom} or {safeTransferFrom} for any token owned by the caller. 116 | * 117 | * Requirements: 118 | * 119 | * - The `operator` cannot be the caller. 120 | * 121 | * Emits an {ApprovalForAll} event. 122 | */ 123 | function setApprovalForAll(address operator, bool _approved) external; 124 | 125 | /** 126 | * @dev Returns if the `operator` is allowed to manage all of the assets of `owner`. 127 | * 128 | * See {setApprovalForAll} 129 | */ 130 | function isApprovedForAll(address owner, address operator) external view returns (bool); 131 | 132 | /** 133 | * @dev Safely transfers `tokenId` token from `from` to `to`. 134 | * 135 | * Requirements: 136 | * 137 | * - `from` cannot be the zero address. 138 | * - `to` cannot be the zero address. 139 | * - `tokenId` token must exist and be owned by `from`. 140 | * - If the caller is not `from`, it must be approved to move this token by either {approve} or {setApprovalForAll}. 141 | * - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon a safe transfer. 142 | * 143 | * Emits a {Transfer} event. 144 | */ 145 | function safeTransferFrom( 146 | address from, 147 | address to, 148 | uint256 tokenId, 149 | bytes calldata data 150 | ) external; 151 | } 152 | abstract contract ERC165 is IERC165 { 153 | /** 154 | * @dev See {IERC165-supportsInterface}. 155 | */ 156 | function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) { 157 | return interfaceId == type(IERC165).interfaceId; 158 | } 159 | } 160 | abstract contract Context { 161 | function _msgSender() internal view virtual returns (address) { 162 | return msg.sender; 163 | } 164 | 165 | function _msgData() internal view virtual returns (bytes calldata) { 166 | return msg.data; 167 | } 168 | } 169 | 170 | 171 | library Strings { 172 | bytes16 private constant _HEX_SYMBOLS = "0123456789abcdef"; 173 | 174 | /** 175 | * @dev Converts a `uint256` to its ASCII `string` decimal representation. 176 | */ 177 | function toString(uint256 value) internal pure returns (string memory) { 178 | // Inspired by OraclizeAPI's implementation - MIT licence 179 | // https://github.com/oraclize/ethereum-api/blob/b42146b063c7d6ee1358846c198246239e9360e8/oraclizeAPI_0.4.25.sol 180 | 181 | if (value == 0) { 182 | return "0"; 183 | } 184 | uint256 temp = value; 185 | uint256 digits; 186 | while (temp != 0) { 187 | digits++; 188 | temp /= 10; 189 | } 190 | bytes memory buffer = new bytes(digits); 191 | while (value != 0) { 192 | digits -= 1; 193 | buffer[digits] = bytes1(uint8(48 + uint256(value % 10))); 194 | value /= 10; 195 | } 196 | return string(buffer); 197 | } 198 | 199 | /** 200 | * @dev Converts a `uint256` to its ASCII `string` hexadecimal representation. 201 | */ 202 | function toHexString(uint256 value) internal pure returns (string memory) { 203 | if (value == 0) { 204 | return "0x00"; 205 | } 206 | uint256 temp = value; 207 | uint256 length = 0; 208 | while (temp != 0) { 209 | length++; 210 | temp >>= 8; 211 | } 212 | return toHexString(value, length); 213 | } 214 | 215 | /** 216 | * @dev Converts a `uint256` to its ASCII `string` hexadecimal representation with fixed length. 217 | */ 218 | function toHexString(uint256 value, uint256 length) internal pure returns (string memory) { 219 | bytes memory buffer = new bytes(2 * length + 2); 220 | buffer[0] = "0"; 221 | buffer[1] = "x"; 222 | for (uint256 i = 2 * length + 1; i > 1; --i) { 223 | buffer[i] = _HEX_SYMBOLS[value & 0xf]; 224 | value >>= 4; 225 | } 226 | require(value == 0, "Strings: hex length insufficient"); 227 | return string(buffer); 228 | } 229 | } 230 | 231 | 232 | library Address { 233 | /** 234 | * @dev Returns true if `account` is a contract. 235 | * 236 | * [IMPORTANT] 237 | * ==== 238 | * It is unsafe to assume that an address for which this function returns 239 | * false is an externally-owned account (EOA) and not a contract. 240 | * 241 | * Among others, `isContract` will return false for the following 242 | * types of addresses: 243 | * 244 | * - an externally-owned account 245 | * - a contract in construction 246 | * - an address where a contract will be created 247 | * - an address where a contract lived, but was destroyed 248 | * ==== 249 | */ 250 | function isContract(address account) internal view returns (bool) { 251 | // This method relies on extcodesize, which returns 0 for contracts in 252 | // construction, since the code is only stored at the end of the 253 | // constructor execution. 254 | 255 | uint256 size; 256 | assembly { 257 | size := extcodesize(account) 258 | } 259 | return size > 0; 260 | } 261 | 262 | /** 263 | * @dev Replacement for Solidity's `transfer`: sends `amount` wei to 264 | * `recipient`, forwarding all available gas and reverting on errors. 265 | * 266 | * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost 267 | * of certain opcodes, possibly making contracts go over the 2300 gas limit 268 | * imposed by `transfer`, making them unable to receive funds via 269 | * `transfer`. {sendValue} removes this limitation. 270 | * 271 | * https://diligence.consensys.net/posts/2019/09/stop-using-soliditys-transfer-now/[Learn more]. 272 | * 273 | * IMPORTANT: because control is transferred to `recipient`, care must be 274 | * taken to not create reentrancy vulnerabilities. Consider using 275 | * {ReentrancyGuard} or the 276 | * https://solidity.readthedocs.io/en/v0.5.11/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern]. 277 | */ 278 | function sendValue(address payable recipient, uint256 amount) internal { 279 | require(address(this).balance >= amount, "Address: insufficient balance"); 280 | 281 | (bool success, ) = recipient.call{value: amount}(""); 282 | require(success, "Address: unable to send value, recipient may have reverted"); 283 | } 284 | 285 | /** 286 | * @dev Performs a Solidity function call using a low level `call`. A 287 | * plain `call` is an unsafe replacement for a function call: use this 288 | * function instead. 289 | * 290 | * If `target` reverts with a revert reason, it is bubbled up by this 291 | * function (like regular Solidity function calls). 292 | * 293 | * Returns the raw returned data. To convert to the expected return value, 294 | * use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`]. 295 | * 296 | * Requirements: 297 | * 298 | * - `target` must be a contract. 299 | * - calling `target` with `data` must not revert. 300 | * 301 | * _Available since v3.1._ 302 | */ 303 | function functionCall(address target, bytes memory data) internal returns (bytes memory) { 304 | return functionCall(target, data, "Address: low-level call failed"); 305 | } 306 | 307 | /** 308 | * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with 309 | * `errorMessage` as a fallback revert reason when `target` reverts. 310 | * 311 | * _Available since v3.1._ 312 | */ 313 | function functionCall( 314 | address target, 315 | bytes memory data, 316 | string memory errorMessage 317 | ) internal returns (bytes memory) { 318 | return functionCallWithValue(target, data, 0, errorMessage); 319 | } 320 | 321 | /** 322 | * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], 323 | * but also transferring `value` wei to `target`. 324 | * 325 | * Requirements: 326 | * 327 | * - the calling contract must have an ETH balance of at least `value`. 328 | * - the called Solidity function must be `payable`. 329 | * 330 | * _Available since v3.1._ 331 | */ 332 | function functionCallWithValue( 333 | address target, 334 | bytes memory data, 335 | uint256 value 336 | ) internal returns (bytes memory) { 337 | return functionCallWithValue(target, data, value, "Address: low-level call with value failed"); 338 | } 339 | 340 | /** 341 | * @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but 342 | * with `errorMessage` as a fallback revert reason when `target` reverts. 343 | * 344 | * _Available since v3.1._ 345 | */ 346 | function functionCallWithValue( 347 | address target, 348 | bytes memory data, 349 | uint256 value, 350 | string memory errorMessage 351 | ) internal returns (bytes memory) { 352 | require(address(this).balance >= value, "Address: insufficient balance for call"); 353 | require(isContract(target), "Address: call to non-contract"); 354 | 355 | (bool success, bytes memory returndata) = target.call{value: value}(data); 356 | return verifyCallResult(success, returndata, errorMessage); 357 | } 358 | 359 | /** 360 | * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], 361 | * but performing a static call. 362 | * 363 | * _Available since v3.3._ 364 | */ 365 | function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) { 366 | return functionStaticCall(target, data, "Address: low-level static call failed"); 367 | } 368 | 369 | /** 370 | * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`], 371 | * but performing a static call. 372 | * 373 | * _Available since v3.3._ 374 | */ 375 | function functionStaticCall( 376 | address target, 377 | bytes memory data, 378 | string memory errorMessage 379 | ) internal view returns (bytes memory) { 380 | require(isContract(target), "Address: static call to non-contract"); 381 | 382 | (bool success, bytes memory returndata) = target.staticcall(data); 383 | return verifyCallResult(success, returndata, errorMessage); 384 | } 385 | 386 | /** 387 | * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], 388 | * but performing a delegate call. 389 | * 390 | * _Available since v3.4._ 391 | */ 392 | function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) { 393 | return functionDelegateCall(target, data, "Address: low-level delegate call failed"); 394 | } 395 | 396 | /** 397 | * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`], 398 | * but performing a delegate call. 399 | * 400 | * _Available since v3.4._ 401 | */ 402 | function functionDelegateCall( 403 | address target, 404 | bytes memory data, 405 | string memory errorMessage 406 | ) internal returns (bytes memory) { 407 | require(isContract(target), "Address: delegate call to non-contract"); 408 | 409 | (bool success, bytes memory returndata) = target.delegatecall(data); 410 | return verifyCallResult(success, returndata, errorMessage); 411 | } 412 | 413 | /** 414 | * @dev Tool to verifies that a low level call was successful, and revert if it wasn't, either by bubbling the 415 | * revert reason using the provided one. 416 | * 417 | * _Available since v4.3._ 418 | */ 419 | function verifyCallResult( 420 | bool success, 421 | bytes memory returndata, 422 | string memory errorMessage 423 | ) internal pure returns (bytes memory) { 424 | if (success) { 425 | return returndata; 426 | } else { 427 | // Look for revert reason and bubble it up if present 428 | if (returndata.length > 0) { 429 | // The easiest way to bubble the revert reason is using memory via assembly 430 | 431 | assembly { 432 | let returndata_size := mload(returndata) 433 | revert(add(32, returndata), returndata_size) 434 | } 435 | } else { 436 | revert(errorMessage); 437 | } 438 | } 439 | } 440 | } 441 | 442 | interface IERC721Metadata is IERC721 { 443 | /** 444 | * @dev Returns the token collection name. 445 | */ 446 | function name() external view returns (string memory); 447 | 448 | /** 449 | * @dev Returns the token collection symbol. 450 | */ 451 | function symbol() external view returns (string memory); 452 | 453 | /** 454 | * @dev Returns the Uniform Resource Identifier (URI) for `tokenId` token. 455 | */ 456 | function tokenURI(uint256 tokenId) external view returns (string memory); 457 | } 458 | 459 | interface IERC721Receiver { 460 | /** 461 | * @dev Whenever an {IERC721} `tokenId` token is transferred to this contract via {IERC721-safeTransferFrom} 462 | * by `operator` from `from`, this function is called. 463 | * 464 | * It must return its Solidity selector to confirm the token transfer. 465 | * If any other value is returned or the interface is not implemented by the recipient, the transfer will be reverted. 466 | * 467 | * The selector can be obtained in Solidity with `IERC721.onERC721Received.selector`. 468 | */ 469 | function onERC721Received( 470 | address operator, 471 | address from, 472 | uint256 tokenId, 473 | bytes calldata data 474 | ) external returns (bytes4); 475 | } 476 | 477 | contract ERC721 is Context, ERC165, IERC721, IERC721Metadata { 478 | using Address for address; 479 | using Strings for uint256; 480 | 481 | // Token name 482 | string private _name; 483 | 484 | // Token symbol 485 | string private _symbol; 486 | 487 | // Mapping from token ID to owner address 488 | mapping(uint256 => address) private _owners; 489 | 490 | // Mapping owner address to token count 491 | mapping(address => uint256) private _balances; 492 | 493 | // Mapping from token ID to approved address 494 | mapping(uint256 => address) private _tokenApprovals; 495 | 496 | // Mapping from owner to operator approvals 497 | mapping(address => mapping(address => bool)) private _operatorApprovals; 498 | 499 | /** 500 | * @dev Initializes the contract by setting a `name` and a `symbol` to the token collection. 501 | */ 502 | constructor(string memory name_, string memory symbol_) { 503 | _name = name_; 504 | _symbol = symbol_; 505 | } 506 | 507 | /** 508 | * @dev See {IERC165-supportsInterface}. 509 | */ 510 | function supportsInterface(bytes4 interfaceId) public view virtual override(IERC165, ERC165) returns (bool) { 511 | return 512 | interfaceId == type(IERC721).interfaceId || 513 | interfaceId == type(IERC721Metadata).interfaceId || 514 | super.supportsInterface(interfaceId); 515 | } 516 | 517 | /** 518 | * @dev See {IERC721-balanceOf}. 519 | */ 520 | function balanceOf(address owner) public view virtual override returns (uint256) { 521 | require(owner != address(0), "ERC721: balance query for the zero address"); 522 | return _balances[owner]; 523 | } 524 | 525 | /** 526 | * @dev See {IERC721-ownerOf}. 527 | */ 528 | function ownerOf(uint256 tokenId) public view virtual override returns (address) { 529 | address owner = _owners[tokenId]; 530 | require(owner != address(0), "ERC721: owner query for nonexistent token"); 531 | return owner; 532 | } 533 | 534 | /** 535 | * @dev See {IERC721Metadata-name}. 536 | */ 537 | function name() public view virtual override returns (string memory) { 538 | return _name; 539 | } 540 | 541 | /** 542 | * @dev See {IERC721Metadata-symbol}. 543 | */ 544 | function symbol() public view virtual override returns (string memory) { 545 | return _symbol; 546 | } 547 | 548 | /** 549 | * @dev See {IERC721Metadata-tokenURI}. 550 | */ 551 | function tokenURI(uint256 tokenId) public view virtual override returns (string memory) { 552 | require(_exists(tokenId), "ERC721Metadata: URI query for nonexistent token"); 553 | 554 | string memory baseURI = _baseURI(); 555 | return bytes(baseURI).length > 0 ? string(abi.encodePacked(baseURI, tokenId.toString())) : ""; 556 | } 557 | 558 | /** 559 | * @dev Base URI for computing {tokenURI}. If set, the resulting URI for each 560 | * token will be the concatenation of the `baseURI` and the `tokenId`. Empty 561 | * by default, can be overriden in child contracts. 562 | */ 563 | function _baseURI() internal view virtual returns (string memory) { 564 | return ""; 565 | } 566 | 567 | /** 568 | * @dev See {IERC721-approve}. 569 | */ 570 | function approve(address to, uint256 tokenId) public virtual override { 571 | address owner = ownerOf(tokenId); 572 | require(to != owner, "ERC721: approval to current owner"); 573 | 574 | require( 575 | _msgSender() == owner || isApprovedForAll(owner, _msgSender()), 576 | "ERC721: approve caller is not owner nor approved for all" 577 | ); 578 | 579 | _approve(to, tokenId); 580 | } 581 | 582 | /** 583 | * @dev See {IERC721-getApproved}. 584 | */ 585 | function getApproved(uint256 tokenId) public view virtual override returns (address) { 586 | require(_exists(tokenId), "ERC721: approved query for nonexistent token"); 587 | 588 | return _tokenApprovals[tokenId]; 589 | } 590 | 591 | /** 592 | * @dev See {IERC721-setApprovalForAll}. 593 | */ 594 | function setApprovalForAll(address operator, bool approved) public virtual override { 595 | require(operator != _msgSender(), "ERC721: approve to caller"); 596 | 597 | _operatorApprovals[_msgSender()][operator] = approved; 598 | emit ApprovalForAll(_msgSender(), operator, approved); 599 | } 600 | 601 | /** 602 | * @dev See {IERC721-isApprovedForAll}. 603 | */ 604 | function isApprovedForAll(address owner, address operator) public view virtual override returns (bool) { 605 | return _operatorApprovals[owner][operator]; 606 | } 607 | 608 | /** 609 | * @dev See {IERC721-transferFrom}. 610 | */ 611 | function transferFrom( 612 | address from, 613 | address to, 614 | uint256 tokenId 615 | ) public virtual override { 616 | //solhint-disable-next-line max-line-length 617 | require(_isApprovedOrOwner(_msgSender(), tokenId), "ERC721: transfer caller is not owner nor approved"); 618 | 619 | _transfer(from, to, tokenId); 620 | } 621 | 622 | /** 623 | * @dev See {IERC721-safeTransferFrom}. 624 | */ 625 | function safeTransferFrom( 626 | address from, 627 | address to, 628 | uint256 tokenId 629 | ) public virtual override { 630 | safeTransferFrom(from, to, tokenId, ""); 631 | } 632 | 633 | /** 634 | * @dev See {IERC721-safeTransferFrom}. 635 | */ 636 | function safeTransferFrom( 637 | address from, 638 | address to, 639 | uint256 tokenId, 640 | bytes memory _data 641 | ) public virtual override { 642 | require(_isApprovedOrOwner(_msgSender(), tokenId), "ERC721: transfer caller is not owner nor approved"); 643 | _safeTransfer(from, to, tokenId, _data); 644 | } 645 | 646 | /** 647 | * @dev Safely transfers `tokenId` token from `from` to `to`, checking first that contract recipients 648 | * are aware of the ERC721 protocol to prevent tokens from being forever locked. 649 | * 650 | * `_data` is additional data, it has no specified format and it is sent in call to `to`. 651 | * 652 | * This internal function is equivalent to {safeTransferFrom}, and can be used to e.g. 653 | * implement alternative mechanisms to perform token transfer, such as signature-based. 654 | * 655 | * Requirements: 656 | * 657 | * - `from` cannot be the zero address. 658 | * - `to` cannot be the zero address. 659 | * - `tokenId` token must exist and be owned by `from`. 660 | * - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon a safe transfer. 661 | * 662 | * Emits a {Transfer} event. 663 | */ 664 | function _safeTransfer( 665 | address from, 666 | address to, 667 | uint256 tokenId, 668 | bytes memory _data 669 | ) internal virtual { 670 | _transfer(from, to, tokenId); 671 | require(_checkOnERC721Received(from, to, tokenId, _data), "ERC721: transfer to non ERC721Receiver implementer"); 672 | } 673 | 674 | /** 675 | * @dev Returns whether `tokenId` exists. 676 | * 677 | * Tokens can be managed by their owner or approved accounts via {approve} or {setApprovalForAll}. 678 | * 679 | * Tokens start existing when they are minted (`_mint`), 680 | * and stop existing when they are burned (`_burn`). 681 | */ 682 | function _exists(uint256 tokenId) internal view virtual returns (bool) { 683 | return _owners[tokenId] != address(0); 684 | } 685 | 686 | /** 687 | * @dev Returns whether `spender` is allowed to manage `tokenId`. 688 | * 689 | * Requirements: 690 | * 691 | * - `tokenId` must exist. 692 | */ 693 | function _isApprovedOrOwner(address spender, uint256 tokenId) internal view virtual returns (bool) { 694 | require(_exists(tokenId), "ERC721: operator query for nonexistent token"); 695 | address owner = ERC721.ownerOf(tokenId); 696 | return (spender == owner || getApproved(tokenId) == spender || isApprovedForAll(owner, spender)); 697 | } 698 | 699 | /** 700 | * @dev Safely mints `tokenId` and transfers it to `to`. 701 | * 702 | * Requirements: 703 | * 704 | * - `tokenId` must not exist. 705 | * - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon a safe transfer. 706 | * 707 | * Emits a {Transfer} event. 708 | */ 709 | function _safeMint(address to, uint256 tokenId) internal virtual { 710 | _safeMint(to, tokenId, ""); 711 | } 712 | 713 | /** 714 | * @dev Same as {xref-ERC721-_safeMint-address-uint256-}[`_safeMint`], with an additional `data` parameter which is 715 | * forwarded in {IERC721Receiver-onERC721Received} to contract recipients. 716 | */ 717 | function _safeMint( 718 | address to, 719 | uint256 tokenId, 720 | bytes memory _data 721 | ) internal virtual { 722 | _mint(to, tokenId); 723 | require( 724 | _checkOnERC721Received(address(0), to, tokenId, _data), 725 | "ERC721: transfer to non ERC721Receiver implementer" 726 | ); 727 | } 728 | 729 | /** 730 | * @dev Mints `tokenId` and transfers it to `to`. 731 | * 732 | * WARNING: Usage of this method is discouraged, use {_safeMint} whenever possible 733 | * 734 | * Requirements: 735 | * 736 | * - `tokenId` must not exist. 737 | * - `to` cannot be the zero address. 738 | * 739 | * Emits a {Transfer} event. 740 | */ 741 | function _mint(address to, uint256 tokenId) internal virtual { 742 | require(to != address(0), "ERC721: mint to the zero address"); 743 | require(!_exists(tokenId), "ERC721: token already minted"); 744 | 745 | _beforeTokenTransfer(address(0), to, tokenId); 746 | 747 | _balances[to] += 1; 748 | _owners[tokenId] = to; 749 | 750 | emit Transfer(address(0), to, tokenId); 751 | } 752 | 753 | /** 754 | * @dev Destroys `tokenId`. 755 | * The approval is cleared when the token is burned. 756 | * 757 | * Requirements: 758 | * 759 | * - `tokenId` must exist. 760 | * 761 | * Emits a {Transfer} event. 762 | */ 763 | function _burn(uint256 tokenId) internal virtual { 764 | address owner = ERC721.ownerOf(tokenId); 765 | 766 | _beforeTokenTransfer(owner, address(0), tokenId); 767 | 768 | // Clear approvals 769 | _approve(address(0), tokenId); 770 | 771 | _balances[owner] -= 1; 772 | delete _owners[tokenId]; 773 | 774 | emit Transfer(owner, address(0), tokenId); 775 | } 776 | 777 | /** 778 | * @dev Transfers `tokenId` from `from` to `to`. 779 | * As opposed to {transferFrom}, this imposes no restrictions on msg.sender. 780 | * 781 | * Requirements: 782 | * 783 | * - `to` cannot be the zero address. 784 | * - `tokenId` token must be owned by `from`. 785 | * 786 | * Emits a {Transfer} event. 787 | */ 788 | function _transfer( 789 | address from, 790 | address to, 791 | uint256 tokenId 792 | ) internal virtual { 793 | require(ERC721.ownerOf(tokenId) == from, "ERC721: transfer of token that is not own"); 794 | require(to != address(0), "ERC721: transfer to the zero address"); 795 | 796 | _beforeTokenTransfer(from, to, tokenId); 797 | 798 | // Clear approvals from the previous owner 799 | _approve(address(0), tokenId); 800 | 801 | _balances[from] -= 1; 802 | _balances[to] += 1; 803 | _owners[tokenId] = to; 804 | 805 | emit Transfer(from, to, tokenId); 806 | } 807 | 808 | /** 809 | * @dev Approve `to` to operate on `tokenId` 810 | * 811 | * Emits a {Approval} event. 812 | */ 813 | function _approve(address to, uint256 tokenId) internal virtual { 814 | _tokenApprovals[tokenId] = to; 815 | emit Approval(ERC721.ownerOf(tokenId), to, tokenId); 816 | } 817 | 818 | /** 819 | * @dev Internal function to invoke {IERC721Receiver-onERC721Received} on a target address. 820 | * The call is not executed if the target address is not a contract. 821 | * 822 | * @param from address representing the previous owner of the given token ID 823 | * @param to target address that will receive the tokens 824 | * @param tokenId uint256 ID of the token to be transferred 825 | * @param _data bytes optional data to send along with the call 826 | * @return bool whether the call correctly returned the expected magic value 827 | */ 828 | function _checkOnERC721Received( 829 | address from, 830 | address to, 831 | uint256 tokenId, 832 | bytes memory _data 833 | ) private returns (bool) { 834 | if (to.isContract()) { 835 | try IERC721Receiver(to).onERC721Received(_msgSender(), from, tokenId, _data) returns (bytes4 retval) { 836 | return retval == IERC721Receiver.onERC721Received.selector; 837 | } catch (bytes memory reason) { 838 | if (reason.length == 0) { 839 | revert("ERC721: transfer to non ERC721Receiver implementer"); 840 | } else { 841 | assembly { 842 | revert(add(32, reason), mload(reason)) 843 | } 844 | } 845 | } 846 | } else { 847 | return true; 848 | } 849 | } 850 | 851 | /** 852 | * @dev Hook that is called before any token transfer. This includes minting 853 | * and burning. 854 | * 855 | * Calling conditions: 856 | * 857 | * - When `from` and `to` are both non-zero, ``from``'s `tokenId` will be 858 | * transferred to `to`. 859 | * - When `from` is zero, `tokenId` will be minted for `to`. 860 | * - When `to` is zero, ``from``'s `tokenId` will be burned. 861 | * - `from` and `to` are never both zero. 862 | * 863 | * To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks]. 864 | */ 865 | function _beforeTokenTransfer( 866 | address from, 867 | address to, 868 | uint256 tokenId 869 | ) internal virtual { 870 | } 871 | } 872 | 873 | pragma solidity ^0.8.0; 874 | 875 | /** 876 | * @dev Standard math utilities missing in the Solidity language. 877 | */ 878 | library Math { 879 | /** 880 | * @dev Returns the largest of two numbers. 881 | */ 882 | function max(uint256 a, uint256 b) internal pure returns (uint256) { 883 | return a >= b ? a : b; 884 | } 885 | 886 | /** 887 | * @dev Returns the smallest of two numbers. 888 | */ 889 | function min(uint256 a, uint256 b) internal pure returns (uint256) { 890 | return a < b ? a : b; 891 | } 892 | 893 | /** 894 | * @dev Returns the average of two numbers. The result is rounded towards 895 | * zero. 896 | */ 897 | function average(uint256 a, uint256 b) internal pure returns (uint256) { 898 | // (a + b) / 2 can overflow. 899 | return (a & b) + (a ^ b) / 2; 900 | } 901 | 902 | /** 903 | * @dev Returns the ceiling of the division of two numbers. 904 | * 905 | * This differs from standard division with `/` in that it rounds up instead 906 | * of rounding down. 907 | */ 908 | function ceilDiv(uint256 a, uint256 b) internal pure returns (uint256) { 909 | // (a + b - 1) / b can overflow on addition, so we distribute. 910 | return a / b + (a % b == 0 ? 0 : 1); 911 | } 912 | } 913 | abstract contract ERC721URIStorage is ERC721 { 914 | using Strings for uint256; 915 | 916 | // Optional mapping for token URIs 917 | mapping(uint256 => string) private _tokenURIs; 918 | 919 | /** 920 | * @dev See {IERC721Metadata-tokenURI}. 921 | */ 922 | function tokenURI(uint256 tokenId) public view virtual override returns (string memory) { 923 | require(_exists(tokenId), "ERC721URIStorage: URI query for nonexistent token"); 924 | 925 | string memory _tokenURI = _tokenURIs[tokenId]; 926 | string memory base = _baseURI(); 927 | 928 | // If there is no base URI, return the token URI. 929 | if (bytes(base).length == 0) { 930 | return _tokenURI; 931 | } 932 | // If both are set, concatenate the baseURI and tokenURI (via abi.encodePacked). 933 | if (bytes(_tokenURI).length > 0) { 934 | return string(abi.encodePacked(base, _tokenURI)); 935 | } 936 | 937 | return super.tokenURI(tokenId); 938 | } 939 | 940 | /** 941 | * @dev Sets `_tokenURI` as the tokenURI of `tokenId`. 942 | * 943 | * Requirements: 944 | * 945 | * - `tokenId` must exist. 946 | */ 947 | function _setTokenURI(uint256 tokenId, string memory _tokenURI) internal virtual { 948 | require(_exists(tokenId), "ERC721URIStorage: URI set of nonexistent token"); 949 | _tokenURIs[tokenId] = _tokenURI; 950 | } 951 | 952 | /** 953 | * @dev Destroys `tokenId`. 954 | * The approval is cleared when the token is burned. 955 | * 956 | * Requirements: 957 | * 958 | * - `tokenId` must exist. 959 | * 960 | * Emits a {Transfer} event. 961 | */ 962 | function _burn(uint256 tokenId) internal virtual override { 963 | super._burn(tokenId); 964 | 965 | if (bytes(_tokenURIs[tokenId]).length != 0) { 966 | delete _tokenURIs[tokenId]; 967 | } 968 | } 969 | } 970 | 971 | 972 | pragma solidity ^0.8.0; 973 | 974 | /** 975 | * @title Counters 976 | * @author Matt Condon (@shrugs) 977 | * @dev Provides counters that can only be incremented, decremented or reset. This can be used e.g. to track the number 978 | * of elements in a mapping, issuing ERC721 ids, or counting request ids. 979 | * 980 | * Include with `using Counters for Counters.Counter;` 981 | */ 982 | library Counters { 983 | struct Counter { 984 | // This variable should never be directly accessed by users of the library: interactions must be restricted to 985 | // the library's function. As of Solidity v0.5.2, this cannot be enforced, though there is a proposal to add 986 | // this feature: see https://github.com/ethereum/solidity/issues/4637 987 | uint256 _value; // default: 0 988 | } 989 | 990 | function current(Counter storage counter) internal view returns (uint256) { 991 | return counter._value; 992 | } 993 | 994 | function increment(Counter storage counter) internal { 995 | unchecked { 996 | counter._value += 1; 997 | } 998 | } 999 | 1000 | function decrement(Counter storage counter) internal { 1001 | uint256 value = counter._value; 1002 | require(value > 0, "Counter: decrement overflow"); 1003 | unchecked { 1004 | counter._value = value - 1; 1005 | } 1006 | } 1007 | 1008 | function reset(Counter storage counter) internal { 1009 | counter._value = 0; 1010 | } 1011 | } 1012 | 1013 | abstract contract Ownable is Context { 1014 | address private _owner; 1015 | 1016 | event OwnershipTransferred(address indexed previousOwner, address indexed newOwner); 1017 | 1018 | /** 1019 | * @dev Initializes the contract setting the deployer as the initial owner. 1020 | */ 1021 | constructor() { 1022 | _setOwner(_msgSender()); 1023 | } 1024 | 1025 | /** 1026 | * @dev Returns the address of the current owner. 1027 | */ 1028 | function owner() public view virtual returns (address) { 1029 | return _owner; 1030 | } 1031 | 1032 | /** 1033 | * @dev Throws if called by any account other than the owner. 1034 | */ 1035 | modifier onlyOwner() { 1036 | require(owner() == _msgSender(), "Ownable: caller is not the owner"); 1037 | _; 1038 | } 1039 | 1040 | /** 1041 | * @dev Leaves the contract without owner. It will not be possible to call 1042 | * `onlyOwner` functions anymore. Can only be called by the current owner. 1043 | * 1044 | * NOTE: Renouncing ownership will leave the contract without an owner, 1045 | * thereby removing any functionality that is only available to the owner. 1046 | */ 1047 | function renounceOwnership() public virtual onlyOwner { 1048 | _setOwner(address(0)); 1049 | } 1050 | 1051 | /** 1052 | * @dev Transfers ownership of the contract to a new account (`newOwner`). 1053 | * Can only be called by the current owner. 1054 | */ 1055 | function transferOwnership(address newOwner) public virtual onlyOwner { 1056 | require(newOwner != address(0), "Ownable: new owner is the zero address"); 1057 | _setOwner(newOwner); 1058 | } 1059 | 1060 | function _setOwner(address newOwner) private { 1061 | address oldOwner = _owner; 1062 | _owner = newOwner; 1063 | emit OwnershipTransferred(oldOwner, newOwner); 1064 | } 1065 | } 1066 | contract WolfNFT is ERC721URIStorage, Ownable { 1067 | using Counters for Counters.Counter; 1068 | Counters.Counter private _tokenIds; 1069 | 1070 | constructor() ERC721("WolfNFT", "WFT") { 1071 | 1072 | } 1073 | 1074 | function setTokenURI(uint256 tokenId, string memory _tokenURI) public { 1075 | super._setTokenURI(tokenId, _tokenURI); 1076 | } 1077 | 1078 | function mint(address recipient, string memory tokenURI) public onlyOwner returns (uint256) { 1079 | _tokenIds.increment(); 1080 | 1081 | uint256 nftId = _tokenIds.current(); 1082 | _mint(recipient, nftId); 1083 | setTokenURI(nftId, tokenURI); 1084 | 1085 | return nftId; 1086 | } 1087 | } --------------------------------------------------------------------------------