Quickstart Guide To Building NFT Minting DAPPs On The Ethereum Chain
Probably Not So Quick But It Will Be Comprehensive ๐
Table of contents
- What On The Earth Are NFTs?
- How We Can Create A NFT Collection?
- Let's start Building Our NFT Collection
Hey folks ๐๐ป, it's been a while since I wrote my last article and I thought why not write another article. In this tutorial, we'll discuss how to generate NFTs, create a minting DAPP, set up a delayed reveal and many more. So, let's just talk about the background story for a little bit and hop into the actual smart contract.
What On The Earth Are NFTs?
If you haven't been living under a rock for the past year or so you should know what are NFTs at this point but if you don't know, NFTs are just digital assets that are most importantly stored in the blockchain. (Well, not exactly stored in the blockchain but kind of like that) NFTs are not just sitting in the blockchain doing nothing. They are bought and sold online, not for 5$ but for millions of dollars ๐ค. You can go to opensea and scroll through a bunch of awesome NFTs that costs millions of dollars.
NFTs are unique, as the name suggests. (Non-Fungible Tokens) So they can't be replaced with something else. For example crypto is fungible, you can trade 1ETH for 1ETH which are exactly the same but in NFTs, you can't do that. If you traded an NFT for a different NFT you will have a completely different NFT.
Also, I must say that they are a TON of hate going on for NFTs. Read more about that. But I don't care because,
So without any further ado, let's jump into the REAL STUFF.
How We Can Create A NFT Collection?
Well, there are many methods for you to create NFT collections, you can create them with code, low code or even no code. But in this article, we focus on the method with code to mint our NFTs. But we'll just go over the other method briefly.
- No Code
So the easiest method of all of them is Open Sea Lazy Mint. You can just go to opensea and click that create button and enter the details manually and save the NFT. I'll actually be minted when someone bought your NFT. That's why they call it Lazy Minting.
- Low Code
You can use services like Thirdweb or NFT Port to mint your NFTs and you don't actually have to write a single line of solidity. It might be a good way as well if you think about it.
These are definitely not No Code. You will have to write some code in order to work with these. But you are free from writing blockchain specific languages like Solidity. So it might be a great fit for your choice. Especially Thirdweb, I find Thirdweb really interesting so make sure to give Thirdweb a go as well.
Let's start Building Our NFT Collection
Getting The Base Character And Layers Ready
Before writing any smart contract, DAPPs or anything like that, we need some digital thing to mint as NFTs, right? So far the most popular thing is little artworks. So let's build our project with some artwork as well.
But most of them are not just a piece of art. If you take a look at popular NFT collections like Bored Ape Yacht Club. You can see a similarity between all of those. It is basically like there is a base figure, in this case, an ape and on that ape, there are different things that are called traits. You have to design those base characters and traits separately, to learn how to properly do that, you can check out this amazing video by codestackr. I am not gonna explain it too much because I feel like it is out of the scope of this article.
You can generate all the layers you need and come back to this article to proceed further.
Generating The Actual NFT Arts
So this is where the interesting things start happening. Now you will need an Art Generator to merge all of your layers and create a single PNG file. There are many ways you could achieve that but in this case, I am gonna use something called HashLips. I have made a customized fork of the official HashLips art generator on my GitHub profile.
You will need to clone the premade repository by me to proceed with this tutorial. In that repository, you will be able to find starter codes for all of the things that we are gonna be doing. To clone the repository, use this command.
git clone -b starter https://github.com/osadavc/build-a-minting-dapp.git
once you have it cloned, cd into the art-generator
directory. This is where all the magic happens. If you have generated your layers according to the video I suggested I have before, you should have a folder structure like this or something similar to this structure
images/
โโโ Background/
โ โโโ GreenBackground.png
โ โโโ BlueBackground.png
โโโ Base/
โ โโโ MainBase.png
โโโ Eyes/
โ โโโ SunGlasses.png
โ โโโ Eyes.png
โ โโโ EyebrowRaised.png
โโโ Hair/
โโโ StraightHair.png
โโโ CurlyHair.png
What you have to do is remove everything inside the art-generator/layers
that you cloned earlier and copy all of the folders that you have generated and put them inside that art-generator/layers
directory.
Then open the file called art-generator/src/config.js
and look from lines 6 to 23. Which looks something like this.
const namePrefix = "Your Collection";
const description = "Remember to replace this description";
const baseUri = "ipfs://NewUriToReplace";
const layerConfigurations = [
{
growEditionSizeTo: 5,
layersOrder: [
{ name: "Background" },
{ name: "Eyeball" },
{ name: "Eye color" },
{ name: "Iris" },
{ name: "Shine" },
{ name: "Bottom lid" },
{ name: "Top lid" },
],
},
];
Here you have a number of things to change here. First, change the namePrefix
variable to the name of your NFT Collection and make sure to add a little description to the description
variable as well. We'll have to do some work before changing the base URL, so we'll look into that really soon. Then you have the most important part.
const layerConfigurations = [
{
growEditionSizeTo: 5,
layersOrder: [
{ name: "Background" },
{ name: "Eyeball" },
{ name: "Eye color" },
{ name: "Iris" },
{ name: "Shine" },
{ name: "Bottom lid" },
{ name: "Top lid" },
],
},
];
First, change the property called growEditionSizeTo
to the maximum number of arts that you need and then you have the layer order, so you have to put all of your folder names in the order of the real layer order, so the topmost object in the array layersOrder
should be the layer that sits behind all of the other layers in the actual artwork. Most probably this will be the background and you have to put all of your traits in this array for the art generator to work properly.
Then you've come to the satisfying part. Make sure you're in the art-generator
directory in your terminal and run
yarn generate
or
npm run generate
Once you've done that you'll see a directory called build
with all of your images and metadata, if you take a look at one of these images, you will see that the image is composed of all of the layers that you defined previously.
Uploading Images To IPFS
Now that you have all the images that you need, you have to upload them somewhere in order for the other people to see them. You can store them in many places but the most recommended way is to store them in IPFS (InterPlanetary File System). We can't store them in the blockchain itself because the images are too big to be stored in the blockchain. This is the reason why I said NFTs are not really stored in the blockchain. We only have a record of them in the blockchain.
To upload the images to IPFS, you'll need a service that'll allow you to store files in IPFS. We are gonna use Pinata. Go ahead and create an account in pinata.
Now, you can select your images folder and upload them to pinata like this,
Now, if you take a closer look at the newly created row, you will see something called CID, make sure to copy that and replace it like this in the config file in the art generator.
const namePrefix = "Beautiful Eyes";
const description = "A NFT Collection That Contains 50 Beautiful Eyes";
// Update The CID
const baseUri = "ipfs://QmPAgyCjcMQLag5HM2rJN71Zkt1TFtgLdyqcG8qzum73vh";
Now that you have updated the config file, you need to update the metadata files you previously generated as well. Obviously doing it manually will be a tedious task, so make sure you are at the root level of the art-generator
and run the below command which will update the metadata.
yarn update_info
or
npm run update_info
Once it is successfully completed, you can check a metadata file and you'll see that the information is correctly updated. Now you have to follow the same steps that we did previously for images and upload these metadata files to Pinata as well.
Congratulations ๐. Now you have your artwork generated and hosted in a decentralized manner.
Writing Solidity Smart Contract
Now that the artworks are hosted on IPFS, it is time to write the smart contract to allow users to mint NFTs. We are going to use the Hardhat toolchain to be able to compile and deploy Solidity code. You usually have to set up hardhat with their CLI tool but to save time I have created a starter for you. If you would like to learn how to set up Hardhat by yourself, read Hardhat documentation, they have explained how to set up a new project very clearly.
I am gonna show you how to build the smart contract but if you need to learn solidity from zero to hero, I highly suggest taking a look at Hashlips Solidity playlist.
The starter code for the smart contract is available in the smart-contract
directory in the previously cloned repository.
There are a few folders in the smart-contracts
folder. All of your smart contracts will go to contracts
and all of the scripts that you define will go inside the scripts
directory. We will later see to what we use these scripts.
Creating A Basic NFT Minting Contract
We are going to use the ERC721 industry standard which is suitable to build Non Fungible tokens to build our smart contract.
To make our lives a lot easier and writing smart contracts so easier and more secure, we are going to use a tool called OpenZeppelin. Before doing anything you need to install OpenZeppelin, in-order to do that run one of the following commands inside the smart-contract
directory.
yarn add @openzeppelin/contracts
or
npm i @openzeppelin/contracts
and to get the boilerplate for our contract, we are going to use the OpenZeppelin tool, they have an awesome tool called OpenZeppelin Wizard. Open their wizard and select the following options.
First we are gonna choose ERC721 because that is the standard that we are planning to use.
Then, you need to choose a Name and Symbol for your NFT Contract.
From features, choose Mintable, Auto Increment Ids and URI Storage
Choose Ownable because the contract is owned by only one user.
Once the code is generated, click on the Copy To Clipboard
button and paste the copied code to the contract you have created.
Next, we need to add a couple of functions to customize the behaviour of our smart contract.
- First, we are gonna add a new
mint
function to allow users to mint
function mint(address to) public payable {
require(msg.value >= 0.005 ether, "Not Enough ETH");
_tokenIdCounter.increment();
uint256 newItemId = _tokenIdCounter.current();
_mint(to, newItemId);
}
With this mint function, we are allowing users to mint an NFT by paying 0.005ETH, if they haven't paid the correct amount we are aborting the function.
- Base URI Method
function _baseURI() internal pure override returns (string memory) {
return "ipfs://QmZ5WxKWrL7fMFnQpbHcDDGhSA42PPwQiMXL7guB4v6wJd/";
}
This is the URL where our METADATA is hosted, make sure to change it to your own IPFS content id and keep a slash after the id.
- Token URI Function
function tokenURI(uint256 _tokenId)
public
view
override(ERC721, ERC721URIStorage)
returns (string memory)
{
string memory currentBaseURI = _baseURI();
return
bytes(currentBaseURI).length > 0
? string(
abi.encodePacked(
currentBaseURI,
uintToString(_tokenId),
".json"
)
)
: "";
}
What we are basically doing here is when a marketplace requests for the metadata of the NFT, we get the token id and return the actual metadata URL. Notice here we are using a utility function called uintToString
to convert a uint256
to string
. That function is given below.
function uintToString(uint256 _i)
internal
pure
returns (string memory str)
{
if (_i == 0) {
return "0";
}
uint256 j = _i;
uint256 length;
while (j != 0) {
length++;
j /= 10;
}
bytes memory bstr = new bytes(length);
uint256 k = length;
j = _i;
while (j != 0) {
bstr[--k] = bytes1(uint8(48 + (j % 10)));
j /= 10;
}
str = string(bstr);
}
Once you have made all these changes your contract should look something like this,
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.4;
import "@openzeppelin/contracts/token/ERC721/ERC721.sol";
import "@openzeppelin/contracts/token/ERC721/extensions/ERC721URIStorage.sol";
import "@openzeppelin/contracts/access/Ownable.sol";
import "@openzeppelin/contracts/utils/Counters.sol";
contract BeautifulEyesNFT is ERC721, ERC721URIStorage, Ownable {
using Counters for Counters.Counter;
Counters.Counter private _tokenIdCounter;
constructor() ERC721("BeautifulEyesNFT", "BEN") {}
function _baseURI() internal pure override returns (string memory) {
return "ipfs://QmZ5WxKWrL7fMFnQpbHcDDGhSA42PPwQiMXL7guB4v6wJd/";
}
function mint(address to) public payable {
require(msg.value >= 0.005 ether, "Not Enough ETH");
_tokenIdCounter.increment();
uint256 newItemId = _tokenIdCounter.current();
_mint(to, newItemId);
}
function safeMint(address to, string memory uri) public onlyOwner {
uint256 tokenId = _tokenIdCounter.current();
_tokenIdCounter.increment();
_safeMint(to, tokenId);
_setTokenURI(tokenId, uri);
}
function _burn(uint256 tokenId)
internal
override(ERC721, ERC721URIStorage)
{
super._burn(tokenId);
}
function tokenURI(uint256 _tokenId)
public
view
override(ERC721, ERC721URIStorage)
returns (string memory)
{
string memory currentBaseURI = _baseURI();
return
bytes(currentBaseURI).length > 0
? string(
abi.encodePacked(
currentBaseURI,
uintToString(_tokenId),
".json"
)
)
: "";
}
// utils
function uintToString(uint256 _i)
internal
pure
returns (string memory str)
{
if (_i == 0) {
return "0";
}
uint256 j = _i;
uint256 length;
while (j != 0) {
length++;
j /= 10;
}
bytes memory bstr = new bytes(length);
uint256 k = length;
j = _i;
while (j != 0) {
bstr[--k] = bytes1(uint8(48 + (j % 10)));
j /= 10;
}
str = string(bstr);
}
}
With that in place, the most basic NFT minting contract is done ๐๐ป. If you really want to test this Contract as it is now, go to the very bottom and read the Deployment section but for now, I am not gonna do it.
Adding A Sale Time
Right now a user can mint tokens at any time that they need, but you might need to restrict this to a specific time, let's just say the time that you launch your project. So let's stop users from minting at any time that they want.
First, go to your contract and create a new variable of the type uint256 to store the sale starting time like the following,
uint256 public saleTimeStamp;
Then in the constructor, get a new argument to change the saleTimeStamp
when the contract is deployed like the following,
constructor(uint256 _saleTimeStamp) ERC721("BeautifulEyesNFT", "BEN") {
saleTimeStamp = _saleTimeStamp;
}
You might need to change the sale time later, so you will also need a function to change the value stored in the save timestamp
variable.
function setSaleTimeStamp(uint256 _saleTimeStamp) public onlyOwner {
saleTimeStamp = _saleTimeStamp;
}
Now, when a user is trying to mint we need to check if the time stored in saleTimeStamp
is lower than the time now meaning the sale is already going on. To do that add another require
statement to the mint
function.
function mint(address to) public payable {
// Compare the time now and the saved timestamp
require(saleTimeStamp < block.timestamp, "Sale has not started yet");
require(msg.value >= 0.005 ether, "Not Enough ETH");
_tokenIdCounter.increment();
uint256 newItemId = _tokenIdCounter.current();
_mint(to, newItemId);
}
after necessary changes are made, the contract should look like this
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.4;
import "@openzeppelin/contracts/token/ERC721/ERC721.sol";
import "@openzeppelin/contracts/token/ERC721/extensions/ERC721URIStorage.sol";
import "@openzeppelin/contracts/access/Ownable.sol";
import "@openzeppelin/contracts/utils/Counters.sol";
contract BeautifulEyesNFT is ERC721, ERC721URIStorage, Ownable {
using Counters for Counters.Counter;
uint256 public saleTimeStamp;
Counters.Counter private _tokenIdCounter;
constructor(uint256 _saleTimeStamp) ERC721("BeautifulEyesNFT", "BEN") {
saleTimeStamp = _saleTimeStamp;
}
function _baseURI() internal pure override returns (string memory) {
return "ipfs://QmZ5WxKWrL7fMFnQpbHcDDGhSA42PPwQiMXL7guB4v6wJd/";
}
function mint(address to) public payable {
require(saleTimeStamp < block.timestamp, "Sale has not started yet");
require(msg.value >= 0.005 ether, "Not Enough ETH");
_tokenIdCounter.increment();
uint256 newItemId = _tokenIdCounter.current();
_mint(to, newItemId);
}
function safeMint(address to, string memory uri) public onlyOwner {
uint256 tokenId = _tokenIdCounter.current();
_tokenIdCounter.increment();
_safeMint(to, tokenId);
_setTokenURI(tokenId, uri);
}
function _burn(uint256 tokenId)
internal
override(ERC721, ERC721URIStorage)
{
super._burn(tokenId);
}
function tokenURI(uint256 _tokenId)
public
view
override(ERC721, ERC721URIStorage)
returns (string memory)
{
string memory currentBaseURI = _baseURI();
return
bytes(currentBaseURI).length > 0
? string(
abi.encodePacked(
currentBaseURI,
uintToString(_tokenId),
".json"
)
)
: "";
}
// utils
function uintToString(uint256 _i)
internal
pure
returns (string memory str)
{
if (_i == 0) {
return "0";
}
uint256 j = _i;
uint256 length;
while (j != 0) {
length++;
j /= 10;
}
bytes memory bstr = new bytes(length);
uint256 k = length;
j = _i;
while (j != 0) {
bstr[--k] = bytes1(uint8(48 + (j % 10)));
j /= 10;
}
str = string(bstr);
}
// setters
function setSaleTimeStamp(uint256 _saleTimeStamp) public onlyOwner {
saleTimeStamp = _saleTimeStamp;
}
}
Now, users should not be able to mint beyond the time limit that we've specified. Again, if you want to deploy this and check, you can do that.
Reveal NFT Metadata After Drop
Normally when someone mints your NFT, they will see the actual metadata at the particular moment. But to create hype around the collection what we can do is just show users a placeholder image and at a fixed date we can reveal the collection to display the real images, metadata etc.
In order to do this, you need to prepare the unrevealed metadata, I am gonna use this as unrevealed metadata.
{
"name": "Beautiful Eyes Not Revealed",
"description": "A NFT Collection That Contains 50 Beautiful Eyes",
"image": "ipfs://QmPAgyCjcMQLag5HM2rJN71Zkt1TFtgLdyqcG8qzum73vh/20.png",
"dna": "?",
"edition": 0,
"date": 1652033960576,
"attributes": [
{
"trait_type": "Background",
"value": "?"
},
{
"trait_type": "Eyeball",
"value": "?"
},
{
"trait_type": "Eye color",
"value": "?"
},
{
"trait_type": "Iris",
"value": "?"
},
{
"trait_type": "Shine",
"value": "?"
},
{
"trait_type": "Bottom lid",
"value": "?"
},
{
"trait_type": "Top lid",
"value": "?"
}
]
}
As you can see I have used ?
to represent the unrevealed metadata and a static image, so before revealing, every NFT will have the same image. You need to upload this to IPFS as well.
First, you need a couple of different variables to get this working, add the following. The notRevealedMetadata
should contain the metadata URL for NFTs that are not revealed and the isReveal
variable is what we toggle to reveal or unreveal NFTs.
string public notRevealedMetadata =
"ipfs://QmWX1bxTYxMtgsnZQA1PwWkVZtM9ZYGfJMsLUBgzPsp1MK";
bool public isRevealed = false;
Then you need to add a function to toggle the isReveal
variable accordingly,
function setIsRevealed(bool _isRevealed) public onlyOwner {
isRevealed = _isRevealed;
}
Then what you need to do is modify the tokenURI
function so that it'll return the hidden metadata URL if the metadata is not revealed.
function tokenURI(uint256 _tokenId)
public
view
override(ERC721, ERC721URIStorage)
returns (string memory)
{
// Add if check here to check if the metadata is not revealed.
if (isRevealed == false) {
return notRevealedMetadata;
}
string memory currentBaseURI = _baseURI();
return
bytes(currentBaseURI).length > 0
? string(
abi.encodePacked(
currentBaseURI,
uintToString(_tokenId),
".json"
)
)
: "";
}
Once you have made those changes, your contract should look like this,
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.4;
import "@openzeppelin/contracts/token/ERC721/ERC721.sol";
import "@openzeppelin/contracts/token/ERC721/extensions/ERC721URIStorage.sol";
import "@openzeppelin/contracts/access/Ownable.sol";
import "@openzeppelin/contracts/utils/Counters.sol";
contract BeautifulEyesNFT is ERC721, ERC721URIStorage, Ownable {
using Counters for Counters.Counter;
uint256 public saleTimeStamp;
string public notRevealedMetadata =
"ipfs://QmWX1bxTYxMtgsnZQA1PwWkVZtM9ZYGfJMsLUBgzPsp1MK";
bool public isRevealed = false;
Counters.Counter private _tokenIdCounter;
constructor(uint256 _saleTimeStamp) ERC721("BeautifulEyesNFT", "BEN") {
saleTimeStamp = _saleTimeStamp;
}
function _baseURI() internal pure override returns (string memory) {
return "ipfs://QmZ5WxKWrL7fMFnQpbHcDDGhSA42PPwQiMXL7guB4v6wJd/";
}
function mint(address to) public payable {
require(saleTimeStamp < block.timestamp, "Sale has not started yet");
require(msg.value >= 0.005 ether, "Not Enough ETH");
_tokenIdCounter.increment();
uint256 newItemId = _tokenIdCounter.current();
_mint(to, newItemId);
}
function safeMint(address to, string memory uri) public onlyOwner {
uint256 tokenId = _tokenIdCounter.current();
_tokenIdCounter.increment();
_safeMint(to, tokenId);
_setTokenURI(tokenId, uri);
}
function _burn(uint256 tokenId)
internal
override(ERC721, ERC721URIStorage)
{
super._burn(tokenId);
}
function tokenURI(uint256 _tokenId)
public
view
override(ERC721, ERC721URIStorage)
returns (string memory)
{
if (isRevealed == false) {
return notRevealedMetadata;
}
string memory currentBaseURI = _baseURI();
return
bytes(currentBaseURI).length > 0
? string(
abi.encodePacked(
currentBaseURI,
uintToString(_tokenId),
".json"
)
)
: "";
}
// utils
function uintToString(uint256 _i)
internal
pure
returns (string memory str)
{
if (_i == 0) {
return "0";
}
uint256 j = _i;
uint256 length;
while (j != 0) {
length++;
j /= 10;
}
bytes memory bstr = new bytes(length);
uint256 k = length;
j = _i;
while (j != 0) {
bstr[--k] = bytes1(uint8(48 + (j % 10)));
j /= 10;
}
str = string(bstr);
}
// setters
function setSaleTimeStamp(uint256 _saleTimeStamp) public onlyOwner {
saleTimeStamp = _saleTimeStamp;
}
function setIsRevealed(bool _isRevealed) public onlyOwner {
isRevealed = _isRevealed;
}
}
Deploying The Smart Contract
Now that the smart contract is done, we need to deploy it to actually use it, when you're moving your app to production you have to deploy your contract on Main Network but in Ethereum, deploying on Main Network costs huge gas fees, so for sake of this tutorial we are gonna deploy on a test net called Rinkeby
.
We will need something called Ethereum node to deploy a smart contract, there are multiple node providers like Alchemy and Infura, for this tutorial we are going to use Alchemy. First, go ahead and create an account in Alchemy.
Then you can create an application like this,
Then create a new .env
file in your smart-contract
directory and add the following variables.
RINKEBY_URL=https://eth-rinkeby.alchemyapi.io/v2/XXX-XXXXXXXXXXXXXXXXXXXXXXXXX
PRIVATE_KEY=XXXXXXXXXX
You need to set the HTTP URL from Alchemy to the RINKEBY_URL
and you need to get your wallet private key and set it to PRIVATE_KEY
(You will need some Rinkeby in your wallet, use this faucet to get it if you don't have already).
Read this to learn how to export the private key from your wallet.
Now open the hardhat config file in your smart-contract
directory and in the exporting object, add a new key called networks
and modify it like this.
module.exports = {
solidity: "0.8.4",
networks: {
rinkeby: {
url: process.env.RINKEBY_URL || "",
accounts:
process.env.PRIVATE_KEY !== undefined ? [process.env.PRIVATE_KEY] : [],
},
},
};
Now let's move on to the scripts
directory, you will see a file called deploy.js
. Open that file, here is how we deploy the contract. The main thing you should pay attention to is this line.
const beautifulEyesNFT = await BeautifulEyesNFT.deploy(1652017663);
Here, as parameters, we need to pass everything that we expect from the contract's constructor. In this case the sale starting time. Once you have prepared that open your terminal and navigate to the smart-contract
directory and run the following command.
npx hardhat run scripts/deploy.js --network rinkeby
If everything went okay you should see the contract address in the terminal like this.
Congratulations ๐. Now you have deployed your smart contract to the Rinkeby test network.
Creating A DAPP To Mint NFTs
Now that the contract is deployed, all we have to do is build a frontend to allow the user to mint the NFTs.
As always the starter for the frontend is available inside the client
folder in the Github repo. In the starter, I have implemented the design and installed the dependencies that we are gonna use.
For this tutorial, I am going to use a really interesting library that I found recently called wagmi
. It is essentially a library to interact with the blockchain from a react application.
First, go ahead and open index.tsx
in the pages directory, here we will have to do a bunch of imports from the wagmi
library. You can add these lines to the very top of the file.
import {
useAccount,
useConnect,
useNetwork,
useContractWrite,
chain,
} from "wagmi";
import { InjectedConnector } from "wagmi/connectors/injected";
// Make sure to import ethers as well
import { ethers } from "ethers";
You will also need the artifacts from the solidity contract. Once you have deployed your contract a folder called artifacts will be generated. Find the JSON file with the name of the contract and copy that and put that somewhere in the client
folder. Then make sure to import it in the index.tsx
like this.
import contractInterface from "../lib/BeautifulEyesNFT.json";
Then we'll initialize these hooks
// Used to change the button text dynamically
const [buttonMessage, setButtonMessage] = useState("Mint NFT");
// Used to connect to the wallet
const { connect } = useConnect({
connector: new InjectedConnector(),
});
// Used to connect to the correct network
const { activeChain, switchNetworkAsync } = useNetwork();
// Used to get wallet information
const { data: account } = useAccount();
// Call the minting function in the contract with 0.005 ETH
const { writeAsync } = useContractWrite(
{
// Update your contract address and pass the `abi` object in the artifact to contract interface.
addressOrName: "0x40ac75b32e02bcb48e24c7a2061b399446157f14",
contractInterface: contractInterface.abi,
},
"mint",
{
args: [account?.address],
overrides: {
gasLimit: 3000000,
value: ethers.utils.parseEther("0.005"),
},
}
);
Then we'll need some lifecycle hooks to keep the wallet connected to the network we need.
useEffect(() => {
switchNetworks();
}, [account]);
useEffect(() => {
switchNetworks();
}, [activeChain?.id]);
const switchNetworks = async () => {
if (account && activeChain?.id != chain.rinkeby.id && switchNetworkAsync) {
setButtonMessage("Switching Network...");
await switchNetworkAsync(chain.rinkeby.id);
setButtonMessage("Mint NFT");
}
};
What we are essentially doing here, is checking what is the network and switching according to every time the user manually changes the network or when they connect to metamask.
Then we'll render the button conditionally according to the minting status/wallet connected status
{!account ? (
<button
className="w-full bg-[#a04edf] rounded-md mt-5 text-white py-3"
onClick={() => {
connect();
}}
>
Connect Wallet
</button>
) : (
<button
className="w-full bg-[#a04edf] rounded-md mt-5 text-white py-3 animate-pulse disabled:bg-[#a04edf]/30 disabled:animate-none disabled:cursor-none"
disabled={buttonMessage != "Mint NFT"}
onClick={async () => {
setButtonMessage("Minting...");
await writeAsync();
setButtonMessage("Check Your NFT On Opensea ๐");
}}
>
{buttonMessage}
</button>
)}
Make sure to add your contract address and the ABI in the correct places as mentioned above.
If you put a past time when deploying, you should be able to mint now.
Change Variables In The Smart Contract
If you want to change the information in the smart contract like saleTimeStamp
or isRevealed
there are a few ways you can do that, for now, we're gonna use Remix IDE to do that.
- Open the remix IDE and create a new contract.
- Copy and paste all your smart contract code to remix.
- Compile the contract.
- Selected
Injected Web3
as the provider and connect Metamask. - Paste your smart contract address and interact with the contract.
Here is how you do it in detail,
Revealing the NFTs
Once it is time for NFT reveal, you can follow the steps mentioned above and call the function called setIsRevealed
with true
as an argument. Once you do that opensea won't automatically pick up the new metadata, you either have to update them manually from opensea or you can use a service such as nftrefreshmetadata.com (only work on mainnet) to refresh all the metadata and show the real image.
So, that is pretty much for the article and I hope you enjoyed it. The completed code is available here at the final branch - github.com/osadavc/build-a-minting-dapp
Let me know what you think in the comments ๐๐ป.