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.
npx thirdweb create --contract
You will get something like this:
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.
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)
// 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.
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.
// 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.
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:
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.
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.
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.
Copy your contract address and paste the address on Testnet Opensea to view your NFT with metadata.
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.
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.
Now let's try to get the smart contract balance by checking the getContractBalance
Function.
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, GitHub and LinkedIn. Keep your suggestions/comments coming!
WAGMI ๐๐
Top comments (0)