0xViking
0xViking

0xViking

How to write a smart contract in solidity, deploy, and mint a simple NFT on the Polygon blockchain

How to write a smart contract in solidity, deploy, and mint a simple NFT on the Polygon blockchain

A simple step-by-step guide on writing solidity smart contract for NFT and deploying it to polygon chain

0xViking's photo
0xViking
·Apr 14, 2022·

11 min read

Subscribe to my newsletter and never miss my upcoming articles

Table of contents

  • Introduction
  • ⚙️ Project Setup
  • ✍Writing actual Smart Contract
  • 💻 Let's deploy to Polygon Mumbai testnet

Introduction

Blockchain is one of the most interesting and exciting technology in recent times. You can see people making millions of dollars by selling NFTs. Take for example an Indonesian guy Sultan Gustaf Al Ghozali, 22, who studies computer science at a college in Indonesia, he took a photo of himself sitting in front of his computer every day for five years.

Started selling them as NFT for $3 each but later it became famous and was sold out, as of today total volume traded is Ether 396 which is ~ $1.1 M.

ghozali-everyday Opensea account

With the rising interest in NFTs, developers are also slowly rising to the top. Ethereum is the biggest smart-contract blockchain and has a huge NFT community but the problem is its gas fees.

At the time of writing this article, a simple NFT smart contract would cost around $1000+ to deploy. So we would be using Polygon’s blockchain to deploy and mint. It is an EVM-compatible blockchain with many projects adopting it as a secondary option including OpenSea the dominating NFT marketplace. Gas fees in Polygon are relatively low compared to Ethereum and have the same functionalities.

⚙️ Project Setup

We will be using a tool called Hardhat, It is an Ethereum development environment, fortunately, it also works with Polygon and a few other chains. Hardhat will let us quickly compile smart contracts and test them locally. Assuming you already have node/npm in your system.

Next, let's head to the terminal. Go ahead and cd to the directory you want to work in. then install hardhat following the below commands,

mkdir polygon-nft
cd polygon-nft
npm init -y
npm install --save-dev hardhat

After you execute the last command and install hardhat, You may see a message about vulnerabilities. This is a common warning when you install anything using NPM. You can google about it if you want to know more!

Now execute the following command which is basically initiating hardhat which will give us an option to start with basic/simple project setup. After you execute the below command it gives you a few options, for now, we will go ahead with default options, so just keep pressing enter.

npx hardhat

The sample project will ask you to install a few other dependencies which are helpful like hardhat-waffle and hardhat-ethers.

I would suggest going ahead and installing these other dependencies just in case it didn't do it automatically.

npm install --save-dev @nomiclabs/hardhat-waffle ethereum-waffle chai @nomiclabs/hardhat-ethers ethers

We'll also want to install "OpenZeppelin" which is another library that's used a lot to develop secure smart contracts

npm install @openzeppelin/contracts

You should now have a directory filled with a few files and folders like contracts, scripts, and test.

So just to verify go ahead and run the following command

npx hardhat run scripts/sample-script.js

which should give us something like

image.png

💥 If you see this it means your local environment is set up and you also ran/deployed a smart contract to a local blockchain.

What happened here is that

  • Hardhat compiled your smart contract from solidity to bytecode
  • Hardhat created a "local blockchain" on your computer
  • Hardhat then "deployed" your compiled contract to your local blockchain

✍Writing actual Smart Contract

Now there are multiple files and folders in your polygon-nft folder. Go ahead and delete the file sample-test.js under the test folder. Also, delete sample-script.js under scripts. Then, delete Greeter.sol under contracts. Don't delete the actual folders!

Now you can try going to docs.openzeppelin.com/contracts/4.x/wizard and get code is generated by their useful tool depending on selected features for making a simple contract.

OR

Let's write it!!

Create a file named polygonNFT.sol under the contracts directory and copy-paste the following code in that file and save it. I will explain the code below

// SPDX-License-Identifier: UNLICENSED

pragma solidity ^0.8.1;

import "hardhat/console.sol";

contract polygonNFT {
    constructor() {
        console.log("This is my first ever Polygon NFT contract. I am Excited!");
    }
}

let's go line by line :

// SPDX-License-Identifier: UNLICENSED

This is about the license of using your smart contract, you can fine more about them here.

pragma solidity ^0.8.1;

This is the version of the Solidity compiler we want our contract to use. When you say 0.8.1, it means that any Solidity compiler with version 0.8.1 or higher but less than 0.9.0 can be used to run our smart contract.

Note, be sure your compiler is set accordingly (0.8.1) in hardhat.config.js.

import "hardhat/console.sol";

Importing Hardhat provided a solidity file that will allow us to do some console logs in our contract which would be helpful while developing and testing.

contract polygonNFT {
    constructor() {
        console.log("This is my first ever Polygon NFT contract. I am Excited!");
    }
}

It is basically like a class on other object-oriented programming languages. Once we initialize this contract for the first time, that constructor will run and print out that line to the console.

🏃 Let's run our smart contract

Go ahead into the scripts folder and create a file named run.js. And copy-paste the following code in run.js:

const main = async () => {
  const polygonContractFactory = await hre.ethers.getContractFactory('polygonNFT');
  const nftContract = await polygonContractFactory.deploy();
  await nftContract.deployed();
  console.log("Contract deployed to:", nftContract.address);
};

const runMain = async () => {
  try {
    await main();
    process.exit(0);
  } catch (error) {
    console.log(error);
    process.exit(1);
  }
};

runMain();

The above basically doing the following things:

  • compile our smart contract
  • deploy it to our local blockchain

let's go line by line again:

const polygonContractFactory = await hre.ethers.getContractFactory("polygonNFT");

This will compile our contract and generate a few necessary files in the artifacts folder which are needed work with our contract.

const nftContract = await polygonContractFactory.deploy();

The above line will make Hardhat create a local Ethereum network for us, but just for this contract. Then, after the script completes it'll destroy that local network. So, every time you run the contract, it'll be a fresh blockchain.

await nftContract.deployed();

The above line will make sure to wait until our contract is officially mined and deployed to our local blockchain. Yeah, hardhat will actually create fake "miners" on your machine to try its best to imitate the actual blockchain.

Our constructor will also run when the smart contract is fully deployed!

console.log("Contract deployed to:", nftContract.address);

You know the console.log line, it will print the nftContract.address which is the address of our smart contract on that blockchain onto the terminal.

The runMain() function is actually the starting function that will be executed once we run below. Function content is self-explanatory :)

Now let's go ahead and run the actual smart contract we created. Go back to your terminal where you executed "npx hardhat run scripts/sample-script.js" previously

npx hardhat run scripts/run.js

Once you run that command, on your terminal you would see "This is my first ever Polygon NFT contract. I am Excited!" the console.log text in your constructor then "Contract deployed to: .........." which displays your contract address.

It should look something like this:

image.png

👷 Now let's write minting functionality

// SPDX-License-Identifier: UNLICENSED

pragma solidity ^0.8.1;

// We import some OpenZeppelin Contracts which are helpful.
import "@openzeppelin/contracts/token/ERC721/extensions/ERC721URIStorage.sol";
import "@openzeppelin/contracts/utils/Counters.sol";
import "hardhat/console.sol";

// We inherit the contract we imported. Which means we'll have access to the inherited contract's methods.
contract polygonNFT is ERC721URIStorage {
  // provided by OpenZeppelin to help us keep track of tokenIds.
  using Counters for Counters.Counter;
  Counters.Counter private _tokenIds;

  // We need to pass the name of our NFT token and its symbol.
  constructor() ERC721 ("My First Polygon NFT Project", "0xViking") {
    console.log("This is my first ever Polygon NFT contract. I am Excited!");
  }

  // A function our user will hit to get their NFT.
  function mintMyPolygonNFT() public {
     // Get the current tokenId, this starts at 0.
    uint256 newItemId = _tokenIds.current();

     //Main minting of the NFT to the caller using msg.sender.
    _safeMint(msg.sender, newItemId);

    // Set the NFTs data.
    _setTokenURI(newItemId, "ThisNeedsToBeChanges");

    // Increment the counter for when the next NFT is minted.
    _tokenIds.increment();
  }
}

I tried to explain each line using comments. If you still have any doubt please comment below, I am more than happy to explain you :)

Now let's update the TokenURI which is the actual NFT data. It usually links to a JSON file called the metadata and that looks something like this:

{
    "name": "This is Viking",
    "description": "Let's conquer the web3",
    "image": "https://i.imgur.com/ZIbmiR6.png"
}

You can customize this, but, almost every NFT has a name, description, and a link to something like a video, image, etc.

Be careful with the structure of your metadata, if your structure does not match the OpenSea Requirements your NFT will appear broken on the website. This is all part of the ERC721 standards and it allows people to build websites on top of NFT data.

The image link mentioned above is an Imgur link which is a centralized image hosting service, I have created an account there and uploaded my image there, and copied the link from it.

Please check your copied Imgur link should be a Direct Imgur link that looks like - i.imgur.com/abc123.png NOT imgur.com/gallery/abc123. The easiest way to tell is to check if the URL ends in an image extension like .png or .jpg. You can right-click the Imgur image and "copy image address". This will give you the correct URL.

We can copy the This is Viking JSON metadata above and paste it into this jsonkeeper site.

This website is just an easy place for people to host JSON data and we'll be using it to keep our NFT data for now. Once you click "Save" you'll get a link to the JSON file. (Something like jsonkeeper.com/b/56BW). You can test your link by opening a new tab and be sure it all looks good!

Now, let's go head to our smart contract and change that one line.

Following line of code

_setTokenURI(newItemId, "ThisNeedsToBeChanges")

to the below code where we are actually setting the URI as the link to our JSON file.

// mine looks like _setTokenURI(newItemId, "https://jsonkeeper.com/b/56BW");
_setTokenURI(newItemId, "INSERT_YOUR_JSON_URL_HERE");

Under that line, we will add a console.log just to help us see when the NFT is minted and to whom!

console.log("An NFT with ID %s has been minted to %s", newItemId, msg.sender);

🎉 Let's Mint an NFT on our local chain

What we need to do is to change our run.js file to call our mintMyPolygonNFT() function. add the following code below our console.log line in the main function

  // Call the function.
  let txn = await nftContract.mintMyPolygonNFT()
  // Wait for it to be mined.
  await txn.wait()

  // Mint another NFT for fun.
  txn = await nftContract.mintMyPolygonNFT()
  // Wait for it to be mined.
  await txn.wait()

that's it, let's run using the following command:

npx hardhat run scripts/run.js

It should mint 2 NFTs on your local blockchain. Here is how mine looks like

image.png

💻 Let's deploy to Polygon Mumbai testnet

When we deploy our contract, we need to tell all those miners, "hey, this is a new smart contract, please add my smart contract to the blockchain and then tell everyone else about it as well".

We will use Alchemy to do this.

Alchemy essentially helps us broadcast our contract creation transaction so that it can be picked up by miners as quickly as possible. Once the transaction is mined, it is then broadcasted to the blockchain as a legit transaction. From there, everyone updates their copy of the blockchain.

Go ahead and create an account at Alchemy

Then check out this video created by Farza below on how to get your Testnet API-key, in the video, he showed Rinkeby testent, instead, you can select Polygon and the Polygon-Mumbai in the dropdown.

BTW you also need a wallet to do so, I will suggest to go with Metamask and creating a wallet. Now add Polygon Mumbai to your metamask wallet. Check out this short video below. which also shows adding fake matic tokens which are required to deploy our smart contract

Once everything is ready let's set our deploy.js file. Go ahead and create a file named deploy.js under the scripts folder. Copy-paste all of run.js into deploy.js.

Then update your hardhat.config.js

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.1",
  networks: {
    mumbai: {
      url: 'YOUR_ALCHEMY_API_URL-COPY_IT-FROM-DASHBOARD',
      accounts: ['YOUR-MUMBAI-ACCOUNT-PRIVATE-KEY'],
    },
  },
};

DON'T COMMIT THIS FILE TO GITHUB. IT HAS YOUR PRIVATE KEY. YOU WILL GET HACKED + ROBBED. THIS PRIVATE KEY IS THE SAME AS YOUR MAINNET PRIVATE KEY. In the meantime - open your .gitignore file and add a line for hardhat.config.js.

Once you've got your config setup we're set to deploy with the deploy script we wrote earlier.

execute the following command in the terminal:

npx hardhat run scripts/deploy.js --network mumbai

Executing the above command gives you similar output:

image.png

Copy that contract address and go to Mumbai Testnet explorer and paste the address there and verify whether it was successful or not.

Okay, BTW you have minted two NFTs while deploying your smart contract let's go to opensea testnetsand see in your account whether those minted NFTs are there are not, Opensea might take a few minutes to sync.

This is how my collection looks:

image.png

That brings an end to this article.

So while deploying to the Polygon mainnet, in the deploy.js file remove that mintMyPolygonNFT() triggering statements so that your collection starts from starting in the mainnet.

txn = await nftContract.mintMyPolygonNFT()
// Wait for it to be mined.
await txn.wait()

BONUS: If you want to deploy it to mainnet, add configurations to networks key in hardhat.config.js. Something like;

networks: {
    mumbai: {
      url: 'YOUR_ALCHEMY_API_URL-COPY_IT-FROM-DASHBOARD',
      accounts: ['YOUR-MUMBAI-ACCOUNT-PRIVATE-KEY'],
    },
    rinkeby: {
      url: 'YOUR_ALCHEMY_RINKEBY-API_URL-COPY_IT-FROM-DASHBOARD',
      accounts: ['YOUR-ACCOUNT-PRIVATE-KEY'],
    },
    polygon: {
      url: 'YOUR_ALCHEMY_POLYGON-MAINNET-API_URL-COPY_IT-FROM-DASHBOARD',
      accounts: ['YOUR-ACCOUNT-PRIVATE-KEY'],
    },
  },

I hope this was helpful and all the best for your journey in web3.

Let me know how this went on Twitter @0xViking. My DMs are open feel free to text me :)

WAGMI🚀

Did you find this article valuable?

Support 0xViking by becoming a sponsor. Any amount is appreciated!

Learn more about Hashnode Sponsors
 
Share this