DEV Community

Cover image for How to create a Smart Contract to mint an NFT
Emanuel Ferreira
Emanuel Ferreira

Posted on • Updated on

How to create a Smart Contract to mint an NFT

In this article we are going to create a smart contract to mint an NFT on Ethereum.

Setup the project

To start the project we will use the hardhat that will provide us with a boilerplate to deploy the contract, do tests, etc.

Installing the hardhat

We will install hardhat as development dependency using the yarn.

Open a new terminal and run these commands:

mkdir nft-token
cd nft-token
yarn init -y
yarn add hardhat -D 
Enter fullscreen mode Exit fullscreen mode

In the same directory where you installed Hardhat run:

npx hardhat
Enter fullscreen mode Exit fullscreen mode

Select Create an empty hardhat.config.js with your keyboard and hit enter.

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.6.4 👷‍

? What do you want to do? … 
  Create a basic sample project
  Create an advanced sample project
❯ Create an empty hardhat.config.js
  Quit
Enter fullscreen mode Exit fullscreen mode

Creating the files and directories

Let's start by creating a directory called contracts in the root folder.

/node_modules
/contracts
hardhat.config.js
package.json
yarn.lock
Enter fullscreen mode Exit fullscreen mode

After creating the folder we can create our first file of our contract inside the directory, let's call it: factoryNFT.sol

/node_modules
/contracts
 | - factoryNFT.sol
hardhat.config.js
package.json
yarn.lock
Enter fullscreen mode Exit fullscreen mode

Writing the smart contract

Now we go to the most anticipated part of the article, writing our contract.

Note: You can review the basics in my other article: How to create a smart contract to receive donations using Solidity

Let's start defining the pragma version:
factoryNFT.sol

pragma solidity ^0.8.3;
Enter fullscreen mode Exit fullscreen mode

To help us, we will install the openzeppelin contracts package.

yarn add @openzeppelin/contracts  @openzeppelin/hardhat-upgrades
Enter fullscreen mode Exit fullscreen mode

Now we can import it into the contract.

factoryNFT.sol

pragma solidity ^0.8.3;

import "@openzeppelin/contracts/utils/Counters.sol";
import "@openzeppelin/contracts/token/ERC721/extensions/ERC721URIStorage.sol";
import "@openzeppelin/contracts/token/ERC721/ERC721.sol";
Enter fullscreen mode Exit fullscreen mode

We made 3 imports:

Counters: is a useful one that will help us increment a tokenId for each new token.
ERC721URIStorage: we will provide some functions to handle our tokenURI, which contains metadata and image.

ERC721: will we provide some basic functions of the ERC721;

Inheriting the OpenZeppelin contract

Now we can start by defining the name of our contract and inheriting the OpenZeppelin contracts.

factoryNFT.sol

pragma solidity ^0.8.3;

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

contract FactoryNFT is ERC721URIStorage { 

    using Counters for Counters.Counter; 
    Counters.Counter private _tokenIds;

    constructor() ERC721("Factory NFT", "FTN") {
    }
}
Enter fullscreen mode Exit fullscreen mode

Creating our Mint function

Let's start by creating our function calling createToken, where it will receive as a parameter, our tokenURI.

factoryNFT.sol

pragma solidity ^0.8.3;

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

contract FactoryNFT is ERC721URIStorage { 

    using Counters for Counters.Counter; 
    Counters.Counter private _tokenIds;

    constructor() ERC721("Factory NFT", "FTN") {
    }

    function createToken(string memory tokenURI) public returns (uint) {
    }
}
Enter fullscreen mode Exit fullscreen mode

Now let's increment the amount of token in storage and save it in a variable.

factoryNFT.sol

pragma solidity ^0.8.3;

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

contract FactoryNFT is ERC721URIStorage { 

    using Counters for Counters.Counter; 
    Counters.Counter private _tokenIds;

    constructor() ERC721("Factory NFT", "FTN") {
    }

    function createToken(string memory tokenURI) public returns (uint) {
        _tokenIds.increment();
        uint256 newItemId = _tokenIds.current();
    }
}
Enter fullscreen mode Exit fullscreen mode

Now we can put the main function of the contract which is to mint our NFT and set its tokenURI.

factoryNFT.sol

pragma solidity ^0.8.3;

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

contract FactoryNFT is ERC721URIStorage { 

    using Counters for Counters.Counter; 
    Counters.Counter private _tokenIds;

    constructor() ERC721("Factory NFT", "FTN") {
    }

    function createToken(string memory tokenURI) public returns (uint) {
        _tokenIds.increment();
        uint256 newItemId = _tokenIds.current();

        _mint(msg.sender, newItemId);
        _setTokenURI(newItemId, tokenURI);

        return newItemId;
    }
}
Enter fullscreen mode Exit fullscreen mode

Compiling and testing the Contract
Now to test the contract, we are going to compile it and write a test for it.

First of all let's go to the hardhat.config.js file and change the solidity version to ^0.8.3.

hardhat.config.js

/**
 * @type import('hardhat/config').HardhatUserConfig
 */
module.exports = {
  solidity: "^0.8.3",
};
Enter fullscreen mode Exit fullscreen mode

Plugins

Now to test our contract we need to install some plugins for hardhat to help us interact with Ethereum and test our contracts.

yarn add -D @nomiclabs/hardhat-ethers ethers @nomiclabs/hardhat-waffle ethereum-waffle chai
Enter fullscreen mode Exit fullscreen mode

After adding the plugins we need to add a line in hardhat.config.js to setup the plugins.

hardhat.config.js

require("@nomiclabs/hardhat-waffle");
/**
 * @type import('hardhat/config').HardhatUserConfig
 */
module.exports = {
  solidity: "^0.8.3",
};
Enter fullscreen mode Exit fullscreen mode

Writing the test

Now create a directory called test in the root folder and add called: factoryNFT.js

/node_modules
/contracts
/test
 |- factoryNFT.js
hardhat.config.js
package.json
yarn.lock
Enter fullscreen mode Exit fullscreen mode

Now I am going to put the test below and explain each line to understand it.

const { expect } = require("chai");

describe("Minting the token and returning it", function () {
  it("should the contract be able to mint a function and return it", async function () {
    const metadata = "https://opensea-creatures-api.herokuapp.com/api/creature/1" //Random metadata url

    const FactoryContract = await ethers.getContractFactory("FactoryNFT"); // Getting the contract

    const factoryContract = await FactoryContract.deploy(); //Deploying the Contract

    const transaction = await factoryContract.createToken(metadata); // Minting the token
    const tx = await transaction.wait() // Waiting for the token to be minted

    const event = tx.events[0];
    const value = event.args[2];
    const tokenId = value.toNumber(); // Getting the tokenID

    const tokenURI = await factoryContract.tokenURI(tokenId) // Using the tokenURI from ERC721 to retrieve de metadata

    expect(tokenURI).to.be.equal(metadata); // Comparing and testing

  });
});
Enter fullscreen mode Exit fullscreen mode

Now let's configure the package.json to run our script, add this script to your package.json to compile the contract.

  "scripts": {
    "compile": "yarn hardhat compile"
  }
Enter fullscreen mode Exit fullscreen mode

package.json

{
  "name": "hardhat",
  "version": "1.0.0",
  "main": "index.js",
  "license": "MIT",
  "scripts": {
    "compile": "yarn hardhat compile"
  },
  "devDependencies": {
    "@nomiclabs/hardhat-ethers": "^2.0.2",
    "@nomiclabs/hardhat-waffle": "^2.0.1",
    "chai": "^4.3.4",
    "ethereum-waffle": "^3.4.0",
    "ethers": "^5.4.6",
    "hardhat": "^2.6.4"
  },
  "dependencies": {
    "@openzeppelin/contracts": "^4.3.2",
    "@openzeppelin/hardhat-upgrades": "^1.10.0"
  }
}
Enter fullscreen mode Exit fullscreen mode

Now just compile and run the test and we will have the result!.

yarn compile
npx hardhat test
Enter fullscreen mode Exit fullscreen mode

So we come to the end and you can see my next article on how to deploy to rinkeby testnet How to deploy smart contract to Rinkeby Testnet using Infura and Hardhat.

Repository: https://github.com/EmanuelCampos/mint-nft
Follow me on Twitter

Top comments (2)

Collapse
 
real_ai_works profile image
ai_works

Can this be done on a Chromebook?
When I try to input the terminal commands i get Unknown Command error.

Collapse
 
maxwhitehat profile image
Max-Whitehat

Why is node modules not found when I do /node_modules? Do I have to install anything extra for it to work?