├── .gitignore ├── metadata ├── images │ ├── cryptobeetle_1.gif │ ├── cryptobeetle_2.gif │ └── cryptobeetle_3.gif ├── cryptobeetle_2.json ├── cryptobeetle_1.json └── cryptobeetle_3.json ├── README.md ├── package.json ├── scripts ├── deploy.js └── mint.js ├── test └── sample-test.js ├── contracts └── CryptoBeetles.sol └── hardhat.config.js /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | .env 3 | coverage 4 | coverage.json 5 | typechain 6 | 7 | #Hardhat files 8 | cache 9 | artifacts 10 | -------------------------------------------------------------------------------- /metadata/images/cryptobeetle_1.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jspruance/erc-721-nft-contract-tutorial/main/metadata/images/cryptobeetle_1.gif -------------------------------------------------------------------------------- /metadata/images/cryptobeetle_2.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jspruance/erc-721-nft-contract-tutorial/main/metadata/images/cryptobeetle_2.gif -------------------------------------------------------------------------------- /metadata/images/cryptobeetle_3.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jspruance/erc-721-nft-contract-tutorial/main/metadata/images/cryptobeetle_3.gif -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # erc-721-nft-contract-tutorial 2 | This is the source code for the ERC-721 project from my Block Explorer YouTube channel. 3 | https://www.youtube.com/c/BlockExplorerMedia 4 | -------------------------------------------------------------------------------- /metadata/cryptobeetle_2.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Crypto Beetle 2", 3 | "description": "Crypto Beetle", 4 | "image": "https://ipfs.io/ipfs/QmPCyMfKzCUz97exZAyWZJRQXPqfAQN4EbVUSxSWKLCWaA", 5 | "attributes": [{ 6 | "trait_type": "Generation", 7 | "value": 1 8 | }, 9 | { 10 | "trait_type": "Dots", 11 | "value": 6 12 | }, 13 | { 14 | "trait_type": "Antenna", 15 | "value": "large" 16 | }, 17 | { 18 | "trait_type": "Background", 19 | "value": "space" 20 | } 21 | ] 22 | } -------------------------------------------------------------------------------- /metadata/cryptobeetle_1.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Crypto Beetle 1", 3 | "description": "Crypto Beetle", 4 | "image": "https://ipfs.io/ipfs/QmbB8vrz117iixtkgNg4UxFqMuvHczNRgmrhXosCxyypJv", 5 | "attributes": [{ 6 | "trait_type": "Generation", 7 | "value": 1 8 | }, 9 | { 10 | "trait_type": "Dots", 11 | "value": 4 12 | }, 13 | { 14 | "trait_type": "Antenna", 15 | "value": "regular" 16 | }, 17 | { 18 | "trait_type": "Background", 19 | "value": "space" 20 | } 21 | ] 22 | } -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "nft-smart-contract", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "author": "", 10 | "license": "ISC", 11 | "devDependencies": { 12 | "@nomiclabs/hardhat-ethers": "^2.0.0", 13 | "@nomiclabs/hardhat-waffle": "^2.0.0", 14 | "chai": "^4.2.0", 15 | "ethereum-waffle": "^3.0.0", 16 | "ethers": "^5.0.0", 17 | "hardhat": "^2.8.2" 18 | }, 19 | "dependencies": { 20 | "@openzeppelin/contracts": "^4.4.2", 21 | "dotenv": "^12.0.3" 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /metadata/cryptobeetle_3.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Crypto Beetle 3", 3 | "description": "Crypto Beetle", 4 | "image": "https://ipfs.io/ipfs/QmP2TZTNAqNaxGq9gKxG5o3QiPpmtVWr4HDqafd71rChbC", 5 | "attributes": [{ 6 | "trait_type": "Generation", 7 | "value": 1 8 | }, 9 | { 10 | "trait_type": "Dots", 11 | "value": 4 12 | }, 13 | { 14 | "trait_type": "Antenna", 15 | "value": "large" 16 | }, 17 | { 18 | "trait_type": "Background", 19 | "value": "clouds" 20 | }, 21 | { 22 | "trait_type": "Sunglasses", 23 | "value": true 24 | } 25 | ] 26 | } -------------------------------------------------------------------------------- /scripts/deploy.js: -------------------------------------------------------------------------------- 1 | const { ethers } = require("hardhat") 2 | 3 | async function main() { 4 | const CryptoBeetles = await ethers.getContractFactory("CryptoBeetles") 5 | const cryptoBeetles = await CryptoBeetles.deploy("CryptoBeetles", "CBEET") 6 | 7 | await cryptoBeetles.deployed() 8 | console.log(`Contract successfully deployed to ${cryptoBeetles.address}`) 9 | 10 | const newItemId = await cryptoBeetles.mint("https://ipfs.io/ipfs/QmSgF5GanmEvZjQ3VYj9r83kvp5zeXe9UcihHxK4PKVzJ2") 11 | console.log(`NFT minted successfully :: ${newItemId}`) 12 | } 13 | 14 | main() 15 | .then(() => process.exit(0)) 16 | .catch((error) => { 17 | console.error(error); 18 | process.exit(1); 19 | }); 20 | -------------------------------------------------------------------------------- /test/sample-test.js: -------------------------------------------------------------------------------- 1 | const { expect } = require("chai"); 2 | const { ethers } = require("hardhat"); 3 | 4 | describe("Greeter", function () { 5 | it("Should return the new greeting once it's changed", async function () { 6 | const Greeter = await ethers.getContractFactory("Greeter"); 7 | const greeter = await Greeter.deploy("Hello, world!"); 8 | await greeter.deployed(); 9 | 10 | expect(await greeter.greet()).to.equal("Hello, world!"); 11 | 12 | const setGreetingTx = await greeter.setGreeting("Hola, mundo!"); 13 | 14 | // wait until the transaction is mined 15 | await setGreetingTx.wait(); 16 | 17 | expect(await greeter.greet()).to.equal("Hola, mundo!"); 18 | }); 19 | }); 20 | -------------------------------------------------------------------------------- /scripts/mint.js: -------------------------------------------------------------------------------- 1 | const { ethers } = require("hardhat") 2 | const cryptoBeetlesJSON = require("../artifacts/contracts/CryptoBeetles.sol/CryptoBeetles.json") 3 | 4 | async function main() { 5 | const abi = cryptoBeetlesJSON.abi 6 | const provider = new ethers.providers.InfuraProvider("rinkeby", process.env.PROJECT_ID) 7 | const wallet = new ethers.Wallet(process.env.PRIVATE_KEY, provider) 8 | const signer = wallet.connect(provider) 9 | const cryptoBeetles = new ethers.Contract(process.env.CONTRACT_ADDRESS, abi, signer) 10 | await cryptoBeetles.mint("https://ipfs.io/ipfs/QmRGNeFvupSSq96WxoYeRAjuvGpYQxdjpLXkBBHgX3pFM6") 11 | console.log('NFT minted!') 12 | } 13 | 14 | main() 15 | .then(() => process.exit(0)) 16 | .catch((error) => { 17 | console.error(error); 18 | process.exit(1); 19 | }); 20 | -------------------------------------------------------------------------------- /contracts/CryptoBeetles.sol: -------------------------------------------------------------------------------- 1 | // contracts/CryptoBeetles.sol 2 | // SPDX-License-Identifier: MIT 3 | pragma solidity ^0.8.11; 4 | 5 | import "@openzeppelin/contracts/token/ERC721/extensions/ERC721URIStorage.sol"; 6 | import "@openzeppelin/contracts/utils/Counters.sol"; 7 | 8 | contract CryptoBeetles is ERC721URIStorage { 9 | using Counters for Counters.Counter; 10 | Counters.Counter private _tokenIds; 11 | 12 | constructor(string memory _name, string memory _symbol) ERC721(_name, _symbol) {} 13 | 14 | function mint(string memory tokenURI) 15 | public 16 | returns (uint256) 17 | { 18 | _tokenIds.increment(); 19 | 20 | uint256 newItemId = _tokenIds.current(); 21 | _mint(msg.sender, newItemId); 22 | _setTokenURI(newItemId, tokenURI); 23 | 24 | return newItemId; 25 | } 26 | } -------------------------------------------------------------------------------- /hardhat.config.js: -------------------------------------------------------------------------------- 1 | require("@nomiclabs/hardhat-waffle"); 2 | require("dotenv").config(); 3 | 4 | // This is a sample Hardhat task. To learn how to create your own go to 5 | // https://hardhat.org/guides/create-task.html 6 | task("accounts", "Prints the list of accounts", async (taskArgs, hre) => { 7 | const accounts = await hre.ethers.getSigners(); 8 | 9 | for (const account of accounts) { 10 | console.log(account.address); 11 | } 12 | }); 13 | 14 | // You need to export an object to set up your config 15 | // Go to https://hardhat.org/config/ to learn more 16 | 17 | /** 18 | * @type import('hardhat/config').HardhatUserConfig 19 | */ 20 | module.exports = { 21 | solidity: "0.8.11", 22 | networks: { 23 | mumbai: { 24 | url: process.env.MUMBAI_URL, 25 | accounts: [process.env.PRIVATE_KEY] 26 | }, 27 | rinkeby: { 28 | url: process.env.RINKEBY_URL, 29 | accounts: [process.env.PRIVATE_KEY] 30 | } 31 | } 32 | }; 33 | --------------------------------------------------------------------------------