Creating a NFT
Pre-requisites
Before we start, you should have learned about creating a ERC20 token. If you haven't, please go back and read that chapter first.
What is a NFT?
A NFT is a non-fungible token. It is a token that is unique and cannot be replaced by another token. It is a token that is not interchangeable with other tokens. It is a token that is unique.
Writing the contract
Create the contract file
Create a new file called NFT.sol
test/
├── contracts
│ ├── NFT.sol # <--- Create this file
├── scripts
├── test
├── hardhat.config.ts
├── package.json
├── tsconfig.json
Importing the ERC721 contract
We will be using the ERC721 contract from OpenZeppelin. This is a contract that implements the ERC721 standard. We will be inheriting from this contract to create our own NFT contract.
import "hardhat/console.sol";
import "@openzeppelin/contracts/token/ERC721/extensions/ERC721URIStorage.sol";
import "@openzeppelin/contracts/utils/Counters.sol";
Inheriting from the ERC721 contract
We will be inheriting from the ERC721 contract from OpenZeppelin.
contract MyNFT is ERC721URIStorage {
Creating a counter
We will be using a counter to keep track of the number of NFTs we have created.
using Counters for Counters.Counter;
Counters.Counter private _tokenIds;
Setting the name and symbol
We will be setting the name and symbol of our NFT.
constructor() ERC721("MyNFT", "NFT") {
}
Creating the mint function
This function will award the player
an NFT whenever it has been called.
function awardItem(address player, string memory tokenURI)
public
returns (uint256)
{
uint256 newItemId = _tokenIds.current();
_mint(player, newItemId);
_setTokenURI(newItemId, tokenURI);
_tokenIds.increment();
return newItemId;
}
Complete contract
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.9;
import "hardhat/console.sol";
import "@openzeppelin/contracts/token/ERC721/extensions/ERC721URIStorage.sol";
import "@openzeppelin/contracts/utils/Counters.sol";
contract MyNFT is ERC721URIStorage {
using Counters for Counters.Counter;
Counters.Counter private _tokenIds;
constructor() ERC721("MyNFT", "NFT") {}
function awardItem(address player, string memory tokenURI)
public
returns (uint256)
{
uint256 newItemId = _tokenIds.current();
_mint(player, newItemId);
_setTokenURI(newItemId, tokenURI);
_tokenIds.increment();
return newItemId;
}
}
Testing the contract
Create the test file
Now we will write a test to test our smart contract.
Open the test
folder and create a new file called MyNFT.test.ts
.
test/
├── contracts
├── scripts
├── test
│ ├── MyNFT.test.ts #<--- create this file
├── hardhat.config.ts
├── package.json
├── tsconfig.json
Deploy the contract in the test
We will be deploying the contract before we start testing it.
We will use a special javascript syntax to get the first and second item from the array.
For example, we have an array [1, 2, 3, 4, 5]
.
if we use const [first, second, ...rest] = [1, 2, 3, 4, 5]
, then first
will be 1
, second
will be 2
, and rest
will be [3, 4, 5]
.
const [owner, otherAddress, ...rest] = await ethers.getSigners();
const MyToken = await ethers.getContractFactory("MyNFT");
const myToken = await MyToken.deploy();
await myToken.deployed();
Testing the mint function
let tx = await myToken.awardItem(owner.address, "https://www.google.com");
await tx.wait();
Check the owner of the token and the tokenURI
let tokenOwner = await myToken.ownerOf(0);
expect(tokenOwner).to.equal(owner.address);
let tokenURI = await myToken.tokenURI(0);
expect(tokenURI).to.equal("https://www.google.com");
Complete test file
import { expect } from "chai";
import { ethers } from "hardhat";
describe("Given MyNFT", function () {
it("Should be able to award", async function () {
const [owner, otherAddress, ...rest] = await ethers.getSigners();
const MyToken = await ethers.getContractFactory("MyNFT");
const myToken = await MyToken.deploy();
await myToken.deployed();
let tx = await myToken.awardItem(owner.address, "https://www.google.com");
await tx.wait();
let tokenOwner = await myToken.ownerOf(0);
expect(tokenOwner).to.equal(owner.address);
let tokenURI = await myToken.tokenURI(0);
expect(tokenURI).to.equal("https://www.google.com");
tx = await myToken.awardItem(
otherAddress.address,
"https://www.google.com/hk"
);
await tx.wait();
tokenOwner = await myToken.ownerOf(1);
expect(tokenOwner).to.equal(otherAddress.address);
tokenURI = await myToken.tokenURI(1);
expect(tokenURI).to.equal("https://www.google.com/hk");
});
});
Deploying the contract
npx hardhat run scripts/deploy.ts --network etherdata
You can download the complete code from here.
We also provided a deployed contract on testnet. The contract address is 0x9ab29c1cc907448Bc081668A09Bfd77338B4D037
.