ERC-721 (NFT) Smart Contract Deployment— using Hardhat

A guide on how to write and deploy ERC-721 standard smart contract

As part of this article, we are trying to write and deploy the ERC-721 standard smart contract on Polygon Mumbai Testnet using the Hardhat configuration.

What are smart contracts? Contracts which are smart in some way? — By removing intermediary involvements.

Smart Contracts

Smart contracts are like contracts in the real world. The only difference is that they are completely digital. It is a computer program that we can deploy to a blockchain and can keep track of the ownerships. It automates the execution of the agreement without any time loss or intermediary involvement. Once a smart contract is created, it can never be changed again. Smart contracts can be programmed in a special programming language called Solidity.

ERC 721

ERC 721 is the token standard for NFT on the Ethereum blockchain. They define a set of rules for NFT and provide the ability to get token-specific information like token balance, total supply, and owner of the token.


Hardhat is a development environment where we can compile, run, deploy, debug and test our smart contracts.

Enough of the lecture! now make it handy.

Step 1: Initialize the project

npm init

It will create a new package.json file which you can edit accordingly.

"name": "MyArgramNFT",
"version": "1.0.0",
"description": "",
"main": "index.js",
"engines": {
"node": "12.19.0"
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"start": "nodemon index.js"
"keywords": [],
"author": "",
"license": "ISC",

Step 2: Write smart contract

Create a folder contracts/ and create a new file called MyArgramNFT.sol.

Make sure to install OpenZeppelin library as we are extending classes from OpenZeppelin Contracts library. npm install @openzeppelin/contracts.

Copy the following code to your smart contract file.

// SPDX-License-Identifier: MIT
pragma solidity >=0.8.0 <0.9.0;
import “@openzeppelin/contracts/token/ERC721/ERC721.sol”;
import “@openzeppelin/contracts/utils/Counters.sol”;
import “@openzeppelin/contracts/access/Ownable.sol”;
contract ArGram is ERC721, Ownable {
using Counters for Counters.Counter;
Counters.Counter private _tokenIds;
using Strings for uint256;
mapping(uint256 => string) private _tokenURIs;
// Base URI
string private _baseURIextended;
constructor() ERC721(“ARGRAM”, “ARGRAM”) {}
function setBaseURI(string memory baseURI_) external onlyOwner {
_baseURIextended = baseURI_;
function _setTokenURI(uint256 tokenId, string memory _tokenURI)
“ERC721Metadata: URI set of nonexistent token”
_tokenURIs[tokenId] = _tokenURI;
function _baseURI() internal view virtual override returns (string memory) {
return _baseURIextended;
function tokenURI(uint256 tokenId)
returns (string memory)
“ERC721Metadata: URI query for nonexistent token”
string memory _tokenURI = _tokenURIs[tokenId];
string memory base = _baseURI();
// If there is no base URI, return the token URI.
if (bytes(base).length == 0) {
return _tokenURI;
// If both are set, concatenate the baseURI and tokenURI (via abi.encodePacked).
if (bytes(_tokenURI).length > 0) {
return string(abi.encodePacked(base, _tokenURI));
// If there is a baseURI but no tokenURI, concatenate the tokenID to the baseURI.
return string(abi.encodePacked(base, tokenId.toString()));
function mintNFT(address recipient, string memory _tokenURI)
public onlyOwner
returns (uint256)
uint256 newItemId = _tokenIds.current();
_mint(recipient, newItemId);
_setTokenURI(newItemId, _tokenURI);
return newItemId;
view raw smart contract hosted with ❤ by GitHub

Step 3: Install Hardhat & Ethers.js

npm install –save-dev hardhat
npm install –save-dev @nomiclabs/hardhat-ethers ‘[email protected]^5.0.0’

Step 4: Create Hardhat project

npx hardhat

You will get a prompt like below and select “create an empty hardhat.config.js”. It will create an empty hardhat.config.js file in your project folder.

888    888                      888 888               888
888 888 888 888 888
888 888 888 888 888
8888888888 8888b. 888d888 .d88888 88888b. 8888b. 888888
888 888 "88b 888P" d88" 888 888 "88b "88b 888
888 888 .d888888 888 888 888 888 888 .d888888 888
888 888 888 888 888 Y88b 888 888 888 888 888 Y88b.
888 888 "Y888888 888 "Y88888 888 888 "Y888888 "Y888

Welcome to Hardhat v2.8.0

? What do you want to do? ...
Create a basic sample project
Create an advanced sample project
Create an advanced sample project that uses TypeScript
> Create an empty hardhat.config.js

Step 5: Update hardhat.config.js

* @type import(‘hardhat/config’).HardhatUserConfig
const { API_URL, PRIVATE_KEY } = process.env;
module.exports = {
solidity: “0.8.9”,
defaultNetwork: “mumbai”,
networks: {
hardhat: {},
mumbai: {
url: API_URL,
accounts: [`0x${PRIVATE_KEY}`],

Make sure your .env file contains API_URL and PRIVATE_KEY.

Depending on which node provider you are using to request the blockchain, the API_URL will differ. You can use InfuraAlchemy, or Moralis as the node provider. All the above node providers offer free accounts and you can create one and will get the RPC URL to connect with.

Your .env file should look like below:

PRIVATE_KEY = "Metamask Private Key"

Step 6: Compile smart contract

npx hardhat compile

Step 7: Write deploy script

Create another folder called scripts/ and create a new file called deploy.js and add the following content to it.

async function main() {
const ArGramNFT = await ethers.getContractFactory(“ArGram”);
const argramNFT = await ArGramNFT.deploy();
const txHash = argramNFT.deployTransaction.hash;
const txReceipt = await ethers.provider.waitForTransaction(txHash); console.log(“Contract deployed to address:”, txReceipt.contractAddress);
.then(() => process.exit(0))
.catch((error) => {

Step 8: Get some fake MATIC

As blockchain transactions are bound to gas fees, we need to collect some fake MATIC to deploy our smart contract.

Get some fake MATIC from here:

Other MATIC faucets available:


Step 9: Deploy smart contract

npx hardhat run scripts/deploy.js --network mumbai

You will get a console output like below:

Contract deployed to address: 0xF57dB8d93b987027ca2B4D87c7D3a0201b92329f

Now you can verify the deployed contract address on mumbai polygonscan. The transaction will look something like below:

Great! You have successfully deployed your first smart contract on Polygon Mumbai blockchain.