├── img ├── 0.png ├── 1.png ├── 2.png ├── 3.png ├── 4.png ├── 5.png ├── 6.png ├── 7.png ├── 8.png ├── 9.png ├── placeholder.png ├── 8.json ├── 0.json ├── 2.json ├── 3.json ├── 4.json ├── 5.json ├── 6.json ├── 7.json ├── 9.json ├── 1.json ├── placeholder.svg ├── 6.svg ├── 4.svg ├── 0.svg ├── 3.svg ├── 9.svg ├── 1.svg ├── 5.svg └── 7.svg ├── vite.config.js ├── .gitignore ├── src ├── App.jsx ├── main.jsx ├── components │ ├── Install.jsx │ ├── WalletBalance.jsx │ └── Home.jsx ├── index.css ├── App.css ├── favicon.svg └── logo.svg ├── package.json ├── index.html ├── README.md ├── hardhat.config.js ├── test └── sample-test.js ├── scripts └── sample-script.js └── contracts └── MyNFT.sol /img/0.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fireship-io/web3-nft-dapp-tutorial/HEAD/img/0.png -------------------------------------------------------------------------------- /img/1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fireship-io/web3-nft-dapp-tutorial/HEAD/img/1.png -------------------------------------------------------------------------------- /img/2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fireship-io/web3-nft-dapp-tutorial/HEAD/img/2.png -------------------------------------------------------------------------------- /img/3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fireship-io/web3-nft-dapp-tutorial/HEAD/img/3.png -------------------------------------------------------------------------------- /img/4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fireship-io/web3-nft-dapp-tutorial/HEAD/img/4.png -------------------------------------------------------------------------------- /img/5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fireship-io/web3-nft-dapp-tutorial/HEAD/img/5.png -------------------------------------------------------------------------------- /img/6.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fireship-io/web3-nft-dapp-tutorial/HEAD/img/6.png -------------------------------------------------------------------------------- /img/7.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fireship-io/web3-nft-dapp-tutorial/HEAD/img/7.png -------------------------------------------------------------------------------- /img/8.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fireship-io/web3-nft-dapp-tutorial/HEAD/img/8.png -------------------------------------------------------------------------------- /img/9.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fireship-io/web3-nft-dapp-tutorial/HEAD/img/9.png -------------------------------------------------------------------------------- /img/placeholder.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fireship-io/web3-nft-dapp-tutorial/HEAD/img/placeholder.png -------------------------------------------------------------------------------- /img/8.json: -------------------------------------------------------------------------------- 1 | {"name":"buff-ivan","description":"A drawing of buff ivan","image":"ipfs://YOUR_ASSET_CID","attributes":[{"beard":"","rarity":0.5}]} -------------------------------------------------------------------------------- /img/0.json: -------------------------------------------------------------------------------- 1 | {"name":"sloppy-steve","description":"A drawing of sloppy steve","image":"ipfs://YOUR_ASSET_CID","attributes":[{"beard":"","rarity":0.5}]} -------------------------------------------------------------------------------- /img/2.json: -------------------------------------------------------------------------------- 1 | {"name":"shady-randy","description":"A drawing of shady randy","image":"ipfs://YOUR_ASSET_CID","attributes":[{"beard":"","rarity":0.5}]} -------------------------------------------------------------------------------- /img/3.json: -------------------------------------------------------------------------------- 1 | {"name":"sloppy-fred","description":"A drawing of sloppy fred","image":"ipfs://YOUR_ASSET_CID","attributes":[{"beard":"","rarity":0.5}]} -------------------------------------------------------------------------------- /img/4.json: -------------------------------------------------------------------------------- 1 | {"name":"killer-fred","description":"A drawing of killer fred","image":"ipfs://YOUR_ASSET_CID","attributes":[{"beard":"","rarity":0.5}]} -------------------------------------------------------------------------------- /img/5.json: -------------------------------------------------------------------------------- 1 | {"name":"hot-quinten","description":"A drawing of hot quinten","image":"ipfs://YOUR_ASSET_CID","attributes":[{"beard":"","rarity":0.5}]} -------------------------------------------------------------------------------- /img/6.json: -------------------------------------------------------------------------------- 1 | {"name":"mangy-kyle","description":"A drawing of mangy kyle","image":"ipfs://YOUR_ASSET_CID","attributes":[{"beard":"","rarity":0.5}]} -------------------------------------------------------------------------------- /img/7.json: -------------------------------------------------------------------------------- 1 | {"name":"mangy-plop","description":"A drawing of mangy plop","image":"ipfs://YOUR_ASSET_CID","attributes":[{"beard":"","rarity":0.5}]} -------------------------------------------------------------------------------- /img/9.json: -------------------------------------------------------------------------------- 1 | {"name":"harry-tevin","description":"A drawing of harry tevin","image":"ipfs://YOUR_ASSET_CID","attributes":[{"beard":"","rarity":0.5}]} -------------------------------------------------------------------------------- /img/1.json: -------------------------------------------------------------------------------- 1 | {"name":"sinful-xavier","description":"A drawing of sinful xavier","image":"ipfs://YOUR_ASSET_CID","attributes":[{"beard":"","rarity":0.5}]} -------------------------------------------------------------------------------- /vite.config.js: -------------------------------------------------------------------------------- 1 | import { defineConfig } from 'vite' 2 | import react from '@vitejs/plugin-react' 3 | 4 | // https://vitejs.dev/config/ 5 | export default defineConfig({ 6 | plugins: [react()] 7 | }) 8 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | .DS_Store 3 | dist 4 | dist-ssr 5 | *.local 6 | node_modules 7 | .env 8 | coverage 9 | coverage.json 10 | typechain 11 | 12 | #Hardhat files 13 | cache 14 | artifacts 15 | -------------------------------------------------------------------------------- /src/App.jsx: -------------------------------------------------------------------------------- 1 | import Install from './components/Install'; 2 | import Home from './components/Home'; 3 | 4 | function App() { 5 | 6 | if (window.ethereum) { 7 | return ; 8 | } else { 9 | return 10 | } 11 | } 12 | 13 | export default App; -------------------------------------------------------------------------------- /src/main.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import ReactDOM from 'react-dom' 3 | import './index.css' 4 | import App from './App' 5 | 6 | ReactDOM.render( 7 | 8 | 9 | , 10 | document.getElementById('root') 11 | ) 12 | -------------------------------------------------------------------------------- /src/components/Install.jsx: -------------------------------------------------------------------------------- 1 | const Install = () => { 2 | return ( 3 |
4 |

Follow the link to install 👇🏼

5 | Meta Mask 6 |
7 | ); 8 | }; 9 | 10 | export default Install; 11 | -------------------------------------------------------------------------------- /src/index.css: -------------------------------------------------------------------------------- 1 | body { 2 | margin: 0; 3 | font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen', 4 | 'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue', 5 | sans-serif; 6 | -webkit-font-smoothing: antialiased; 7 | -moz-osx-font-smoothing: grayscale; 8 | } 9 | 10 | code { 11 | font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New', 12 | monospace; 13 | } 14 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "web3app", 3 | "version": "0.0.0", 4 | "scripts": { 5 | "dev": "vite", 6 | "build": "vite build", 7 | "preview": "vite preview" 8 | }, 9 | "dependencies": { 10 | "react": "^17.0.2", 11 | "react-dom": "^17.0.2" 12 | }, 13 | "devDependencies": { 14 | "@nomiclabs/hardhat-ethers": "^2.0.4", 15 | "@nomiclabs/hardhat-waffle": "^2.0.1", 16 | "@openzeppelin/contracts": "^4.4.2", 17 | "@vitejs/plugin-react": "^1.0.7", 18 | "chai": "^4.3.4", 19 | "ethereum-waffle": "^3.4.0", 20 | "ethers": "^5.5.3", 21 | "vite": "^2.7.2" 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | Vite App 10 | 11 | 12 |
13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Web3 - Full Tutorial 2 | 3 | The demo contains a basic web3 app and smart contract for minting NFTs. 4 | 5 | - See it in action in the [Web3 NFT Tutorial](https://youtu.be/meTpMP0J5E8) on YouTube. 6 | - Follow the full [Web3 Tutorial](https://fireship.io/lessons/web3-solidity-hardhat-react-tutorial) on Fireship. 7 | 8 | ## Usage 9 | 10 | ```bash 11 | git clone 12 | npm install 13 | 14 | # terminal 1 15 | npx hardhat node 16 | 17 | # terminal 2 18 | npx hardhat compile 19 | npx hardhat run scripts/sample-script.js --network localhost 20 | 21 | # terminal 3 22 | npm run dev 23 | ``` 24 | 25 | Update the deployed contract address in `compoonents/Home.js` -------------------------------------------------------------------------------- /src/App.css: -------------------------------------------------------------------------------- 1 | .App { 2 | text-align: center; 3 | } 4 | 5 | .App-logo { 6 | height: 40vmin; 7 | pointer-events: none; 8 | } 9 | 10 | @media (prefers-reduced-motion: no-preference) { 11 | .App-logo { 12 | animation: App-logo-spin infinite 20s linear; 13 | } 14 | } 15 | 16 | .App-header { 17 | background-color: #282c34; 18 | min-height: 100vh; 19 | display: flex; 20 | flex-direction: column; 21 | align-items: center; 22 | justify-content: center; 23 | font-size: calc(10px + 2vmin); 24 | color: white; 25 | } 26 | 27 | .App-link { 28 | color: #61dafb; 29 | } 30 | 31 | @keyframes App-logo-spin { 32 | from { 33 | transform: rotate(0deg); 34 | } 35 | to { 36 | transform: rotate(360deg); 37 | } 38 | } 39 | 40 | button { 41 | font-size: calc(10px + 2vmin); 42 | } 43 | -------------------------------------------------------------------------------- /hardhat.config.js: -------------------------------------------------------------------------------- 1 | require("@nomiclabs/hardhat-waffle"); 2 | 3 | // This is a sample Hardhat task. To learn how to create your own go to 4 | // https://hardhat.org/guides/create-task.html 5 | task("accounts", "Prints the list of accounts", async (taskArgs, hre) => { 6 | const accounts = await hre.ethers.getSigners(); 7 | 8 | for (const account of accounts) { 9 | console.log(account.address); 10 | } 11 | }); 12 | 13 | // You need to export an object to set up your config 14 | // Go to https://hardhat.org/config/ to learn more 15 | 16 | /** 17 | * @type import('hardhat/config').HardhatUserConfig 18 | */ 19 | module.exports = { 20 | solidity: "0.8.4", 21 | paths: { 22 | artifacts: './src/artifacts', 23 | }, 24 | networks: { 25 | matic: { 26 | url: "https://polygon-mumbai.g.alchemy.com/v2/YOUR_APP", 27 | accounts: ["MATIC_PRIVATE_KEY"] 28 | } 29 | }, 30 | }; 31 | -------------------------------------------------------------------------------- /src/components/WalletBalance.jsx: -------------------------------------------------------------------------------- 1 | import { useState } from 'react'; 2 | import { ethers } from 'ethers'; 3 | 4 | function WalletBalance() { 5 | 6 | const [balance, setBalance] = useState(); 7 | 8 | const getBalance = async () => { 9 | const [account] = await window.ethereum.request({ method: 'eth_requestAccounts' }); 10 | const provider = new ethers.providers.Web3Provider(window.ethereum); 11 | const balance = await provider.getBalance(account); 12 | setBalance(ethers.utils.formatEther(balance)); 13 | }; 14 | 15 | return ( 16 |
17 |
18 |
Your Balance: {balance}
19 | 20 |
21 |
22 | ); 23 | }; 24 | 25 | export default WalletBalance; -------------------------------------------------------------------------------- /img/placeholder.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /test/sample-test.js: -------------------------------------------------------------------------------- 1 | const { expect } = require("chai"); 2 | const { ethers } = require("hardhat"); 3 | 4 | describe("MyNFT", function () { 5 | it("Should mint and transfer an NFT to someone", async function () { 6 | const FiredGuys = await ethers.getContractFactory("FiredGuys"); 7 | const firedguys = await FiredGuys.deploy(); 8 | await firedguys.deployed(); 9 | 10 | const recipient = '0xf39fd6e51aad88f6f4ce6ab8827279cfffb92266'; 11 | const metadataURI = 'cid/test.png'; 12 | 13 | let balance = await firedguys.balanceOf(recipient); 14 | expect(balance).to.equal(0); 15 | 16 | const newlyMintedToken = await firedguys.payToMint(recipient, metadataURI, { value: ethers.utils.parseEther('0.05') }); 17 | 18 | // wait until the transaction is mined 19 | await newlyMintedToken.wait(); 20 | 21 | balance = await firedguys.balanceOf(recipient) 22 | expect(balance).to.equal(1); 23 | 24 | expect(await firedguys.isContentOwned(metadataURI)).to.equal(true); 25 | const newlyMintedToken2 = await firedguys.payToMint(recipient, 'foo', { value: ethers.utils.parseEther('0.05') }); 26 | }); 27 | }); 28 | -------------------------------------------------------------------------------- /scripts/sample-script.js: -------------------------------------------------------------------------------- 1 | // We require the Hardhat Runtime Environment explicitly here. This is optional 2 | // but useful for running the script in a standalone fashion through `node