Create NFT Smart Contract with thirdweb and Chainlink
Tags: thirdweb, chainlink, tutorial, nft
Published: 2022-12-16 06:36:21

In this Tutorial, we will build a custom ERC-721 NFT Collection using `Thirdweb` and `Chainlink`. `Thirdweb ContractKit` is used for the ERC-721 token standard, `thirdweb deploy` is used to deploy our smart contract to mumbai testnetwork, and `Chainlink Data Feeds` is used to fetch the real-time price of Matic, to set the base price for minting NFT. Basically, we will build and deploy a smart contract that allows users to mint NFT. The user has to pay at least $1 in Matic to mint the NFT. We get the real price conversion using Chainlink Data Feeds.

## Overview

ERC-721 is the common standard for creating NFTs. Every major marketplace lists new tokens as an ERC-1155 standard. We will use Thirdweb ContractKit to build our smart contract along with some custom logic. We allow users to mint as many NFTs as they wanted to mint with only one condition that they have to pay a minimum of $1 to mint the NFT. We get the real price conversion using Chainlink Data Feeds.

## Why Use ContractKit?

Each feature you implement in your smart contracts unlocks functionality in both the thirdweb dashboard and SDK to help you build applications on top of them.

For example, if you implement the ERC721Base contract, you'll unlock the mint button in the dashboard and can use the mint function in the SDK; which automatically uploads and pins your metadata to IPFS!

## What is ERC-721?

ERC-721 is a free, open standard that describes how to build non-fungible or unique tokens on the Ethereum blockchain. While most tokens are fungible (every token is the same as every other token), ERC-721 tokens are all unique.

The ERC-721 introduces a standard for NFT, in other words, this type of Token is unique and can have a different value than another Token from the same Smart Contract, maybe due to its age, rarity, or even something else like its visual. Wait, visual?

Yes! All NFTs have a uint256 variable called `tokenId`, so for any ERC-721 Contract, the pair contract address, uint256 `tokenId` must be globally unique. That said, a dapp can have a "converter" that uses the `tokenId` as input and outputs an image of something cool, like zombies, weapons, skills, or amazing kitties!

## What is Chainlink Data Feeds ?

Chainlink is a decentralized network of oracles that enables smart contracts to securely interact with real-world data and services that exist outside of blockchain networks. With Chainlink, the traditional systems that currently power modern economies can connect to the emerging blockchain industry to generate more security, efficiency, and transparency in business and social processes.

Chainlink Data Feeds are the quickest way to connect your smart contracts to the real-world data such as asset prices, reserve balances, and L2 sequencer health.

## Setting up the environment

First of all, we will setup our environment and install dependencies. To get started, run the command below to create a new project by opening the terminal of your preferred IDE.

```bash
npx thirdweb create --contract
```

You will get something like this:

![1-setup.png](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/l8x9hw5398hn1st613zi.png)



Now enter your preferred details.

After completion, navigate and open the new directory by running the following command. 
``` 
cd dapp-name

code . 
```

You will see this type of file structure.

![1-initail-setup.png](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/31tu086nbe5t55q5l3ui.png)



## Let's BUIDL

Now open the contracts directory and select the `contract.sol` file. You will see the demo code. Now copy the below smart contract code and replace it with the demo code.

### Contract.sol (thirdweb contractKit)

```solidity
// SPDX-License-Identifier: MIT
/**
* @author Aayush Gupta  Github:AAYUSH-GUPTA-coder   Twitter:Aayush_gupta_ji
* 
* Smart conntract to allow users to mint as many NFTs as they wanted to mint with only one condition that they have to pay a minimum of $1 to mint the NFT. 
* Using `Thirdweb` contractKit and deploy tool. Get real time price conversion using `Chainlink Data Feeds`
*/
pragma solidity ^0.8.0;

// thirdweb contract for ERC-721 token standard
import "@thirdweb-dev/contracts/base/ERC721Base.sol";
// thirdweb contract for counters
import "@thirdweb-dev/contracts/openzeppelin-presets/utils/Counters.sol";
// chainlink data feed
import "./PriceConverter.sol";

contract Contract is ERC721Base {
   using PriceConverter for uint256;

   using Counters for Counters.Counter;
   Counters.Counter private _tokenIdCounter;
   // Minimum price of NFT $1 in MATIC
   uint256 public constant MINIMUM_USD = 1 * 10 ** 18;
    

   /**
    * @dev ERC721Base library's constructor takes four Parameters
    * _name of the NFT, _symbol of the NFT,
    *  _royaltyRecipient (address) who will get a royalty on secondary sale, _royaltyBps (royality percentage)
    * we don't need to set Royality for the purpose of our smart contract. setting _royaltyBps to Zero
    * @param _name: name of the whole NFT bundle Collection
    * @param _symbol: symbol of the whole NFT bundle Collection
   */
   constructor(
       string memory _name,
       string memory _symbol,
       address _royaltyRecipient,
       uint128 _royaltyBps
   )
       ERC721Base(
           _name,
           _symbol,
           _royaltyRecipient,
           _royaltyBps
       )
   {}

   /**
    * @dev createToken mint the ERC721 Token / NFT with the check that the user have paid $1 to mint the NFT
    */
  function createToken() public payable
   {
       // require statement to check the user have paid $1 to mint the NFT
       require(msg.value.getConversionRate() >= MINIMUM_USD, "SEND_MORE_MATIC");

       // Increment it so next time it's correct when we call .current()
       _tokenIdCounter.increment();

       // Current counter value will be the minted token's token ID.
       uint256 newTokenId = _tokenIdCounter.current();

       // Mint the token
       _mint(msg.sender, newTokenId);

       // Default token Metadata on token minting
       string memory tokenURI = "https://ipfs.io/ipfs/QmVAUVEmr6pxqZq2KtLtjs52d2c91q5sKKhDZweQeqaH7Z";

       // setting default token Metadata 
       _setTokenURI(newTokenId, tokenURI);
   }

   /**
    * @dev function to withdraw funds present in contract address to owner address. In this case, the address that deploy this smart contract
    */
   function withdraw() public onlyOwner(){
       (bool callSuccess, ) = payable(msg.sender).call{value: address(this).balance}("");
       require(callSuccess,"TRANSFER_FUND_FAIL");
   }

   /**
    * @dev view / Getter function to get the balance of the smart contract
    */
   function getContractBalance() public view returns(uint){
       return address(this).balance;
   }
   
   // A contract receiving Ether must have at least one of the functions

   // receive() is called if msg.data have value
   fallback() external payable {}

   // receive() is called if msg.data is empty
   receive() external payable {}

}
```

I will explain the code below but before seeing the explanation. I request you to try to understand smart contracts on your own by reading the code and comments.

## Let's move to the explanation

Line 1: Specifying SPDX license type, which is added after Solidity version ^0.6.8. Whenever the source code of a smart contract is made available to the public, these licenses can help resolve/avoid copyright issues. If you do not wish to specify any license type, you can use a special value UNLICENSED or skip the whole comment (it will not result in an error, just a warning).

Line 8: Declaring the Solidity version.

Line 11: Importing the thirdweb `ERC721Base` contract.

Line 13: Importing the thirdweb `Counters` contract. We will use it to increment and keep track of `tokenID` of NFTs.

Line 15: importing `./PriceConverter.sol`. We will use it to get the real-time price conversion of MATIC/USD.

Line 17: Inheriting the `ERC721Base` contract in our `Contract` smart contract. In order to use all the functionalities of `ERC721Base` contract.

Line 18: Using `PriceConverter` as `uint256` data type.

Line 20: Using `Counters` as `Counters.Counter` datatype.

Line 21: Setting up the minimum price of NFT in terms of USD. In our case it will be $1. Also due to the lack of testnet funds.

Line 34: `constructor`: `ERC721Base` library's constructor takes four Parameters

`_name`: name of the NFT

`_symbol`: symbol of the NFT

`_royaltyRecipient`: (address) who will get a royalty on a secondary sale

`_royaltyBps`: royalty percentage on secondary sale. `100` is `1%` here.

Line 51: `function createToken()`: Allow the user to mint the ERC721 Token / NFT with the check that the user has paid $1 to mint the NFT.

Line 54: `require` to check that the user sends more than $1 in Matic value.

Line 57: `_tokenIdCounter.increment()` to increment the TokenID. so next time it's correct when we call .current().

Line 60: Current counter value will be the minted token's token ID.

Line 63: `_mint` Mint the NFT

Line 66: Giving a default NFT Metadata. In our case, we have hardcoded the IPFS link.

Line 69: `_setTokenURI` function to set URI/Metadata of NFT.

Line 75: `function withdraw`: function to withdraw funds present in contract address to owner address. In this case, owner is the address that deploy this smart contract.

Line 83: View / Getter function to get the balance of the smart contract.

Line 90: A contract receiving Ether must have at least one of the functions. `fallback` is called if msg.data have value.

Line 93: `receive` is called if msg.data is empty.

Woah... That's a long explanation. Now, before moving to deploy our smart contract using `thirdweb deploy` tool. We have to write `PriceConverter.sol` contract to get the realtime value of MATIC in USD. We will use `Chainlink Data Feeds` to get the realtime value. Using `chainlink` services is extremelly easy. You can refer to official chainlink docs to learn about data feeds in depth [Chainlink Docs](https://docs.chain.link/docs/data-feeds/).

Create a new `.sol` file in the `contracts` directory name `PriceConverter.sol` and copy the below smart contract code.

### PriceConverter.sol (Chainlink Data Feeds)

`PriceConverter` is a smart contract to get real-time price of MATIC / USD using `Chainlink Data feeds`. We use `PriceConverter` as a library because we are not sending any Matic to the smart contract. We are only using it to get the value.

```solidity
// SPDX-License-Identifier: MIT

/**
 * @author Aayush Gupta  Github:AAYUSH-GUPTA-coder   Twitter:Aayush_gupta_ji
 * smart contract to get real time price of MATIC / USD using `Chainlink Data feeds`
 */

pragma solidity ^0.8.8;

import "@chainlink/contracts/src/v0.8/interfaces/AggregatorV3Interface.sol";

 /**
     * Network: Mumbai
     * Aggregator: MATIC / USD
     * Address: 0xd0D5e3DB44DE05E9F294BB0a3bEEaF030DE24Ada
     */

library PriceConverter {

    /**
     * @dev function to get the Price of MATIC / USD. 
     * The problem with this we get value with 8 float point while Matic/ETH have 18 float point. 
     * Therefore we raise the power of our answer with 10 floating point.
     */
    function getPrice() internal view returns (uint256) {
        AggregatorV3Interface priceFeed = AggregatorV3Interface(
            0xd0D5e3DB44DE05E9F294BB0a3bEEaF030DE24Ada
        );
        (, int256 answer, , , ) = priceFeed.latestRoundData();
        return uint256(answer * 1e10); // 1* 10 ** 10 == 10000000000
    }

    /**
     * @dev function to get eth(matic) in USD. 
     * Will get the actual ETH/USD conversion rate, after adjusting the extra 0s.
     */
    function getConversionRate(uint256 ethAmount)
        internal
        view
        returns (uint256)
    {
        uint256 ethPrice = getPrice();
        uint256 ethAmountInUsd = (ethPrice * ethAmount) / 1e18; // 1 * 10 ** 18 == 1000000000000000000
        // the actual ETH/USD conversion rate, after adjusting the extra 0s.
        return ethAmountInUsd;
    }
}
```

Again I will explain the code below but before seeing the explanation. I request you to try to understand smart contracts on your own by reading the code and comments.

### Let's move to the explanation

Line 1: Specifying SPDX license type.

Line 8: Declaring the Solidity version.

Line 10: Importing the chainlink `AggregatorV3Interface.sol` contract.

Line 25: `function getPrice()` function to get the Price of MATIC / USD. The problem with this we get a value with 8 float points while Matic/ETH has 18 float points (i.e 1 ether == 1 ^ 18 wei). Therefore, we raise the power of our answer with 10 floating points.

Line 37: `function getConversionRate` function to get eth(Matic) in USD. Will get the actual ETH/USD conversion rate, after adjusting the extra 0s.

**BOOM** You have created our smart contract with custom logic using thirdweb ContractKit and Chainlink Data Feeds.

## Deploy our smart contract using thirdweb deploy

Use the CLI to ship our contract directly to any of thirdweb supported networks using their dashboard.

```bash
npx thirdweb deploy
```

Running this command will:

*   Compile all the contracts in the current directory.
    
*   Allow you to select which contract(s) you want to deploy.
    
*   Upload your contract source code (ABI) to IPFS.
    
*   Open the deploy flow in the dashboard for you to select one of ThirdWeb supported networks to deploy to.
    
*   You don't need to write your deploy script. Meaning you don't need your `Private Key` to deploy your smart contract, which saves you and eliminates any chance to expose your Private Key.
    

After running the above command, you will get something like this:


![1-deploy](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/za2f6s3p85go2ljkm01z.png)


Now click on the link. You will open the Deploy dashboard. Enter the details (Name of the NFT, symbol of the NFT, address of Royalty Recipient, percentage of Royalty and network you want to deploy your smart contract) and click on the `Deploy Now` button.


![2-deploy-dashboard.png](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/2q4pk4uuybfsgkaktilg.png)


Congratulations! You have deployed your smart contract on desired testnetwork.

## Mint the NFT

Go to Explorer section of your NFT collection Dashboard. You will be seeing all the functions you can perform. For now, let's just focus on our custom `createToken` function.

![createToken-button.png](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/71igoqgcgbhulgbjk79m.png)


Now let's mint the NFT by sending Matic valuing more than $1. Just to keep things simple we will set the value to 2.

![nft-created.png](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/qlerxz1lv88ionqj61ow.png)


Copy your contract address and paste the address on Testnet Opensea to view your NFT with metadata.

![opensea-NFT.png](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/kvj707env4c4ti492l3f.png)


You have successfully minted the NFT and viewed it on opensea. You can share link of opensea and brag about NFTs like all web3 native people do 😎.

## Check Custom Errors

Enter the `Native Token Value` to mint the NFT. Now to check whether our code is working, we will try to mint NFT without paying any matic.

![send-more-matic.png](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/5cl1swnt364esii7buyk.png)


As you can see it reverts the transaction with the `SEND_MORE_MATIC` meaning our custom logic is working.

## Getter Function

We have already seen how we can access and use the setter function in thirdWeb. Now we will interact with the getter function. You can access the getter function by scrolling down on the `explorer` page and clicking on your custom `MINIMUM_USD` public Variable.

![minimun-usd.png](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/usyqkvey176b6ur19w1y.png)


Now let's try to get the smart contract balance by checking the `getContractBalance` Function.

![balance.png](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/xybnfp0xcm7v3yg4s8kq.png)


You can see you are getting the right result.

## 🎉BOOM 🎉

You have completed the whole tutorial. Give yourself a pat on the back. You have learned about:

ERC-721 token standard

*   Build your own custom smart contract using `thirdweb contractkit`
    
*   Fetch the real-time price of Matic in USD by using `Chainlink Data Feeds`
    
*   Deploy your smart contract using `thirdweb deploy`
    
*   Mint your NFT and interact with your smart contract using the Explorer section of `thirdweb dashboard`
    

💥 Simply WOW 💥

If you learn and enjoy this article. Please share this article with your friends. I hope you learned something new or maybe even solved a problem. Thanks for reading, and have fun!

You can follow me on [Twitter](https://twitter.com/Aayush_gupta_ji), [GitHub](https://github.com/AAYUSH-GUPTA-coder) and [LinkedIn](https://www.linkedin.com/in/aayush-gupta-20023b183/). Keep your suggestions/comments coming!

WAGMI 🚀🚀