How to Create an NFT on Mantle Tutorial Part I — Deploying the NFT

02/22/2319 min read

Mantleby Mantle




How to Create an NFT on Mantle Tutorial Part I — Deploying the NFT

This tutorial will walk you through writing and deploying a non-fungible (ERC-721) token smart contract using Hardhat and ethers.js!

With the popularity of NFTs bringing blockchain technology to the forefront of public attention, there is no better time than now to gain a firsthand understanding of the excitement. You can do this by creating and publishing your very own NFT (ERC-721 token) on the Mantle Testnet.

In this three-part tutorial, we will walk through creating and deploying an ERC-721 smart contract on Mantle Testnet using MetaMaskSolidityHardhat and Pinata.

Part I will highlight the initial setup, creation and deployment of the contract!

In Part II of this tutorial, we’ll go through how we can use our smart contract to mint an NFT from code, and in Part III we’ll cover how to View Your NFT in Your Mobile Wallet.

And of course, if you have questions at any point, don’t hesitate to reach out on Mantle’s Discord.

Part 1: Creating an NFT


Before you begin the steps in this tutorial, please ensure that you have completed the following steps:

  • Install both Node.js (>14) and npm on your local machine. To check your Node version, run the following command in your terminal:
node -v

Step 1: Create a Node Project

Create an empty Node project — navigate to your command line and type:

mkdir my-nft && cd my-nft
npm init -y

Step 2: Create a Hardhat Project

Hardhat is a development environment to compile, deploy, test, and debug smart contracts. It helps developers create dApps locally before deploying them to a live chain.

In your terminal, run the following commands:

npm install --save-dev hardhat
npx hardhat

You should then see a welcome message and options on what you can do. Use your “arrow keys” to navigate the small menu and Select Create a JavaScript project by clicking “Enter”.

Agree to all the prompts (project root, adding a .gitignore, and installing all sample project dependencies).

Now that we’re done with setting up Hardhat, let’s check if everything works properly.

npx hardhat test  

0 Zhtz S5lt Vsbt71 Ic

Step 3: Install OpenZeppelin Contracts Package

We now have our hardhat development environment successfully configured. Let us now install the OpenZeppelin contracts package. This will give us access to ERC-721 implementations (one of the standards for NFTs alongside ERC-1155), on top of which we will build our contract.

npm install @openzeppelin/contracts

Note: You might encounter some alerts about vulnerabilities like in the screenshot below, please ignore them as they aren’t affecting our deployment of the contract.

0 2ze Kp Pfu F1 Zazt Ic (1)

0 D29v Le L C D Mmp Oc D

Step 4: Open the OpenZeppelin Contracts Package

Open the project with a project of your liking (e.g. VSCode). Note to open the project and navigate to the folder where the project was created in the first step.

For example, on Mac, we will use a language called Solidity to write our contract.

Navigate to the contracts folder and create a new file called MyNFT.sol. Add the following code to the file.

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.17;

import "@openzeppelin/contracts/token/ERC721/extensions/ERC721URIStorage.sol";
import "@openzeppelin/contracts/utils/Counters.sol";
import "@openzeppelin/contracts/access/Ownable.sol";

contract MyNFT is ERC721URIStorage, Ownable {
using Counters for Counters.Counter;
Counters.Counter private _tokenIds;

constructor() ERC721("MyNFT", "NFT") {}

function mintNFT(address recipient, string memory tokenURI)
returns (uint256)

uint256 newItemId = _tokenIds.current();
_mint(recipient, newItemId);
_setTokenURI(newItemId, tokenURI);

return newItemId;

Make sure that the version defined above (⁰.8.17) is the same as the version defined in the hardhat.config.js file.

Now, Let’s break down the code line by line. In MyNFT.sol, our code inherits three OpenZepplin smart contract classes:

  • @openzeppelin/contracts/token/ERC721/extensions/ERC721URIStorage.sol contains the implementation of the ERC-721 standard, which our NFT smart contract will inherit. (To be a valid NFT, your smart contract must implement all the methods of the ERC-721 standard.)
  • @openzeppelin/contracts/utils/Counters.solprovides counters that can only be incremented or decremented by one. Our smart contract uses a counter to keep track of the total number of NFTs minted and set the unique ID to our new NFT. Each NFT minted using a smart contract must be assigned a unique ID — here our unique ID is just determined by the total number of NFTs in existence.
  • @openzeppelin/contracts/access/Ownable.sol sets up access control on our smart contract, so only the owner of the smart contract (you) can mint NFTs.

Then we have our custom NFT smart contract, which is surprisingly short — it only contains a counter, a constructor, and a single function!

This is thanks to our inherited Open Zeppelin contracts, which implement most of the methods we need to create an NFT, such as ownerOf (returns the owner of the NFT) and transferFrom (transfers ownership of the NFT).

You’ll also notice we pass 2 strings, “MyNFT” and “NFT” into the ERC-721 constructor. The first variable is the smart contract’s name, and the second is its symbol. You can name each of these variables whatever you wish!

Finally, starting on we have our function mintNFT() that allows us to mint an NFT! You’ll notice this function takes in two variables:

  • address recipient specifies the address that will receive your freshly minted NFT
  • string memory tokenURI is a string that should resolve to a JSON document that describes the NFT’s metadata.
  • An NFT’s metadata is really what brings it to life, allowing it to have additional properties, such as a name, description, image, and other attributes. In Part II of this tutorial, we will describe how to configure this metadata.

Lastly, mintNFT calls some methods from the inherited ERC-721 library, and ultimately returns a number that represents the ID of the freshly minted NFT.

Step 5: Connect MetaMask & Mantle to Your Project

Now that we’ve created a MetaMask wallet and a smart contract, it’s time to connect both with Mantle Testnet.

Every transaction sent from your virtual wallet requires a signature using your unique private key. To provide our program with this permission, we can safely store our private key in an environmental file.

Install the dotenv package in your project directory by running.

npm install dotenv --save

Then, create a .env file in the root directory (Main Folder) of your project, and add your MetaMask private key.

Note: The file name should be “.env” only, not “xyz.env” or “abc.env”

Having issues exporting your private key from MetaMask? Here’s a quick guide to help you out.

Your .env should look like this:

PRIV_KEY = 0x"your exported private key"

Note: make sure to replace your exported private key with “your exported private key”.

Step 6: Update hardhat.config.js

We’ve added several dependencies and plugins so far. Now we need to update hardhat.config.js, so that your project knows about all of them.

Replace the contents of hardhat.config.js with the following:


/** @type import('hardhat/config').HardhatUserConfig */
module.exports = {
solidity: "0.8.17",
networks: {
"mantle-testnet": {
url: "",
accounts: [process.env.PRIV_KEY], // Uses the private key from the .env file

Step 7: Write the Deployment Script

Now that your contract is written, and our configuration file is good to go, it’s time to write the contract deploy script.

Navigate to the scripts/ folder and replace the contents in the file deploy.js with the following:

async function main() {
// Grab the contract factory
const MyNFT = await ethers.getContractFactory("MyNFT");

// Start deployment, returning a promise that resolves to a contract object
const myNFT = await MyNFT.deploy(); // Instance of the contract
console.log("Contract deployed to address:", myNFT.address);

.then(() => process.exit(0))
.catch(error => {

One of the best features about Harhat is that it gives a line by line explanation in their Contract Tutorial. You’ll find similar explanation below

const MyNFT = await ethers.getContractFactory("MyNFT");

A ContractFactory in ethers.js is an abstraction used to deploy new smart contracts, so MyNFT here is a factory for instance of our NFT contract. When using the hardhat-ethers.js plugin ContractFactory and Contract instances are connected to the first signer by default.

const myNFT = await MyNFT.deploy();

Calling deploy() on a ContractFactory will start the deployment, and return a Promise that resolves to a Contract. This is the object that has a method for each of our smart contract functions.

Step 8: Deploy the Contract

We’re finally ready to deploy our smart contract! Navigate back to the root of your project directory, and in the command line run:

npx hardhat run scripts/deploy.js --network mantle-testnet

You should have an output similar to the one shown below ⬇️

0 D W Vu Wixb D4 O Tj A1e

Now that our contract is deployed, we can go to Mantle Explorer and check if our contract was deployed.

0 T JS  V Kh B U  S Rr

Congrats! You just deployed your NFT smart contract to the Mantle Testnet 🎉 🥳

That’s all for Part I of this tutorial. In Part II, we’ll actually interact with our smart contract by minting an NFT, and in Part III we’ll explain how to view your NFT in MetaMask! 😎

Join the Community