Photo by Choong Deng Xiang on Unsplash
How to Create an NFT Token
Deploying an NFT Smart Contract on Ethereum
NFT(Non-Fungible Tokens) are tokens used to represent digital assets with inherent properties that are unique and that cannot be exchanged or interchanged(fungible), in many cases, they are used to represent items like images, art, etc. The Ethereum blockchain has a unique standard called the ERC721 for defining the functionality of these tokens. We will follow the template pattern of using Hardhat, Metamask, Alchemy, etc to build our smart contract just like in our other solidity tutorials
Project Setup
mkdir MikNFT
cd MikNFT
npm init -y
npm install --save-dev hardhat
npx hardhat
npm i @openzeppelin/contracts
The code below imports the ERC721 library and inherits all the function implementation for ERC721, it also uses the Counters library which will be used for counting the number of tokens and giving each a unique positive integer value.
The contract is initialized with the name and symbol for the token the mintNFT function takes into two parameters the address where the NFT will be sent and the tokenURI which gives the metadata description of the token, it then returns the unique integer id of the token
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.17;
import "@openzeppelin/contracts/token/ERC721/extensions/ERC721URIStorage.sol";
import "@openzeppelin/contracts/utils/Counters.sol";
contract MikNFT is ERC721URIStorage {
using Counters for Counters.Counter;
Counters.Counter private _tokenIds;
constructor() ERC721("MikNFT", "NFT") {}
function mintNFT(address recipient, string memory tokenURI)
public
returns (uint256)
{
_tokenIds.increment();
uint256 newItemId = _tokenIds.current();
_mint(recipient, newItemId);
_setTokenURI(newItemId, tokenURI);
return newItemId;
}
}
after it has successfully complied with this command,
npx hardhat compile
we'll install the dotenv package to enable us to interact with our environment variables. The environment variables that will be used are the wallet private key, API_URL, and API_KEY, these will be kept in a .env file that will be added to the project's .gitignore list as these variables are quite sensitive and are not to be uploaded to a public repository.
npm install dotenv --save
API_URL = "https://eth-goerli.g.alchemy.com/v2/api-key"
PRIVATE_KEY = "metamask-private-key"
API_KEY = "api-key"
We'll go straight ahead to create our hardhat.config.js in the root folder and deploy.js in the scripts folder respectively, the deploy.js script uses the Ethers.js library to handle the deployment of our smart contract.
require('dotenv').config();
require("@nomiclabs/hardhat-ethers");
const { API_URL, PRIVATE_KEY } = process.env;
module.exports = {
solidity: "0.8.17",
defaultNetwork: "goerli",
networks: {
hardhat: {},
goerli: {
url: API_URL,
accounts: [`0x${PRIVATE_KEY}`]
}
},
};
async function main() {
// Grab Contract Factory
const MikNFT = await ethers.getContractFactory("MikNFT");
// Start deployment, returning a promise that resolves to a contract object
const mikNFT= await MikNFT.deploy(); // Instance of the contract
console.log(
"Contract deployed to address:", mikNFT.address );
}
// We recommend this pattern to be able to use async/await everywhere
// and properly handle errors.
main()
.then(()=> process.exit(0))
.catch((error) => {
console.error(error);
process.exit(1);
});
Let's deploy the smart contract on the goerli testnet
npx hardhat run scripts/deploy.js --network goerli
At the end of the deployment, the contract address will be shown, this is what we'll use to check the details of our transaction on goerli etherscan
Contract deployed to address: 0x52Fb82C6D01F4077F18508C1ab59560c651A9099
You have successfully deployed your NFT smart contract! The next step is to use the contract to mint an NFT
Minting NFT
So we will decide what we want to put up as an NFT, I 'dreamed' up a really cool art piece using an AI image generation tool called Wombo, you can check it out.
The primary way to store NFTs is on IPFS(Interplanetary File System) platforms which are decentralized file-sharing networks for keeping data. We'll use Pinata for our example. After creating an account on the platform, we'll upload our image and copy the hash code or content identifier that was generated from the successful upload, the address of the image will be represented like this https://gateway.pinata.cloud/ipfs/<hash-code>
Then go ahead to create a nft-metadata.json file that provides all the vital attributes for your NFT, make sure the image points to the address of the image, and when this is done make sure this is also uploaded to Pinata and the URL address noted.
{
"attributes" : [ {
"trait_type" : "Background",
"value" : "forest"
}, {
"trait_type" : "flag",
"value" : "Nigeria"
} ],
"description" : "car moving in a country background, flag seen in the skyline, locally dressed man",
"image" : "https://gateway.pinata.cloud/ipfs/Qmcp8yj1rBBSJAHmUq3rd3oYF5Uhy2ftBLGLGG4SpPSvzP",
"name" : "Nairaland"
}
we then create a mint-nft.js file that will execute the minting of our NFT, we'll use the contracts' ABI for that, the ABI is a json formatted interface used to interact with the contract, it is automatically generated by hardhat when the contract is compiled, the ABI for this contract is generated in this location.
const contract = require("../artifacts/contracts/MikNFT.sol/MikNFT.json");
the contract will use the ether library to connect with our node provider using our API key earlier defined in our .env file, it will also use the wallet's private key, the contract's address and the contract's abi to define the contract
require('dotenv').config();
const { AlchemyProvider } = require('@ethersproject/providers');
const ethers= require('ethers');
// Get Alchemy API key
const API_KEY= process.env.API_KEY;
// Define an Alchemy Provider
const provider= new ethers.providers.AlchemyProvider('goerli', API_KEY);
const privateKey=process.env.PRIVATE_KEY;
const signer= new ethers.Wallet(privateKey, provider);
// Get contract ABI and address
const abi=contract.abi;
const contractAddress='0x52fb82c6d01f4077f18508c1ab59560c651a9099';
// Create a contract instance
const myNftContract= new ethers.Contract(contractAddress, abi, signer);
the tokenURI for our NFT image will now be used and defined in our script, this is how the final version of our mint-nft.js script will look like
const contract = require("../artifacts/contracts/MikNFT.sol/MikNFT.json");
require('dotenv').config();
const { AlchemyProvider } = require('@ethersproject/providers');
const ethers= require('ethers');
// Get Alchemy API key
const API_KEY= process.env.API_KEY;
// Define an Alchemy Provider
const provider= new ethers.providers.AlchemyProvider('goerli', API_KEY);
const privateKey=process.env.PRIVATE_KEY;
const signer= new ethers.Wallet(privateKey, provider);
// Get contract ABI and address
const abi=contract.abi;
const contractAddress='0x52fb82c6d01f4077f18508c1ab59560c651a9099';
// Create a contract instance
const myNftContract= new ethers.Contract(contractAddress, abi, signer);
const tokenUri= "https://gateway.pinata.cloud/ipfs/QmQRJK9mMtueyJN9APfQGCdsT1pHELZ9dKN1mcjaRV8qHo";
// Call mintNFT function
const mintNFT = async () => {
let nftTxn = await myNftContract.mintNFT(signer.address, tokenUri)
await nftTxn.wait()
console.log(`NFT Minted! Check it out at: https://goerli.etherscan.io/tx/${nftTxn.hash}`)
}
mintNFT()
.then(() => process.exit(0))
.catch((error) => {
console.error(error);
process.exit(1);
});
run the below command to execute our script
node scripts/mint-nft.js
Our output will be like this
NFT Minted! Check it out at: https://goerli.etherscan.io/tx/0x907e775d3b45507eb17af5bd97b65c58457b7354354157c7ef148e32f2f8f31c
Awesome! what cool NFT project can you spin-off? Happy NFTing!