From ab3f959d5465145e78951cd7d5e0d6323df9f654 Mon Sep 17 00:00:00 2001 From: Damian Date: Fri, 18 Oct 2024 10:48:54 -0300 Subject: [PATCH 1/6] ERC-721 extension --- README.md | 33 ++--- extension/README.md.args.mjs | 19 +++ .../packages/foundry/contracts/SE2NFT.sol | 48 +++++++ .../foundry/script/Deploy.s.sol.args.mjs | 5 + .../foundry/script/DeploySE2Nft.s.sol | 14 ++ .../packages/hardhat/contracts/SE2NFT.sol | 51 +++++++ .../hardhat/deploy/01_deploy_se2_nft.ts | 38 +++++ .../nextjs/app/erc721/components/AllNfts.tsx | 92 ++++++++++++ .../nextjs/app/erc721/components/MyNfts.tsx | 93 ++++++++++++ .../nextjs/app/erc721/components/NFTCard.tsx | 60 ++++++++ extension/packages/nextjs/app/erc721/page.tsx | 136 ++++++++++++++++++ .../nextjs/components/Header.tsx.args.mjs | 6 + 12 files changed, 577 insertions(+), 18 deletions(-) create mode 100644 extension/README.md.args.mjs create mode 100644 extension/packages/foundry/contracts/SE2NFT.sol create mode 100644 extension/packages/foundry/script/Deploy.s.sol.args.mjs create mode 100644 extension/packages/foundry/script/DeploySE2Nft.s.sol create mode 100644 extension/packages/hardhat/contracts/SE2NFT.sol create mode 100644 extension/packages/hardhat/deploy/01_deploy_se2_nft.ts create mode 100644 extension/packages/nextjs/app/erc721/components/AllNfts.tsx create mode 100644 extension/packages/nextjs/app/erc721/components/MyNfts.tsx create mode 100644 extension/packages/nextjs/app/erc721/components/NFTCard.tsx create mode 100644 extension/packages/nextjs/app/erc721/page.tsx create mode 100644 extension/packages/nextjs/components/Header.tsx.args.mjs diff --git a/README.md b/README.md index fac6319..6dcf3c5 100644 --- a/README.md +++ b/README.md @@ -1,30 +1,27 @@ -# 🔌 create-eth Extensions +# ERC-721 NFT Extension for Scaffold-ETH 2 -This repository holds all the BG curated extensions for [create-eth](https://github.com/scaffold-eth/create-eth), so you can extend the functionality of your Scaffold-ETH project. +This extension introduces an ERC-721 token contract and demonstrates how to use it, including getting the total supply and holder balance, listing all NFTs and NFTs from an address, and how to transfer. -## Usage +The ERC-721 Token Standard introduces a standard for Non-Fungible Tokens ([EIP-721](https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/token/ERC721/ERC721.sol)), in other words, each Token is unique. + +The ERC-721 token contract is implemented using the [ERC-721 token implementation](https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/token/ERC721/ERC721.sol) from OpenZeppelin. + +The ERC-721 token implementation uses the [ERC-721 Enumerable extension](https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/token/ERC721/extensions/ERC721Enumerable.sol) from OpenZeppelin to list all tokens and all the tokens owned by an address. You can remove this if you plan to use an indexer, like a Subgraph or Ponder (extensions available). + +## Installation You can install any of the extensions in this repository by running the following command: ```bash -npx create-eth@latest -e +npx create-eth@latest -e erc-721 ``` -## Available Extensions +## 🚀 Setup extension -- [subgraph](https://github.com/scaffold-eth/create-eth-extensions/tree/subgraph): This Scaffold-ETH 2 extension helps you build and test subgraphs locally for your contracts. It also enables interaction with the front-end and facilitates easy deployment to Subgraph Studio. -- [eip-712](https://github.com/scaffold-eth/create-eth-extensions/tree/eip-712): An implementation of EIP-712, allowing you to send, sign, and verify typed messages in a user-friendly manner. -- [ponder](https://github.com/scaffold-eth/create-eth-extensions/tree/ponder): This Scaffold-ETH 2 extension comes pre-configured with [ponder.sh](https://ponder.sh), providing an example to help you get started quickly. -- [onchainkit](https://github.com/scaffold-eth/create-eth-extensions/tree/onchainkit): This Scaffold-ETH 2 extension comes pre-configured with [onchainkit](https://onchainkit.xyz/), providing an example to help you get started quickly. -- [erc-20](https://github.com/scaffold-eth/create-eth-extensions/tree/erc-20): This extension introduces an ERC-20 token contract and demonstrates how to interact with it, including getting a holder balance and transferring tokens. -- [eip-5792](https://github.com/scaffold-eth/create-eth-extensions/tree/eip-5792): This extension shows how to use [EIP-5792](https://eips.ethereum.org/EIPS/eip-5792) wallet capabilities. It comes with an example frontend interaction with the `EIP5792_Example.sol` contract. The code demonstrates how to make a batched transaction that sets new greetings and increments the counter in a single transaction using Wagmi's experimental hooks and Coinbase smart wallet. +Deploy your contract running `yarn deploy` -## Create your own extension +## Interact with the NFT -You can extend Scaffold-ETH by creating your own extension. To do so, you need to create a new repository with the following structure: +Start the front-end with `yarn start` and go to the _/erc721_ page to interact with your deployed ERC-721 token. -`ToDo` - -```bash -npx create-eth@latest -e your-github-username/your-extension-repository:branch-name # branch-name is optional -``` +You can check the code at `packages/nextjs/app/erc721`. diff --git a/extension/README.md.args.mjs b/extension/README.md.args.mjs new file mode 100644 index 0000000..6f31b23 --- /dev/null +++ b/extension/README.md.args.mjs @@ -0,0 +1,19 @@ +export const extraContents = `## 🚀 Setup ERC-721 NFT Extension + +This extension introduces an ERC-721 token contract and demonstrates how to use it, including getting the total supply and holder balance, listing all NFTs and NFTs from an address, and how to transfer. + +The ERC-721 Token Standard introduces a standard for Non-Fungible Tokens ([EIP-721](https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/token/ERC721/ERC721.sol)), in other words, each Token is unique. + +The ERC-721 token contract is implemented using the [ERC-721 token implementation](https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/token/ERC721/ERC721.sol) from OpenZeppelin. + +### Setup + +Deploy your contract running \`\`\`yarn deploy\`\`\` + +### Interact with the NFT + +Start the front-end with \`\`\`yarn start\`\`\` and go to the _/erc721_ page to interact with your deployed ERC-721 token. + +You can check the code at \`\`\`packages/nextjs/app/erc721\`\`\`. + +`; diff --git a/extension/packages/foundry/contracts/SE2NFT.sol b/extension/packages/foundry/contracts/SE2NFT.sol new file mode 100644 index 0000000..43f0c80 --- /dev/null +++ b/extension/packages/foundry/contracts/SE2NFT.sol @@ -0,0 +1,48 @@ +// SPDX-License-Identifier: MIT +pragma solidity >=0.8.0 <0.9.0; + +import "@openzeppelin/contracts/token/ERC721/ERC721.sol"; +import "@openzeppelin/contracts/token/ERC721/extensions/ERC721Enumerable.sol"; + +contract SE2NFT is ERC721Enumerable { + uint256 public tokenIdCounter; + mapping(uint256 tokenId => string) public tokenURIs; + string[] public uris = [ + "QmVHi3c4qkZcH3cJynzDXRm5n7dzc9R9TUtUcfnWQvhdcw", // Zebra + "QmfVMAmNM1kDEBYrC2TPzQDoCRFH6F5tE1e9Mr4FkkR5Xr", // Buffalo + "QmcvcUaKf6JyCXhLD1by6hJXNruPQGs3kkLg2W1xr7nF1j" // Rhino + ]; + + constructor() ERC721("SE2-ERC721-NFT", "SE2NFT") { } + + function _baseURI() internal pure override returns (string memory) { + return "https://ipfs.io/ipfs/"; + } + + function mintItem( + address to + ) public returns (uint256) { + tokenIdCounter++; + _safeMint(to, tokenIdCounter); + + bytes32 predictableRandom = keccak256( + abi.encodePacked( + tokenIdCounter, blockhash(block.number - 1), msg.sender, address(this) + ) + ); + + tokenURIs[tokenIdCounter] = uris[uint256(predictableRandom) % uris.length]; + return tokenIdCounter; + } + + function tokenURI( + uint256 tokenId + ) public view override returns (string memory) { + _requireOwned(tokenId); + + string memory _tokenURI = tokenURIs[tokenId]; + string memory base = _baseURI(); + + return string.concat(base, _tokenURI); + } +} diff --git a/extension/packages/foundry/script/Deploy.s.sol.args.mjs b/extension/packages/foundry/script/Deploy.s.sol.args.mjs new file mode 100644 index 0000000..f74ab4b --- /dev/null +++ b/extension/packages/foundry/script/Deploy.s.sol.args.mjs @@ -0,0 +1,5 @@ +export const deploymentsScriptsImports = `import { DeploySE2Nft } from "./DeploySE2Nft.s.sol";`; +export const deploymentsLogic = ` + DeploySE2Nft deploySE2Nft = new DeploySE2Nft(); + deploySE2Nft.run(); +`; diff --git a/extension/packages/foundry/script/DeploySE2Nft.s.sol b/extension/packages/foundry/script/DeploySE2Nft.s.sol new file mode 100644 index 0000000..0f52a57 --- /dev/null +++ b/extension/packages/foundry/script/DeploySE2Nft.s.sol @@ -0,0 +1,14 @@ +//SPDX-License-Identifier: MIT +pragma solidity ^0.8.19; + +import "../contracts/SE2NFT.sol"; +import "./DeployHelpers.s.sol"; + +contract DeploySE2Nft is ScaffoldETHDeploy { + function run() external ScaffoldEthDeployerRunner { + SE2NFT se2Nft = new SE2NFT(); + console.logString( + string.concat("SE2NFT deployed at: ", vm.toString(address(se2Nft))) + ); + } +} diff --git a/extension/packages/hardhat/contracts/SE2NFT.sol b/extension/packages/hardhat/contracts/SE2NFT.sol new file mode 100644 index 0000000..4e32f4e --- /dev/null +++ b/extension/packages/hardhat/contracts/SE2NFT.sol @@ -0,0 +1,51 @@ +// SPDX-License-Identifier: MIT +pragma solidity >=0.8.0 <0.9.0; + +import "@openzeppelin/contracts/token/ERC721/ERC721.sol"; +import "@openzeppelin/contracts/token/ERC721/extensions/ERC721Enumerable.sol"; + +contract SE2NFT is ERC721Enumerable { + uint256 public tokenIdCounter; + mapping(uint256 tokenId => string) public tokenURIs; + string[] public uris = [ + "QmVHi3c4qkZcH3cJynzDXRm5n7dzc9R9TUtUcfnWQvhdcw", // Zebra + "QmfVMAmNM1kDEBYrC2TPzQDoCRFH6F5tE1e9Mr4FkkR5Xr", // Buffalo + "QmcvcUaKf6JyCXhLD1by6hJXNruPQGs3kkLg2W1xr7nF1j" // Rhino + ]; + + constructor() ERC721("SE2-ERC721-NFT", "SE2NFT") {} + + function _baseURI() internal pure override returns (string memory) { + return "https://ipfs.io/ipfs/"; + } + + function mintItem(address to) public returns (uint256) { + tokenIdCounter++; + _safeMint(to, tokenIdCounter); + + bytes32 predictableRandom = keccak256( + abi.encodePacked( + tokenIdCounter, + blockhash(block.number - 1), + msg.sender, + address(this) + ) + ); + + tokenURIs[tokenIdCounter] = uris[ + uint256(predictableRandom) % uris.length + ]; + return tokenIdCounter; + } + + function tokenURI( + uint256 tokenId + ) public view override returns (string memory) { + _requireOwned(tokenId); + + string memory _tokenURI = tokenURIs[tokenId]; + string memory base = _baseURI(); + + return string.concat(base, _tokenURI); + } +} diff --git a/extension/packages/hardhat/deploy/01_deploy_se2_nft.ts b/extension/packages/hardhat/deploy/01_deploy_se2_nft.ts new file mode 100644 index 0000000..4533ed5 --- /dev/null +++ b/extension/packages/hardhat/deploy/01_deploy_se2_nft.ts @@ -0,0 +1,38 @@ +import { HardhatRuntimeEnvironment } from "hardhat/types"; +import { DeployFunction } from "hardhat-deploy/types"; +import { Contract, parseEther } from "ethers"; + +/** + * Deploys a contract named "YourContract" using the deployer account and + * constructor arguments set to the deployer address + * + * @param hre HardhatRuntimeEnvironment object. + */ +const deploySe2Nft: DeployFunction = async function (hre: HardhatRuntimeEnvironment) { + /* + On localhost, the deployer account is the one that comes with Hardhat, which is already funded. + + When deploying to live networks (e.g `yarn deploy --network sepolia`), the deployer account + should have sufficient balance to pay for the gas fees for contract creation. + + You can generate a random account with `yarn generate` which will fill DEPLOYER_PRIVATE_KEY + with a random private key in the .env file (then used on hardhat.config.ts) + You can run the `yarn account` command to check your balance in every network. + */ + const { deployer } = await hre.getNamedAccounts(); + const { deploy } = hre.deployments; + + await deploy("SE2NFT", { + from: deployer, + log: true, + // autoMine: can be passed to the deploy function to make the deployment process faster on local networks by + // automatically mining the contract deployment transaction. There is no effect on live networks. + autoMine: true, + }); +}; + +export default deploySe2Nft; + +// Tags are useful if you have multiple deploy files and only want to run one of them. +// e.g. yarn deploy --tags SE2NFT +deploySe2Nft.tags = ["SE2NFT"]; diff --git a/extension/packages/nextjs/app/erc721/components/AllNfts.tsx b/extension/packages/nextjs/app/erc721/components/AllNfts.tsx new file mode 100644 index 0000000..8ea44a7 --- /dev/null +++ b/extension/packages/nextjs/app/erc721/components/AllNfts.tsx @@ -0,0 +1,92 @@ +"use client"; + +import { useEffect, useState } from "react"; +import { NFTCard } from "./NFTCard"; +import { useAccount } from "wagmi"; +import { useScaffoldContract, useScaffoldReadContract } from "~~/hooks/scaffold-eth"; +import { notification } from "~~/utils/scaffold-eth"; + +export interface Collectible { + id: number; + uri: string; + owner: string; + image: string; + name: string; +} + +export const AllNfts = () => { + const [myNfts, setMyNfts] = useState([]); + const [loading, setLoading] = useState(false); + + const { data: se2NftContract } = useScaffoldContract({ + contractName: "SE2NFT", + }); + + const { data: totalSupply } = useScaffoldReadContract({ + contractName: "SE2NFT", + functionName: "totalSupply", + watch: true, + }); + + useEffect(() => { + const updateMyNfts = async (): Promise => { + if (totalSupply === undefined || se2NftContract === undefined) return; + + setLoading(true); + const collectibleUpdate: Collectible[] = []; + const totalBalance = parseInt(totalSupply.toString()); + for (let tokenIndex = 0; tokenIndex < totalBalance; tokenIndex++) { + try { + const tokenId = await se2NftContract.read.tokenByIndex([BigInt(tokenIndex)]); + + const tokenURI = await se2NftContract.read.tokenURI([tokenId]); + const owner = await se2NftContract.read.ownerOf([tokenId]); + + const tokenMetadata = await fetch(tokenURI); + const metadata = await tokenMetadata.json(); + + collectibleUpdate.push({ + id: parseInt(tokenId.toString()), + uri: tokenURI, + owner, + image: metadata.image, + name: metadata.name, + }); + } catch (e) { + notification.error("Error fetching your NTFs"); + setLoading(false); + console.log(e); + } + } + collectibleUpdate.sort((a, b) => a.id - b.id); + setMyNfts(collectibleUpdate); + setLoading(false); + }; + + updateMyNfts(); + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [totalSupply]); + + if (loading) + return ( +
+ +
+ ); + + return ( + <> +
+

Total Supply:

+

{totalSupply ? totalSupply.toString() : 0} tokens

+
+ {myNfts.length > 0 && ( +
+ {myNfts.map(item => ( + + ))} +
+ )} + + ); +}; diff --git a/extension/packages/nextjs/app/erc721/components/MyNfts.tsx b/extension/packages/nextjs/app/erc721/components/MyNfts.tsx new file mode 100644 index 0000000..2834216 --- /dev/null +++ b/extension/packages/nextjs/app/erc721/components/MyNfts.tsx @@ -0,0 +1,93 @@ +"use client"; + +import { useEffect, useState } from "react"; +import { NFTCard } from "./NFTCard"; +import { useAccount } from "wagmi"; +import { useScaffoldContract, useScaffoldReadContract } from "~~/hooks/scaffold-eth"; +import { notification } from "~~/utils/scaffold-eth"; + +export interface Collectible { + id: number; + uri: string; + owner: string; + image: string; + name: string; +} + +export const MyNfts = () => { + const { address: connectedAddress } = useAccount(); + const [myNfts, setMyNfts] = useState([]); + const [loading, setLoading] = useState(false); + + const { data: se2NftContract } = useScaffoldContract({ + contractName: "SE2NFT", + }); + + const { data: balance } = useScaffoldReadContract({ + contractName: "SE2NFT", + functionName: "balanceOf", + args: [connectedAddress], + watch: true, + }); + + useEffect(() => { + const updateMyNfts = async (): Promise => { + if (balance === undefined || se2NftContract === undefined || connectedAddress === undefined) return; + + setLoading(true); + const collectibleUpdate: Collectible[] = []; + const totalBalance = parseInt(balance.toString()); + for (let tokenIndex = 0; tokenIndex < totalBalance; tokenIndex++) { + try { + const tokenId = await se2NftContract.read.tokenOfOwnerByIndex([connectedAddress, BigInt(tokenIndex)]); + + const tokenURI = await se2NftContract.read.tokenURI([tokenId]); + + const tokenMetadata = await fetch(tokenURI); + const metadata = await tokenMetadata.json(); + + collectibleUpdate.push({ + id: parseInt(tokenId.toString()), + uri: tokenURI, + owner: connectedAddress, + image: metadata.image, + name: metadata.name, + }); + } catch (e) { + notification.error("Error fetching your NTFs"); + setLoading(false); + console.log(e); + } + } + collectibleUpdate.sort((a, b) => a.id - b.id); + setMyNfts(collectibleUpdate); + setLoading(false); + }; + + updateMyNfts(); + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [connectedAddress, balance]); + + if (loading) + return ( +
+ +
+ ); + + return ( + <> +
+

Your Balance:

+

{balance ? balance.toString() : 0} tokens

+
+ {myNfts.length > 0 && ( +
+ {myNfts.map(item => ( + + ))} +
+ )} + + ); +}; diff --git a/extension/packages/nextjs/app/erc721/components/NFTCard.tsx b/extension/packages/nextjs/app/erc721/components/NFTCard.tsx new file mode 100644 index 0000000..bcc20e6 --- /dev/null +++ b/extension/packages/nextjs/app/erc721/components/NFTCard.tsx @@ -0,0 +1,60 @@ +import { useState } from "react"; +import { Collectible } from "./MyNfts"; +import { Address, AddressInput } from "~~/components/scaffold-eth"; +import { useScaffoldWriteContract } from "~~/hooks/scaffold-eth"; + +export const NFTCard = ({ nft, transfer }: { nft: Collectible; transfer?: boolean }) => { + const [transferToAddress, setTransferToAddress] = useState(""); + + const { writeContractAsync } = useScaffoldWriteContract("SE2NFT"); + + return ( +
+
+ {/* eslint-disable-next-line */} + NFT Image +
+ # {nft.id} +
+
+
+
+

{nft.name}

+
+
+ Owner : +
+
+ {transfer && ( + <> +
+ Transfer To: + setTransferToAddress(newValue)} + /> +
+
+ +
+ + )} +
+
+ ); +}; diff --git a/extension/packages/nextjs/app/erc721/page.tsx b/extension/packages/nextjs/app/erc721/page.tsx new file mode 100644 index 0000000..0980ba1 --- /dev/null +++ b/extension/packages/nextjs/app/erc721/page.tsx @@ -0,0 +1,136 @@ +"use client"; + +import { useState } from "react"; +import { AllNfts } from "./components/AllNfts"; +import { MyNfts } from "./components/MyNfts"; +import type { NextPage } from "next"; +import { useAccount } from "wagmi"; +import { AddressInput, RainbowKitCustomConnectButton } from "~~/components/scaffold-eth"; +import { useScaffoldWriteContract } from "~~/hooks/scaffold-eth"; + +const ERC721: NextPage = () => { + const { address: connectedAddress } = useAccount(); + + const [toAddress, setToAddress] = useState(""); + + const { writeContractAsync: writeSE2TokenAsync } = useScaffoldWriteContract("SE2NFT"); + + return ( + <> +
+
+

ERC-721 NFT

+
+

+ This extension introduces an ERC-721 token contract and demonstrates how to use it, including getting the + total supply and holder balance, listing all NFTs and NFTs from an address, and how to transfer. +

+

+ The ERC-721 Token Standard introduces a standard for Non-Fungible Tokens ( + + EIP-721 + + ), in other words, each Token is unique. +

+

+ The ERC-721 token contract is implemented using the{" "} + + ERC-721 token implementation + {" "} + from OpenZeppelin. +

+

+ The ERC-721 token implementation uses the{" "} + + ERC-721 Enumerable extension + {" "} + from OpenZeppelin to list all tokens and all the tokens owned by an address. You can remove this if you + plan to use an indexer, like a Subgraph or Ponder (extensions available). +

+
+ +
+ +

Interact with the NFT

+ +
+

Below you can mint an NFT for yourself or to another address.

+

+ You can see your balance and your NFTs and after that you can see the total supply and all the NFTs + minted. +

+

+ Check the code under packages/nextjs/app/erc721 to learn more about how to interact with the + ERC721 contract. +

+
+
+ + {connectedAddress ? ( +
+
+ +
+
+

Mint token to another address

+
+
To:
+
+ +
+
+
+ +
+
+ + +
+ ) : ( +
+

Please connect your wallet to interact with the token.

+ +
+ )} +
+ + ); +}; + +export default ERC721; diff --git a/extension/packages/nextjs/components/Header.tsx.args.mjs b/extension/packages/nextjs/components/Header.tsx.args.mjs new file mode 100644 index 0000000..d0e0dd3 --- /dev/null +++ b/extension/packages/nextjs/components/Header.tsx.args.mjs @@ -0,0 +1,6 @@ +export const menuIconImports = `import { PhotoIcon } from "@heroicons/react/24/outline";`; +export const menuObjects = `{ + label: "ERC-721", + href: "/erc721", + icon: , +}`; \ No newline at end of file From 657c2455375b72cb0aaf18a0ed816824e9f2f5f5 Mon Sep 17 00:00:00 2001 From: Damian Date: Tue, 22 Oct 2024 11:52:40 -0300 Subject: [PATCH 2/6] Improvements from the PR review --- README.md | 8 +++----- extension/README.md.args.mjs | 4 ++-- .../nextjs/app/erc721/components/AllNfts.tsx | 20 +++++++++---------- .../nextjs/app/erc721/components/MyNfts.tsx | 4 ++-- extension/packages/nextjs/app/erc721/page.tsx | 18 ++++++++++++----- 5 files changed, 29 insertions(+), 25 deletions(-) diff --git a/README.md b/README.md index 6dcf3c5..3b1af62 100644 --- a/README.md +++ b/README.md @@ -1,17 +1,15 @@ # ERC-721 NFT Extension for Scaffold-ETH 2 -This extension introduces an ERC-721 token contract and demonstrates how to use it, including getting the total supply and holder balance, listing all NFTs and NFTs from an address, and how to transfer. +This extension introduces an ERC-721 token contract and demonstrates how to use it, including getting the total supply and holder balance, listing all NFTs from the collection and NFTs from the connected address, and how to transfer NFTs. -The ERC-721 Token Standard introduces a standard for Non-Fungible Tokens ([EIP-721](https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/token/ERC721/ERC721.sol)), in other words, each Token is unique. +The ERC-721 Token Standard introduces a standard for Non-Fungible Tokens ([EIP-721](https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/token/ERC721/ERC721.sol)), in other words, each token is unique. The ERC-721 token contract is implemented using the [ERC-721 token implementation](https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/token/ERC721/ERC721.sol) from OpenZeppelin. -The ERC-721 token implementation uses the [ERC-721 Enumerable extension](https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/token/ERC721/extensions/ERC721Enumerable.sol) from OpenZeppelin to list all tokens and all the tokens owned by an address. You can remove this if you plan to use an indexer, like a Subgraph or Ponder (extensions available). +The ERC-721 token implementation uses the [ERC-721 Enumerable extension](https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/token/ERC721/extensions/ERC721Enumerable.sol) from OpenZeppelin to list all tokens from the collection and all the tokens owned by an address. You can remove this if you plan to use an indexer, like a Subgraph or Ponder ([extensions available](https://scaffoldeth.io/extensions)). ## Installation -You can install any of the extensions in this repository by running the following command: - ```bash npx create-eth@latest -e erc-721 ``` diff --git a/extension/README.md.args.mjs b/extension/README.md.args.mjs index 6f31b23..64c489d 100644 --- a/extension/README.md.args.mjs +++ b/extension/README.md.args.mjs @@ -1,8 +1,8 @@ export const extraContents = `## 🚀 Setup ERC-721 NFT Extension -This extension introduces an ERC-721 token contract and demonstrates how to use it, including getting the total supply and holder balance, listing all NFTs and NFTs from an address, and how to transfer. +This extension introduces an ERC-721 token contract and demonstrates how to use it, including getting the total supply and holder balance, listing all NFTs from the collection and NFTs from the connected address, and how to transfer NFTs. -The ERC-721 Token Standard introduces a standard for Non-Fungible Tokens ([EIP-721](https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/token/ERC721/ERC721.sol)), in other words, each Token is unique. +The ERC-721 Token Standard introduces a standard for Non-Fungible Tokens ([EIP-721](https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/token/ERC721/ERC721.sol)), in other words, each token is unique. The ERC-721 token contract is implemented using the [ERC-721 token implementation](https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/token/ERC721/ERC721.sol) from OpenZeppelin. diff --git a/extension/packages/nextjs/app/erc721/components/AllNfts.tsx b/extension/packages/nextjs/app/erc721/components/AllNfts.tsx index 8ea44a7..bc9893e 100644 --- a/extension/packages/nextjs/app/erc721/components/AllNfts.tsx +++ b/extension/packages/nextjs/app/erc721/components/AllNfts.tsx @@ -2,7 +2,6 @@ import { useEffect, useState } from "react"; import { NFTCard } from "./NFTCard"; -import { useAccount } from "wagmi"; import { useScaffoldContract, useScaffoldReadContract } from "~~/hooks/scaffold-eth"; import { notification } from "~~/utils/scaffold-eth"; @@ -15,7 +14,7 @@ export interface Collectible { } export const AllNfts = () => { - const [myNfts, setMyNfts] = useState([]); + const [allNfts, setAllNfts] = useState([]); const [loading, setLoading] = useState(false); const { data: se2NftContract } = useScaffoldContract({ @@ -29,13 +28,12 @@ export const AllNfts = () => { }); useEffect(() => { - const updateMyNfts = async (): Promise => { + const updateAllNfts = async (): Promise => { if (totalSupply === undefined || se2NftContract === undefined) return; setLoading(true); const collectibleUpdate: Collectible[] = []; - const totalBalance = parseInt(totalSupply.toString()); - for (let tokenIndex = 0; tokenIndex < totalBalance; tokenIndex++) { + for (let tokenIndex = 0; tokenIndex < parseInt(totalSupply.toString()); tokenIndex++) { try { const tokenId = await se2NftContract.read.tokenByIndex([BigInt(tokenIndex)]); @@ -53,17 +51,17 @@ export const AllNfts = () => { name: metadata.name, }); } catch (e) { - notification.error("Error fetching your NTFs"); + notification.error("Error fetching NTFs"); setLoading(false); console.log(e); } } collectibleUpdate.sort((a, b) => a.id - b.id); - setMyNfts(collectibleUpdate); + setAllNfts(collectibleUpdate); setLoading(false); }; - updateMyNfts(); + updateAllNfts(); // eslint-disable-next-line react-hooks/exhaustive-deps }, [totalSupply]); @@ -77,12 +75,12 @@ export const AllNfts = () => { return ( <>
-

Total Supply:

+

Total Supply:

{totalSupply ? totalSupply.toString() : 0} tokens

- {myNfts.length > 0 && ( + {allNfts.length > 0 && (
- {myNfts.map(item => ( + {allNfts.map(item => ( ))}
diff --git a/extension/packages/nextjs/app/erc721/components/MyNfts.tsx b/extension/packages/nextjs/app/erc721/components/MyNfts.tsx index 2834216..dce1925 100644 --- a/extension/packages/nextjs/app/erc721/components/MyNfts.tsx +++ b/extension/packages/nextjs/app/erc721/components/MyNfts.tsx @@ -54,7 +54,7 @@ export const MyNfts = () => { name: metadata.name, }); } catch (e) { - notification.error("Error fetching your NTFs"); + notification.error("Error fetching your NFTs"); setLoading(false); console.log(e); } @@ -78,7 +78,7 @@ export const MyNfts = () => { return ( <>
-

Your Balance:

+

Your Balance:

{balance ? balance.toString() : 0} tokens

{myNfts.length > 0 && ( diff --git a/extension/packages/nextjs/app/erc721/page.tsx b/extension/packages/nextjs/app/erc721/page.tsx index 0980ba1..668ce68 100644 --- a/extension/packages/nextjs/app/erc721/page.tsx +++ b/extension/packages/nextjs/app/erc721/page.tsx @@ -23,7 +23,7 @@ const ERC721: NextPage = () => {

This extension introduces an ERC-721 token contract and demonstrates how to use it, including getting the - total supply and holder balance, listing all NFTs and NFTs from an address, and how to transfer. + total supply and holder balance, listing all NFTs from the collection and NFTs from the connected address, and how to transfer NFTs.

The ERC-721 Token Standard introduces a standard for Non-Fungible Tokens ( @@ -34,7 +34,7 @@ const ERC721: NextPage = () => { > EIP-721 - ), in other words, each Token is unique. + ), in other words, each token is unique.

The ERC-721 token contract is implemented using the{" "} @@ -57,7 +57,15 @@ const ERC721: NextPage = () => { ERC-721 Enumerable extension {" "} from OpenZeppelin to list all tokens and all the tokens owned by an address. You can remove this if you - plan to use an indexer, like a Subgraph or Ponder (extensions available). + plan to use an indexer, like a Subgraph or Ponder ( + + extensions available + + ).

@@ -68,7 +76,7 @@ const ERC721: NextPage = () => {

Below you can mint an NFT for yourself or to another address.

- You can see your balance and your NFTs and after that you can see the total supply and all the NFTs + You can see your balance and your NFTs, and below that, you can see the total supply and all the NFTs minted.

@@ -111,7 +119,7 @@ const ERC721: NextPage = () => { await writeSE2TokenAsync({ functionName: "mintItem", args: [toAddress] }); setToAddress(""); } catch (e) { - console.error("Error while transfering token", e); + console.error("Error while minting token", e); } }} > From a52ec672f20c51d286ee5d38aecfa570318871a9 Mon Sep 17 00:00:00 2001 From: Pablo <55535804+Pabl0cks@users.noreply.github.com> Date: Sun, 22 Dec 2024 01:04:37 +0100 Subject: [PATCH 3/6] Fix EIP link --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 3b1af62..730616f 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@ This extension introduces an ERC-721 token contract and demonstrates how to use it, including getting the total supply and holder balance, listing all NFTs from the collection and NFTs from the connected address, and how to transfer NFTs. -The ERC-721 Token Standard introduces a standard for Non-Fungible Tokens ([EIP-721](https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/token/ERC721/ERC721.sol)), in other words, each token is unique. +The ERC-721 Token Standard introduces a standard for Non-Fungible Tokens ([EIP-721](https://eips.ethereum.org/EIPS/eip-721)), in other words, each token is unique. The ERC-721 token contract is implemented using the [ERC-721 token implementation](https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/token/ERC721/ERC721.sol) from OpenZeppelin. From 4d5e01a16a161d59f8a4068bc153a2ecf291786b Mon Sep 17 00:00:00 2001 From: Pablo <55535804+Pabl0cks@users.noreply.github.com> Date: Sun, 22 Dec 2024 01:05:56 +0100 Subject: [PATCH 4/6] Fix EIP link in template --- extension/README.md.args.mjs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/extension/README.md.args.mjs b/extension/README.md.args.mjs index 64c489d..b27f88b 100644 --- a/extension/README.md.args.mjs +++ b/extension/README.md.args.mjs @@ -2,7 +2,7 @@ export const extraContents = `## 🚀 Setup ERC-721 NFT Extension This extension introduces an ERC-721 token contract and demonstrates how to use it, including getting the total supply and holder balance, listing all NFTs from the collection and NFTs from the connected address, and how to transfer NFTs. -The ERC-721 Token Standard introduces a standard for Non-Fungible Tokens ([EIP-721](https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/token/ERC721/ERC721.sol)), in other words, each token is unique. +The ERC-721 Token Standard introduces a standard for Non-Fungible Tokens ([EIP-721](https://eips.ethereum.org/EIPS/eip-721)), in other words, each token is unique. The ERC-721 token contract is implemented using the [ERC-721 token implementation](https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/token/ERC721/ERC721.sol) from OpenZeppelin. From a3f238370e606c5e37450c710851526a18214fd5 Mon Sep 17 00:00:00 2001 From: Pablo <55535804+Pabl0cks@users.noreply.github.com> Date: Sun, 22 Dec 2024 01:07:48 +0100 Subject: [PATCH 5/6] Add ERC-721 Enumerable extension section --- extension/README.md.args.mjs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/extension/README.md.args.mjs b/extension/README.md.args.mjs index b27f88b..ccefc2d 100644 --- a/extension/README.md.args.mjs +++ b/extension/README.md.args.mjs @@ -6,6 +6,8 @@ The ERC-721 Token Standard introduces a standard for Non-Fungible Tokens ([EIP-7 The ERC-721 token contract is implemented using the [ERC-721 token implementation](https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/token/ERC721/ERC721.sol) from OpenZeppelin. +The ERC-721 token implementation uses the [ERC-721 Enumerable extension](https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/token/ERC721/extensions/ERC721Enumerable.sol) from OpenZeppelin to list all tokens from the collection and all the tokens owned by an address. You can remove this if you plan to use an indexer, like a Subgraph or Ponder ([extensions available](https://scaffoldeth.io/extensions)). + ### Setup Deploy your contract running \`\`\`yarn deploy\`\`\` From 0fd3bf64332c47d0c6687700a4c7d841be5d1ff2 Mon Sep 17 00:00:00 2001 From: Pablo <55535804+Pabl0cks@users.noreply.github.com> Date: Sun, 22 Dec 2024 01:09:37 +0100 Subject: [PATCH 6/6] Add missing words --- extension/packages/nextjs/app/erc721/page.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/extension/packages/nextjs/app/erc721/page.tsx b/extension/packages/nextjs/app/erc721/page.tsx index 668ce68..df3ee7c 100644 --- a/extension/packages/nextjs/app/erc721/page.tsx +++ b/extension/packages/nextjs/app/erc721/page.tsx @@ -56,7 +56,7 @@ const ERC721: NextPage = () => { > ERC-721 Enumerable extension {" "} - from OpenZeppelin to list all tokens and all the tokens owned by an address. You can remove this if you + from OpenZeppelin to list all tokens from the collection and all the tokens owned by an address. You can remove this if you plan to use an indexer, like a Subgraph or Ponder (