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 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.

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!