ERC721 Contract and IPFS
ERC-721
ERC-721 is the standard for Non-Fungible Tokens (NFTs) on Ethereum, which means that each token is unique and cannot be replaced by another token, unlike ERC-20 tokens that are fungible (interchangeable, like ETH or other tokens). Think of ERC-721 tokens as unique digital items—artwork, collectibles, or property deeds—that you can own, transfer, or sell.
Let’s go through the process of creating an NFT (named ABC) on Ethereum using the Sepolia test network, storing metadata on IPFS, and setting up a basic frontend with Tailwind CSS for minting.
Creating the ERC-721 Contract (ABC NFT)
Set up the Hardhat project:
mkdir ABCNFT cd ABCNFT npm init -y npm install --save-dev hardhat
Initialize Hardhat:
npx hardhat
Follow the prompts to create a basic Hardhat project.
Install OpenZeppelin Contracts:
npm install @openzeppelin/contracts
Create the Smart Contract: Inside the
contracts
folder, create a file namedABC.sol
:// SPDX-License-Identifier: MIT pragma solidity ^0.8.0; import "@openzeppelin/contracts/token/ERC721/ERC721.sol"; import "@openzeppelin/contracts/access/Ownable.sol"; contract ABC is ERC721, Ownable { uint256 public nextTokenId; string public baseURI; constructor(string memory _baseURI) ERC721("ABC", "ABC") { baseURI = _baseURI; } function _baseURI() internal view override returns (string memory) { return baseURI; } function mint(address to) public onlyOwner { _safeMint(to, nextTokenId); nextTokenId++; } function setBaseURI(string memory _baseURI) public onlyOwner { baseURI = _baseURI; } }
This Contract:
Base URI: This is the URI where metadata for each NFT will be located. When you call
tokenURI(tokenId)
, it will returnbaseURI + tokenId
, which points to each token’s metadata on IPFS.Minting Function: Only the contract owner can mint new NFTs. This function assigns a unique
tokenId
to each new token.Set Base URI: Allows the owner to set the base URI, which we’ll point to an IPFS folder where all metadata files are stored.
Setting Up IPFS and Metadata
IPFS (InterPlanetary File System) is a decentralized storage solution that allows you to store files (like images, metadata, videos) across a distributed network. Since NFTs often represent assets like images, it’s common to use IPFS to store these files. IPFS provides content-addressable storage, which means files are accessed by their unique hash, ensuring immutability.
For each NFT, we need a metadata file in JSON format. This metadata file will reference the image URL, which will also be stored on IPFS. Each JSON file should follow a standard format to ensure compatibility with NFT platforms.
IPFS Structure:
Upload the image: First, upload your image file to IPFS. You can do this on IPFS's official desktop application or use services like Pinata or NFT.Storage to pin your files on IPFS. We will use Pinate for this tut.
Let’s say your image file’s IPFS CID is
Qm...ImageHash
.Generate the Metadata JSON: Each token needs a metadata JSON file, following the ERC-721 standard format. Here’s an example
0.json
file for token0
:{ "name": "ABC #0", "description": "An example NFT in the ABC collection", "image": "ipfs://<ImageHash>", "attributes": [ { "trait_type": "Rarity", "value": "Unique" }, { "trait_type": "Color", "value": "Red" } ] }
Replace
<ImageHash>
with the actual IPFS hash of the image.Upload Metadata File to IPFS: Upload the JSON file to IPFS, then use the CID to set the
baseURI
in the contract.
Deploying the Contract to Sepolia
Set Up Hardhat Configuration for Sepolia:
In
hardhat.config.js
, add the following:require("@nomiclabs/hardhat-ethers"); module.exports = { solidity: "0.8.0", networks: { sepolia: { url: "https://sepolia.infura.io/v3/YOUR_INFURA_PROJECT_ID", accounts: ["YOUR_PRIVATE_KEY"] } } };
Replace
YOUR_INFURA_PROJECT_ID
with your Infura project ID andYOUR_PRIVATE_KEY
with the private key of the wallet you’ll use for deployment.Deploy Script:
Inside
scripts/
, create a filedeploy.js
:async function main() { const [deployer] = await ethers.getSigners(); console.log("Deploying contract with account:", deployer.address); const ABC = await ethers.getContractFactory("ABC"); const abc = await ABC.deploy("ipfs://<MetadataCID>/"); console.log("ABC contract deployed to:", abc.address); } main() .then(() => process.exit(0)) .catch(error => { console.error(error); process.exit(1); });
Run the deployment script:
npx hardhat run scripts/deploy.js --network sepolia
Note down the contract address.
Creating a Frontend with Tailwind CSS for Minting
Set up the Frontend: In your root project folder, set up a basic React app:
npx create-react-app frontend cd frontend
Install Dependencies:
npm install ethers npm install -D tailwindcss npx tailwindcss init
Configure Tailwind: In
tailwind.config.js
, add this:module.exports = { content: [ "./src/**/*.{js,jsx,ts,tsx}", ], theme: { extend: {}, }, plugins: [], };
Update
index.css
:@tailwind base; @tailwind components; @tailwind utilities;
Connect to the Contract:
Replace
App.js
with this code:import React, { useState } from "react"; import { ethers } from "ethers"; import "./App.css"; const CONTRACT_ADDRESS = "YOUR_CONTRACT_ADDRESS"; const ABI = [ "function mint(address to) public", "function owner() view returns (address)" ]; function App() { const [status, setStatus] = useState(""); async function mintNFT() { if (!window.ethereum) { alert("Please install MetaMask!"); return; } const provider = new ethers.providers.Web3Provider(window.ethereum); await provider.send("eth_requestAccounts", []); const signer = provider.getSigner(); const contract = new ethers.Contract(CONTRACT_ADDRESS, ABI, signer); try { const tx = await contract.mint(await signer.getAddress()); setStatus("Minting... Check your wallet."); await tx.wait(); setStatus("Minting complete!"); } catch (error) { setStatus("Failed to mint NFT."); console.error(error); } } return ( <div className="App min-h-screen flex flex-col items-center justify-center bg-gray-100"> <h1 className="text-4xl font-bold mb-4">Mint Your ABC NFT</h1> <button onClick={mintNFT} className="bg-blue-500 text-white px-6 py-3 rounded-md hover:bg-blue-600" > Mint NFT </button> <p className="mt-4 text-gray-700">{status}</p> </div> ); } export default App;
Styling: Add additional Tailwind classes as desired to enhance the UI.
Run the App:
npm start
Testing the Frontend and Minting
Make sure you’re connected to the Sepolia network in MetaMask.
Open your frontend in the browser and click the Mint NFT button.
Approve the transaction in MetaMask.
Once the transaction confirms, you should see a message indicating that the minting is complete. If everything is set up correctly, you’ll have successfully minted an NFT through your frontend!
Last updated