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.
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.
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.
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.
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.
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 the Counters
library to the contract, specifically the Counter
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 type Counters.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 variable lastTimeStamp
of type uint256. It's used to keep track of the last timestamp when an event occurred.
uint256 interval
: This line declares a variable interval
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 index 0
.
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 the newUri
. 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.
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.
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:
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.
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.
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.
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!
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!