a
Polygon

How to Write a Hardhat NFT Smart Contract for Polygon

This guide will show you the ropes of working with HardHat, a widely-used framework for creating smart contracts. We'll learn how to construct a powerful NFT project that can transform any data type into an NFT. We will also cover the basics of the Hardhat framework for creating, testing, and deploying smart contracts. You should also be familiar with public and private keys, and ideally have some experience with Metamask. Knowledge of Solidity, Javascript, and Node.js is also highly desirable.

Requirements

  • Make sure the following programs are running on your computer:
  • HardHat and other node packages require Node.js version 14.17.6 LTS or later.
  • To interface with the blockchain, use MetaMask.
  • Install MetaMask, then link it to the Polygon Mumbai testnet.
  • Don’t forget to use the Polygon faucet to load up on test net MATIC tokens.

Need help on AWS?

AWS Partners, such as AllCode, are trusted and recommended by Amazon Web Services to help you deliver with confidence. AllCode employs the same mission-critical best practices and services that power Amazon’s monstrous ecommerce platform.

Developing a Polygonscan API Key

After putting our contract on the blockchain (mainnet or testnet), it’s a good idea to double-check the code. If our smart contract is legitimate, its code will be viewable on the block explorer, and users will be able to interact with it from the explorer itself (such as Polygonscan). Having the source code checked out is a great way to increase trust in our endeavor and get people to use it.

By installing a HardHat plugin, smart contracts can be automatically checked before being put into production. One Polygonscan API key will be required for this task. If you want an API key of your own, do as follows:

  • Load up Polygonscan.
  • You can sign in by selecting the SignIn link in the top right corner of the page.
  • Log in with your existing credentials by visiting https://polygonscan.com/login or register for a new account at https://polygonscan.com/registration.
  • Navigate to API-KEYs in the left-hand menu once you’ve logged in.
  • To create a new one, select the “Add” button, label it, and then press the “Continue” button.

With your new API key, you can begin using the Polygonscan API to do things like verify contracts. Both the production network and the test network will use the same key.

The process of making a HardHat project

The following command will install HardHat:

npm install -g hardhat

With HardHat now worldwide installed, we can use the npx command to initiate new HardHat-based projects in the future.

We’ll use the following instructions to build our program.

mkdir art_gallery # I am naming my project folder as art_gallery but any other name works
cd art_gallery    # move into the directory
npx hardhat

After entering that final command, you should get a screen that looks like this:

 

Learning the Code

Let’s get our project up and running and check it out. Although Visual Studio Code (VSCode) is the editor I’ll be using, you’re welcome to use whatever IDE suits your needs best.

A very basic framework for the project is what we get. The folders’ names are quite descriptive. We’ll have separate directories for our smart contracts, scripts, and test scripts (folders).

The file hardhat.config.js stores all the settings for HardHat.

Before we get into actually developing our smart contract, let’s have a look at the hardhat.config.js file that serves as the beating heart of our HardHat project. You’ll find the following in this file when you first open it:

require("@nomiclabs/hardhat-waffle");

// This is a sample Hardhat task. To learn how to create your own go to
// https://hardhat.org/guides/create-task.html
task("accounts", "Prints the list of accounts", async (taskArgs, hre) => {
  const accounts = await hre.ethers.getSigners();

  for (const account of accounts) {
    console.log(account.address);
  }
});

// You need to export an object to set up your config
// Go to https://hardhat.org/config/ to learn more

/**
 * @type import('hardhat/config').HardhatUserConfig
 */
module.exports = {
  solidity: "0.8.4",
};

To get started using the hre class, we must first import the @nomiclabs/hardhat-waffle package. The acronym HRE stands for “Hardhat Runtime Environment” and refers to an object that contains all of the features that HardHat makes available to the user. Just remember that “HardHat is hre” if you need to.

A variety of jobs are defined in NNext and can be executed with the command npx hardhat TASK NAME>.

As part of our project’s development, we’re also creating specialized scripts to carry out certain functions.

The file’s final section, module.export, is where we’ll specify settings like the compiler’s version, the networks to use, API keys, and so on. Specifically, the solidity version 0.8.4 has been defined here.

Put in the OpenZeppelin software package.

We usually try to use as many pre existing libraries as possible while developing new software, rather than starting from scratch. Since this is an NFT-based project, we’ll be adhering to the guidelines laid down in EIP-721. The easiest method to achieve this is to modify the existing ERC721 contract in the OpenZeppelin contracts library to suit our needs. Launch a terminal and type in the following command to install the package:

npm install @openzeppelin/contracts

Initiating the Smart Contract

In the contracts folder, let’s make a new file called Artwork.sol. Our first attempt at using a smart contract to facilitate the formation of NFTs.

 

To kick things off, let’s define our smart contract’s License. We have decided to leave this lesson without a license. We’ll get a warning at compilation time if we don’t specify the license. For this reason, the pragma keyword is required to specify the Solidity version that was used for compilation. Verify that you are utilizing the same version of Solidity that is specified in the hardhat.config.js file.

Next, we’ll use the OpenZeppelin library we just loaded to bring in an ERC721 smart contract. Import the ERC721 contract after the line specifying the Solidity version but before the contract definition:

import "@openzeppelin/contracts/token/ERC721/ERC721.sol";

Constructor initialization and inheriting ERC721

Update the code to include the following:

//SPDX-License-Identifier: Unlicense
pragma solidity 0.8.4; 

import "@openzeppelin/contracts/token/ERC721/ERC721.sol";

contract Artwork is ERC721 {

    constructor(
        string memory name,
        string memory symbol
    ) ERC721(name, symbol) {}

}

Here’s what we’re up to:

Our Artwork will incorporate the OpenZeppelin ERC721 smart contract.

use the is keyword in a sol smart contract.

When a smart contract is being deployed, the very first thing that happens is the function Object() { [native code] } is called. When building our own function Object() { [native code] }, we must include the values for the inherited smart contract’s function Object() { [native code] }. In this case, we’re calling ERC721’s function Object() { [native code] } with a name and symbol as parameters.

In this case, our NFT will be known by the name and use the symbol.

 

Definition of Token Count

Each NFT is completely unique, hence the name “Non-Fungible Token.” A token’s one-of-a-kindness is determined by its id token. Token ids will be determined by a new global variable that we’ll name tokenCounter. For each NFT added, it will begin at 0 and increase by 1. (or “minted”). The function Object() { [native code] } initialises tokenCounter to 0.

Developing the Mint Operation

We’ll get started by defining a mint function that can be used by anyone to create new NFTs. Specific information related to each NFT will be stored. Since we are basing the NFT on photographs or other valuables, the smart contract will need a way to keep track of the image. The full image and all of its metadata can’t be saved on the blockchain because doing so would be too expensive. The image and the JSON file describing the NFT will need to be hosted on different servers. The image and JSON file can be hosted independently in either a decentralized setting (through IPFS) or a centralized setting (via, say, a web server). The image URL is also included in the JSON file for your convenience. Once the JSON file has been hosted, the URI (which stands for “Universal Resource Identifier”) linking to that file is recorded in the blockchain as tokenURI. An instance of a tokenURI that is hosted in a centralized location.

With this in mind, each NFT associated with the smart contract is generated using the mint function.

New NFTs can also be minted with the help of the _safeMint function, which is included in the OpenZeppelin ERC721 contract. Both the to and from parameters are required. The first input field should be the address of the account that will eventually be the NFT’s owner.

tokenId: The newly issued NFT’s tokenId is the second input.

The msg.sender keyword is used to get the account’s address that is called the smart contract. In this scenario, it would provide back the account details of the person using the mint feature. The newly minted NFT will be owned by the account that called the mint function, which is why this account’s credentials must be provided as the function’s first argument.

For the time being, you can safely disregard calls to the undefined _setTokenURI() method. The tokenURI for the newly created NFT will be set using this function. Since the ERC721 library no longer supports this function as of Solidity 0.8.0, we will have to write it ourselves.

After a token is created and its tokenURI is stored, the tokenCounter is increased by one for the next token to be created.

Writing the _setTokenURI() method

Token identifiers and their corresponding tokenURIs must be stored in our NFT smart contract. This can be accomplished with the help of Solidity’s mapping data type. Mappings perform the same function as hashmaps do in languages like Java. For each token id, we may build a mapping from uint256 to a string that identifies the token’s corresponding tokenURI. Define the mapping immediately below the token counter variable declaration:

mapping (uint256 => string) private _tokenURIs;

The _setTokenURI code has to be created now.

function _setTokenURI(uint256 _tokenId, string memory _tokenURI) internal virtual {
    require(
        _exists(_tokenId),
        "ERC721Metadata: URI set of nonexistent token"
    );  // Checks if the tokenId exists
    _tokenURIs[_tokenId] = _tokenURI;
}

Developing the Token URI () Process

TokenURI() is the final function we need to implement. This will be a publically accessible method that accepts a tokenId as an input and returns the corresponding tokenURI. OpenSea and other NFT-based platforms all make use of this standard function. Information about the NFT, such as its characteristics and display picture, can be displayed on such platforms via the tokenURI returned by this function.

function tokenURI(uint256 _tokenId) public view virtual override returns(string memory) {
    require(
        _exists(_tokenId),
        "ERC721Metadata: URI set of nonexistent token"
    );
    return _tokenURIs[_tokenId];
}

To sum up

When all the pieces are in place, the final form of the smart contract will look like:

//SPDX-License-Identifier: Unlicense
pragma solidity 0.8.4; 

import "@openzeppelin/contracts/token/ERC721/ERC721.sol";

contract Artwork is ERC721 {

    uint256 public tokenCounter;
    mapping (uint256 => string) private _tokenURIs;

    constructor(
        string memory name,
        string memory symbol
    ) ERC721(name, symbol) {
        tokenCounter = 0;
    }

    function mint(string memory _tokenURI) public {
        _safeMint(msg.sender, tokenCounter);
        _setTokenURI(tokenCounter, _tokenURI);

        tokenCounter++;
    }

    function _setTokenURI(uint256 _tokenId, string memory _tokenURI) internal virtual {
        require(
            _exists(_tokenId),
            "ERC721Metadata: URI set of nonexistent token"
        );  // Checks if the tokenId exists
        _tokenURIs[_tokenId] = _tokenURI;
    }

    function tokenURI(uint256 _tokenId) public view virtual override returns(string memory) {
        require(
            _exists(_tokenId),
            "ERC721Metadata: URI set of nonexistent token"
        );
        return _tokenURIs[_tokenId];
    }

}

Building the smart contract

Run the following code:

npx hardhat compile

The message “Compilation ended successfully” will show up if the compilation was successful. If the contract doesn’t compile or there are issues, you can try to figure out what went wrong by rereading the tutorial. Among the possible errors that could occur are:

  • No SPDX-License-Identifier is given.
  • The version of Solidity compiler set with the pragma keyword does not match the version set in hardhat.config.js.
  • The imported smart contract was written in an older version of Solidity than the one used to construct our contract. To fix this, make sure you’re installing the correct version of the OpenZeppelin contracts using npm.

 

Conclusion

In this blog the fundamentals of HardHat were covered in detail. After developing a smart contract that can generate NFTs, testing it, and deploying it to the Mumbai testnet, we have a working implementation. The integration of a HardHat plugin and the use of a Polygonscan API key allowed us to independently verify the integrity of our contract. Following the same steps, we may construct many DeFi projects and send them out for deployment on any network that supports the EVM standard (Ethereum, Polygon, Binance Smart Chain, Avalanche, etc.).

Free AWS Services Template

Download our free PDF list of all AWS services. In this list, you will get all of the AWS services in a PDF file that contains  descriptions and links on how to get started.

Related Articles

3 Ways Gen AI and AWS can Enhance Your Business

3 Ways Gen AI and AWS can Enhance Your Business

Amazon is on the cutting edge of new technologies. They have been increasingly experimenting with AI and learning algorithms, culminating in their most recent breakthroughs in Generative AI. Developers and technology enthusiasts have access to their innovations through the tools available on AWS.

Business Owner’s Guide to DevOps Essentials

Business Owner’s Guide to DevOps Essentials

As a business owner, it’s essential to maximize workplace efficiency. DevOps is a methodology that unites various departments to achieve business goals swiftly. Maintaining a DevOps loop is essential for the health and upkeep of deployed applications.