Create a Smart Contract | Write and Deploy an NFT project on the Rootstock Testnet

Create your NFT Smart Contract

In your root directory, start by creating a new directory called contracts and create a file inside the directory called Meow.sol.

mkdir contracts
touch contracts/Meow.sol
code contracts/Meow.sol

Below is our NFT smart contract code, which is based on the OpenZeppelin library’s ERC-721 implementation. Copy and paste the contents below into your Meow.sol file.

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
// using OpenZeppelin libraries
import "@openzeppelin/contracts/token/ERC721/ERC721.sol";
import "@openzeppelin/contracts/utils/Counters.sol";
import "@openzeppelin/contracts/access/Ownable.sol";
import "@openzeppelin/contracts/token/ERC721/extensions/ERC721URIStorage.sol";

contract Meow is ERC721URIStorage, Ownable {
    /** 
    From the `Counters` docs:
    Provides counters that can only be incremented, decremented or reset. 
    This can be used e.g. to track the number
    of elements in a mapping, issuing ERC721 ids, or counting request ids.
    Include with `using Counters for Counters.Counter;` 
    */

    using Counters for Counters.Counter;
    // tracks the number of minted NFTs
    Counters.Counter private _tokenIds;

    // calling ERC721 constructor
    constructor() ERC721("Meow NFT", "MEO") {}

    // mints new NFTs. Can be called only by the deployer (s/c owner)
    function mintNFT(address recipient, string memory tokenURI)
        public onlyOwner
        returns (uint256)
    {
        // increment counter
        _tokenIds.increment();
        // get new NFT id
        uint256 newItemId = _tokenIds.current();
        // call internal ERC721 mint function
        _mint(recipient, newItemId);
        // write token URI to newly minted NFT
        _setTokenURI(newItemId, tokenURI);

        return newItemId;
    }
}

We are inheriting classes from the OpenZeppelin contracts library, in the command line run npm install @openzeppelin/contracts to install the library into our folder.

So, what does this code do exactly? Let’s break it down, line-by-line.

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

At the top of our smart contract, we import three OpenZeppelin smart contract classes and one extension:

  • @openzeppelin/contracts/token/ERC721/ERC721.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. To learn more about the inherited ERC-721 functions, see the full ERC-721 specification.

  • @openzeppelin/contracts/utils/Counters.sol provides 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 on 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. For example, the first NFT we mint with our smart contract has an ID of 1, our second NFT has an ID of 2, etc.)

  • @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. Note that including access control is entirely a preference. If you'd like anyone to be able to mint an NFT using your smart contract, remove the word Ownable on line 10 and onlyOwner on line 17.

  • @openzeppelin/contracts/token/ERC721/extensions/ERC721URIStorage.sol - This is a more flexible but more expensive way of storing metadata.

    using Counters for Counters.Counter;
    // tracks the number of minted NFTs
    Counters.Counter private _tokenIds;

    // calling ERC721 constructor
    constructor() ERC721("Meow NFT", "MEO") {}

After our import statements, we have our custom NFT smart contract, which is surprisingly short — it only contains a counter, a constructor, and single function! This is thanks to our inherited OpenZeppelin contracts, which implement most of the methods we need to create an NFT, such as ownerOf which returns the owner of the NFT, and transferFrom, which transfers ownership of the NFT from one account to another.

In our ERC-721 constructor, you’ll notice we passed 2 strings, Meow-NFT, and MEO. 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!

    
// mints new NFTs. Can be called only by the deployer (s/c owner)
    function mintNFT(address recipient, string memory tokenURI)
        public onlyOwner
        returns (uint256)
    {
        // increment counter
        _tokenIds.increment();
        // get new NFT id
        uint256 newItemId = _tokenIds.current();
        // call internal ERC721 mint function
        _mint(recipient, newItemId);
        // write token URI to newly minted NFT
        _setTokenURI(newItemId, tokenURI);

        return newItemId;
    }

Finally, we have our function mintNFT(address recipient, string memory tokenURI) 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 configurable properties, such as a name, description, image, and other attributes.

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

Next

Be sure to check out our next article in this series, about how to deploy your NFT smart contract on Rootstock, Deploy NFT Smart Contract on Rootstock


If you would like to delve deeper, here are some resources and tools that we recommend.

Resources

Receive updates

Get the latest updates from the Rootstock ecosystem

Loading...