How to Create a Dynamic NFT
Introduction
Welcome to the world of dynamic NFTs! In this guide, you’ll build a smart contract and learn how to create a dynamic NFT that changes in real time by connecting your smart contract to an oracle using Chainlink Automation! For a deeper understanding of dynamic NFTs and why they matter, check out our previous guide.
Prerequisites
Be sure you understand how NFT works and how to upload files to IPFS. If you don’t, you can check out this article here.
How to Create a Dynamic NFT
In this tutorial, you’ll learn how to:
Deploy a dynamic NFT smart contract.
Connect to Chainlink Keepers.
View the dynamic NFT on Opensea.
Without further ado, let’s get started!
Estimated time to follow along 15 - 25 mins.
Step 1: Creating the starter project
Launch the Remix IDE on the Remix website here. Next, on the Remix interface in the top left corner, click on the file and create a new file.
Step 2: Implementing our dynamic NFT
Before we start coding, we’ll need to do a few imports, as we don’t need to build everything from scratch. In your dynamicNft.sol file, import ERC721Uristorage, and counters.sol.
Here is what the import does:
ERC721URIStorage.sol: This extends the standard ERC721 token contract with the ability to store and manage token URIs. Token URIs are used to associate external metadata, such as images or descriptions, with each NFT.
Counters.sol: This provides a utility for creating and managing counters, which are often used for generating unique token IDs in ERC721. It helps to keep track of the number of tokens and ensure that each token has a unique identifier within a contract.
Step 3: Coding the functionalities
Having imported the necessary imports, let’s implement our functions next.
Let’s go through each of the code snippets:
Contract Declaration
The contract CovNft is ERC721, ERC721URIStorage
inherits its functionalities from the contracts we imported.
Counters for Token Tracking
using Counters for Counters.Counter
: This line introduces theCounters
library to the contract, specifically theCounter
type. It sets up a mechanism for tracking the number of unique ERC721 tokens created.Counters.Counter private _tokenIdCounter
: This declares a private variable_tokenIdCounter
of typeCounters.Counter
. It is used to keep track of the unique token IDs generated by the contract.
IPFS Links For Images
This initializes an array named IpfsUri
that holds URLs to three different images stored on the InterPlanetary File System (IPFS). These images are associated with the NFTs created by our contract.
State Variables
uint256 lastTimeStamp
: This line declares a variablelastTimeStamp
of type uint256. It's used to keep track of the last timestamp when an event occurred.uint256 interval
: This line declares a variableinterval
of type uint256. It's used to store the time interval between Chainlink upkeeps.
Constructor
The constructor(uint _interval) ERC721("Cov_dNft", "CDN")
defines the constructor
function for the contract. It takes an input parameter _interval
, which represents the time interval between Chainlink upkeeps. We then initialize the contract with the name Cov_dNft and the symbol CDN, as required by the ERC721 standard. We also set the interval
and lastTimeStamp
variables with the provided _interval
value and the current block.timestamp
, respectively.
safeMint
The safeMint
function allows for minting a new NFT. It takes an address to
as an argument, which represents the recipient of the newly minted NFT.
Inside the function:
It retrieves the current value of
_tokenIdCounter
to determine the unique ID for the new NFT.Increments the
_tokenIdCounter
to ensure each newly minted NFT has a unique ID.Mints the NFT and assigns ownership to the provided address.
And sets the metadata URI for the NFT, which points to additional information about the NFT (e.g., image, description). In this case, it uses the IPFS link stored in the
IpfsUri
array at index0
.
dynamicNft
The dynamicNft
function allows for dynamically updating the metadata of an existing NFT, allowing it to change. It takes a uint256 argument _tokenId
, which identifies the NFT to be updated.
Inside the function:
It determines the current stage of the NFT by calling the
NftStage
function.Calculates the
nextStage
for the NFT, looping back to the first stage if the current stage is at its maximum (2 or greater).Fetches the metadata URI for the next stage from the
IpfsUri
array.Updates the metadata URI of the NFT associated with the provided
_tokenId
to thenewUri
. This effectively changes the NFT's appearance or attributes based on its current stage.
NftStage
The NftStage
function is used to set the stage of an existing NFT based on its metadata URI. It takes a uint256 argument _tokenId
, which identifies the NFT for which you want to determine the stage. It first retrieves the metadata URI of the NFT with the provided _tokenId
using the tokenURI
function. It then compares this metadata URI to the URIs stored in the IpfsUri
array to identify which stage the NFT corresponds to and checks if the metadata URI matches the URI at index 0 of the IpfsURI
array. It returns 0 and does the same for index 1 and index 2.
compareStrings
This function is responsible for comparing two strings, “a” and “b,” to check if they are equal, which serves as a helper function in the NftStage
function. We utilized the keccak256
hash function to compute the cryptographic hash of both strings and compared the resulting hash values. Hence, if the hash values match, it returns true, indicating that the two strings are equal; otherwise, it returns false.
tokenURI
The tokenURI
function is used to retrieve the metadata URI associated with a specific token ID. It takes a tokenId
as input and returns a string representing the URI where the metadata for that token is stored.
Step 4: Make the contract keepers compatible
To make our contract keepers compatible, we need to add two functions: checkUpkeep
and performUpkeep
. You can read more about them here. Let’s implement the two functions.
checkUpkeep
The checkUpkeep
function is used to determine whether upkeep is needed for our smart contract. It calculates if the upkeep is needed by comparing the current time block.timestamp
with the lastTimeStamp
and checking if it exceeds the specified interval. If the time difference is greater than the interval, it sets upkeepNeeded
to true, indicating that changes are required.
performUpkeep
This checks if block.timestamp - lastTimeStamp
is greater than the specified interval. If this condition is met, it updates the NFT. In response, it updates the lastTimeStamp
to the current block.timestamp
to reset the upkeep timer. After updating the timestamp, it calls the dynamicNft
function with the argument 0
, which contains the actions required to update the NFT.
Step 5: Compiling and deploying the smart contract
To compile our contract, simply navigate to the compiler tab on the left and click on compile as shown.
After you’ve successfully compiled the contract, click on the deploy tab to deploy our contract. Choose Injected Provider-Metamask from the lists of environments, set the constructor value to any value (for this guide, we used 1) and deploy your contract.
You should see something similar to this:
Step 6: Minting an NFT
To mint the newly deployed NFT, click on the deployed contract, then click on safeMint, and paste your wallet address (which you can copy from your MetaMask). After minting the NFT, you can then view it on OpenSea.
You can view the minted NFT by copying the contract address and viewing it on OpenSea here.
We just minted a static NFT; our goal is to make our NFT dynamic -i.e. to keep changing to different NFTs in real time. Let’s see how we can achieve this using Chainlink.
Step 7: Verifying our NFTs
To automate our NFTs, we’ll need to utilize Chainlink keepers. We won’t be able to cover the intricacies of what it’s all about in this guide, but you can read more here. Before we can automate our NFT, let’s verify it. Simply go to the plugins icon in Remix and search for Contract Verification - Etherscan.
Click the Activate button to activate the plugin. Obtain your Etherscan API key by creating an account on Etherscan here. Get the API key and set it on Remix.
Now, navigate to Etherscan's Contract Verification section, select the contract you wish to verify, and click the Verify button.
Step 8: Automating our NFT
We can now automate our NFT using Chainlink keepers. To do that, click here to go to the Chainlink page for you to register your contract.
Once you are on the Chainlink keepers page, click on Register new Upkeep, register the deployed contract, and choose Time-based upkeep as shown.
On the next page, enter the contract address and select dynamicNft as the target function and 0 as the _tokenId, which represents the deployed NFT.
Specify the timeframe for your NFT to undergo continuous changes. In this article, we use 1 minute as the example timeframe.
Now, fund the upkeep by getting a LINK token from this faucet and click on Register Upkeep.
Step 9: Checking the NFT on OpenSea
Once the keeper has been deployed for the contract successfully, you can then go to Opensea and see how the initial minted NFT keeps changing to a different NFT when we refresh, as shown.
And there, you can observe the dynamic NFT you’ve created!
Conclusion
That’s it! By utilizing the power of smart contracts and Chainlink Automation, we've showcased how to bridge the gap between static and dynamic digital assets. This guide has covered each step of creating, deploying, and automating our dynamic NFT, right from setting up our smart contract to witnessing the NFT's transformation on OpenSea in real time. Congratulations, you’ve made it to the end of this tutorial and now understand how to create dynamic NFTs. Check out our other guides for more step-by-step Web3 tutorials!