Smart Contract Development
ERC-721 NFT Smart Contract on Avalanche and Mint an NFT with Ankr, Hardhat, and Ethers.js

Deploy and Mint a CryptoKitties-Like NFT with ERC-721 Smart Contract

By JoshCS (opens in a new tab)


Set up your Dev Environment

First, create a new directory called avaxankr.

Initialize your new npm project in the directory:

npm init

Install Hardhat, an Ethereum development environment for testing and deploying smart contracts to the blockchain.

npm install --save-dev hardhat

Create a new Hardhat project:

npx hardhat

Select “create an empty hardhat.config.js"

Add project folders:

mkdir contracts && mkdir scripts

Write Your Smart Contract

Write your contract. We will use an ERC-721 standard based on OpenZepplin. ERC-721 is the free and open standard that defines the schema for a non-fungible or unique token on Ethereum. ERC-20 tokens are fungible and each token is the same as another, whereas ERC-721 tokens are unique. You can also learn more at the page below. OpenZepplin is an open-source standard for securing blockchain applications and provides security products to build, automate, and operate dApps.

npm install @openzeppelin/contracts

Next, add the following smart contract to the contracts directory and name it AnkrAvalancheNFT.sol

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0; // declare the version of solidity to compile this contract. This must match the version of solidity in your hardhat.config.js file
 
// import ERC-721 URI storage extension standard and Counters.sol which will help set token IDs from OpenZepplin
// add ownable and ERC721.sol
import "@openzeppelin/contracts/token/ERC721/ERC721.sol";
import "@openzeppelin/contracts/token/ERC721/extensions/ERC721URIStorage.sol";
import "@openzeppelin/contracts/utils/Counters.sol";
import "@openzeppelin/contracts/access/Ownable.sol";
 
// @dev This function instantiates the contract for AnkrPolygonNFT and classifies ERC-721 for storage schema
contract AnkrAvalancheNFT is ERC721URIStorage, Ownable {
 
/* @dev Set up our counters
* @param Counters will create unique TokenIds for each mint of the ERC-721 token
* @return This will return the current TokenId
*/
    using Counters for Counters.Counter;
 
/* @dev Store our counter
* @param The TokenId will be stored based on the current count
* @return This will store the current TokenId
*/
    Counters.Counter private _tokenIds;
 
// pass arguments for name and symbol
/* @dev Set the name and token tracker for our contract
* @param AnkrPolygonNFT will be the name of the ERC-721 token, and ANKRPG will be the token tracker
* @return This will name the token each time it is minted
*/
    constructor() ERC721("AnkrAvaxNFT", "ANKRAVAX") {}
 
/* @dev This will set the mint function for the token with the URI and recipient specified
* @param tokenURI specifies the schema and 
* @return This will set the name, image, description, and properties
*/
    function mint(address recipient, string memory tokenURI) public returns (uint256){
 
// increment the tokenId by one
        _tokenIds.increment();
 
// fetch the current tokenId and set the new tokenId
        uint256 newItemId = _tokenIds.current();
 
/* @dev Set the token owner by retrieving the address from recipient
* @param recipient is the wallet address for the receiver and newItemId is current tokenId
* @return Receiver of ERC-721 and tokenId
*/
        _safeMint(recipient, newItemId);
 
/* @dev Set the token URI and new TokenId
* @param newItemId is current tokenId and new tokenURI
* @return Current TokenId and tokenURI
*/
        _setTokenURI(newItemId, tokenURI);
 
// return current newItemId for the ERC-721 token
        return newItemId;
    }
}

Install the Ethers.js Ethereum library:

npm install --save-dev @nomiclabs/hardhat-ethers ethers@^5.0.0 @nomiclabs/hardhat-etherscan

Set and keep our environment variables hidden by using dotenv (opens in a new tab)

npm install --save dotenv

Create a .env file in your project’s root folder and set the environment variable as follows. This is the private key of the account you intend to use on the Avalanche Network from MetaMask.

PRIVATE_KEY=YOUR_PRIVATE_KEY

Setup Hardhat to Compile and Deploy Your Contract to Fuji Testnet

Hello

We will be using Ankr's Public RPC's (opens in a new tab) to connect to and send transactions on the Avalanche blockchain just copy the URL for the Fuji testnet and mainnet. No account or login required!

Hello

Now you are ready to edit your hardhat.config.js with the following:

/**
 * @type import('hardhat/config').HardhatUserConfig
 */
require("dotenv").config();
require("@nomiclabs/hardhat-ethers");
require("@nomiclabs/hardhat-etherscan");
 
module.exports = {
  solidity: "0.8.6",
 
  networks: {
    avalanche: {
      url: "https://rpc.ankr.com/avalanche",
      accounts: [`0x` + process.env.PRIVATE_KEY],
      chainId: 43114,
    },
    fuji: {
      url: "https://rpc.ankr.com/avalanche_fuji",
      accounts: [`0x` + process.env.PRIVATE_KEY],
      chainId: 43113,
    },
  },
  etherscan: {
    apiKey: process.env.SNOWTRACE_API_KEY,
    apiUrl: "https://api.snowtrace.io",
    browserURL: "https://snowtrace.io/",
  },
};

Compile your contract to make sure everything is good to deploy

npx hardhat compile
 
# output
# Compiled 1 Solidity file successfully

Write a deploy script by navigating to the scripts folder and creating a new file called deploy.js

cd scripts && touch deploy.js

Add the following script to the deploy.js file

// open main asynchronous function will handle deployment
async function main() {
  // use ethers to pass the name of our contract to getContractFactory
  const nftContractFactory = await ethers.getContractFactory(
    "AnkrAvalancheNFT"
  );
 
  // create variable to allow us to use the deploy function of getContractFactory
  const nftContract = await nftContractFactory.deploy();
 
  // log the address of the Contract in our console
  console.log("Contract deployed to:", nftContract.address);
  process.exit(0);
}
 
// run main, catch error, if any, and log in console
main()
  .then(() => process.exit(0))
  .catch((error) => {
    console.error(error);
    process.exit(1);
  });

Deploy the contract to the Avalanche Fuji Testnet

npx hardhat run scripts/deploy.js --network fuji
 
# output
# Contract deployed to: 0xc7f2061054FceF82397942F42a37d5fE7A758960

Copy the contract address and search for it in Snowtrace at testnet.snowtrace.io (opens in a new tab)↗ (example)

Hello

Deploy the Contract to Avalanche Mainnet

Deploy your contract to Avalanche Mainnet

npx hardhat run scripts/deploy.js --network avalanche
 
# output
# Contract deployed to: 0xc7f2061054FceF82397942F42a37d5fE7A758960

Copy the contract address and search for it in Snowtrace at snowtrace.io (example contract (opens in a new tab)↗)

Hello

Create an account on Snowtrace and go to https://snowtrace.io/myapikey. Create a new API key. Copy the API key and paste it in your .env file

Hello
PRIVATE_KEY=YOUR_PRIVATE_KEY
SNOWTRACE_API_KEY=YOUR_API_KEY

Be sure your Snowtrace API key is in your .env file and then run the following command to verify your contract on Snowtrace. Verifying the contract on Snowtrace will decode the bytecode to human-readable code. Be sure to replace the YOUR_CONTRACT_ADDRESS with your contract address.

npx hardhat verify YOUR_CONTRACT_ADDRESS --network avalanche
 
# output
# Nothing to compile
# Successfully submitted source code for contract
# contracts/AnkrAvalancheNFT.sol:AnkrAvalancheNFT at 0xc7f2061054FceF82397942F42a37d5fE7A758960 for verification on the block explorer. Waiting for verification result...
 
# Successfully verified contract AnkrAvalancheNFT on Etherscan.
# https://snowtrace.io/address/YOUR_CONTRACT_ADDRESS#code

Mint an ERC-721 NFT on Avalanche

We will be using Hardhat to mint an NFT programmatically and web3.storage (opens in a new tab)↗ to store our image and data. We will need to set the Token URI to a JSON (opens in a new tab)↗ file. I’ve gone ahead and made an image with the tools we’ve used on this tutorial. Web3.storage is a tool that allows users to store their files on the Interplanetary File System (IPFS) by Protocol Labs

Start by uploading your file to Web3.storage.

Copy the URI and create an avaxnft.json file in root directory. Add the URI to the JSON code. You can learn more about how to structure your JSON file from OpenSea’s metadata standards (opens in a new tab)

{
  "name": "Ankr x Avalanche ERC-721 NFT",
  "description": "Ankr x Avalanche ERC-721 NFT",
  "image": "https://ipfs.io/ipfs/bafybeieua46kk2xqshlpttael2r2ypjiyua3fcwnft52a44tk7jlzu263y/AVAX_x_ANKR.png",
  "attributes": [
    {
      "trait_type": "Developer",
      "value": "Josh CS"
    },
    {
      "trait_type": "Website",
      "value": "https://ankr.com"
    },
    {
      "trait_type": "Conference",
      "value": "https://avalanche.house"
    }
  ]
}

Upload avaxnft.json to Web3.storage and copy the URI and add it to the tokenURI variable in mint.js. Add a CONTRACT_ADDRESS variable and PUBLIC_KEY in your .env file and add them.

PRIVATE_KEY=YOUR_PRIVATE_KEY
SNOWTRACE_API_KEY=YOUR_API_KEY
CONTRACT_ADDRESS=YOUR_CONTRACT_ADDRESS
PUBLIC_KEY=YOUR_PUBLIC_KEY

Create your mint.js file in the scripts directory

// require the correct libraries
 
require("dotenv").config();
require("@nomiclabs/hardhat-ethers");
 
// Our contract’s ABI (Application Binary Interface) is the interface enabling our js script to interact with our smart contract. Hardhat generates and stores the ABI version of our contract in the artifacts folder as a JSON file.
const contract = require("../artifacts/contracts/AnkrAvalancheNFT.sol/AnkrAvalancheNFT.json");
const contractInterface = contract.abi;
 
let provider = ethers.provider;
 
const tokenURI =
  "https://ipfs.io/ipfs/bafybeib3skazl2dhjctsmiroc6ug5zketfooovt53ast5pxytcan2n3zba/avaxankr.json";
const privateKey = `0x${process.env.PRIVATE_KEY}`;
const wallet = new ethers.Wallet(privateKey);
 
wallet.provider = provider;
const signer = wallet.connect(provider);
 
// https://docs.ethers.io/v5/api/contract/contract
const nft = new ethers.Contract(
  process.env.CONTRACT_ADDRESS,
  contractInterface,
  signer
);
 
const main = () => {
  console.log("Waiting 5 blocks for confirmation...");
  nft
    .mint(process.env.PUBLIC_KEY, tokenURI)
    .then((tx) => tx.wait(5))
    .then((receipt) =>
      console.log(
        `Your transaction is confirmed, its receipt is: ${receipt.transactionHash}`
      )
    )
    .catch((e) => console.log("something went wrong", e));
};
 
main();

Call mint.js and specify the network as Avalanche

npx hardhat run scripts/mint.js --network avalanche
 
# output
# Waiting 5 blocks for confirmation...
# Your transaction is confirmed, its receipt is: 0xb52eece2e80648fdfc14ffcf460903cf06716ace5616ebd3fd03045239bed94d

View your NFT on NFTrade.com (opens in a new tab)

Copy the PUBLIC_KEY address and navigate to https://nftrade.com/users/avalanche/PUBLIC_KEY to view your NFT.

Hello