Deploying Your Dynamic NFTs
In this section, you will be deploying and interacting with your Dynamic NFTs
You will be creating a dynamic NFT of a flower with 3 stages: Seed, Seedling, and Bloom.
Please make sure you've done Section 2 Smart Contract fundamentals before you attempt this, as we use tools set up in that section.

Create Our DynamicNFT contract
First, open Remix and create a new workspace, then create a new file called Flowers.sol


This will open an empty file on the right side of your screen which is the IDE. Here you can paste the following code.
// SPDX-License-Identifier: MIT
pragma solidity 0.8.19;
import "@chainlink/contracts/src/v0.8/AutomationCompatible.sol";
import "@openzeppelin/contracts@4.6.0/token/ERC721/extensions/ERC721URIStorage.sol";
import "@openzeppelin/contracts@4.6.0/utils/Counters.sol";
contract Flower is ERC721, ERC721URIStorage, AutomationCompatibleInterface {
using Counters for Counters.Counter;
Counters.Counter public tokenIdCounter;
// Metadata information for each stage of the NFT on IPFS.
string[] IpfsUri = [
"<https://ipfs.io/ipfs/QmVCZ6hVENjkdCDLMoovNCR5S6bSisUQnBKPibb8XXSJqF/seed.json>",
"<https://ipfs.io/ipfs/QmVCZ6hVENjkdCDLMoovNCR5S6bSisUQnBKPibb8XXSJqF/purple-sprout.json>",
"<https://ipfs.io/ipfs/QmVCZ6hVENjkdCDLMoovNCR5S6bSisUQnBKPibb8XXSJqF/purple-blooms.json>"
];
uint public immutable interval;
uint public lastTimeStamp;
constructor(uint updateInterval) ERC721("Flower Bootcamp 2024", "FLO") {
interval = updateInterval;
lastTimeStamp = block.timestamp;
safeMint(msg.sender);
}
function safeMint(address to) public {
uint256 tokenId = tokenIdCounter.current();
tokenIdCounter.increment();
_safeMint(to, tokenId);
_setTokenURI(tokenId, IpfsUri[0]);
}
// determine the stage of the flower growth
function flowerStage(uint256 _tokenId) public view returns (uint256) {
string memory _uri = tokenURI(_tokenId);
// Seed
if (compareStrings(_uri, IpfsUri[0])) {
return 0;
}
// Sprout
if (
compareStrings(_uri, IpfsUri[1])
) {
return 1;
}
// Must be a Bloom
return 2;
}
function growFlower(uint256 _tokenId) public {
if(flowerStage(_tokenId) >= 2){return;}
// Get the current stage of the flower and add 1
uint256 newVal = flowerStage(_tokenId) + 1;
// store the new URI
string memory newUri = IpfsUri[newVal];
// Update the URI
_setTokenURI(_tokenId, newUri);
}
// helper function to compare strings
function compareStrings(string memory a, string memory b)
public pure returns (bool)
{
return (keccak256(abi.encodePacked((a))) ==
keccak256(abi.encodePacked((b))));
}
function checkUpkeep(bytes calldata /* checkData */) external view override returns (bool upkeepNeeded, bytes memory /* performData */) {
if ((block.timestamp - lastTimeStamp) > interval ) {
uint256 tokenId = tokenIdCounter.current() - 1;
if (flowerStage(tokenId) < 2) {
upkeepNeeded = true;
}
}
// We don't use the checkData in this example. The checkData is defined when the Upkeep was registered.
}
function performUpkeep(bytes calldata /* performData */) external override {
//We highly recommend revalidating the upkeep in the performUpkeep function
if ((block.timestamp - lastTimeStamp) > interval ) {
uint256 tokenId = tokenIdCounter.current() - 1;
if (flowerStage(tokenId) < 2) {
lastTimeStamp = block.timestamp;
growFlower(tokenId);
}
}
// We don't use the performData in this example. The performData is generated by the Automation's call to your checkUpkeep function
}
function tokenURI(uint256 tokenId)
public view override(ERC721, ERC721URIStorage) returns (string memory)
{
return super.tokenURI(tokenId);
}
// The following function is an override required by Solidity.
function _burn(uint256 tokenId) internal override(ERC721, ERC721URIStorage)
{
super._burn(tokenId);
}
}
Compile
Next we want to make sure that the contract has compiled with the correct version, so head to the Solidity Compiler tab on the left side of remix. Here we will ensure Auto compile is checked.

Deploy & Run Transaction
Next we navigate to the Deploy & Run Transactions tab and select our enviroment to Injected Provider - Metamask. We must ensure that we are connected to Sepolia test network.

Then, in the updateInterval field we want to input 120. This represents the interval between each attempt by Chainlink Automation to "trigger" the contract to update the NFT's metadata from seed stage to flower stage . More on Chainlink Automation's operations later in this page.
After that we want to deploy the contract by clicking the orange transact button and confirming the Metamask transaction.

In our constructor, we are minting an NFT to the deployer (your wallet address), so once the contract is deployed you should see that the balanceOf for your address will be 1.

You can view this NFT on Opensea by copying your contract address, and pasting it in the Opensea searchbar


Before we use Chainlink Automation, let's manually call the growFlower function in our smart contract on remix, passing in the _tokenId of 0 which is the NFT we have just minted (since it's the first NFT minted!). This will cause our seed to change into a seedling.

Then we will go back to Opensea to view our NFT and click the 3 dots in the top right corner, and select refresh metadata. This will tell Opensea to refresh the image of our NFT.

We can also force refresh by doing the following in our browser: Right Click → Inspect → Application → Storage → Clear Site Data

Once you’ve done both of these steps you should now be able to refresh the Opensea page and see that your seed has now changed into a seedling.

Next, we will integrate Chainlink Automation to automatically do this process for us, so we don’t have to manually call growFlower.
First, we will go to remix and call safeMint passing in your wallet address, this will mint you a new NFT. In Opensea you will now see that you have 2 NFTs, and your newly minted NFT (token id 1, since its the second minted NFT) will be a seed.

Then we will head to https://automation.chain.link/. Make sure you're Metamask is still connected to Ethereum Sepolia, and select Register new Upkeep

We will use Custom Logic for this example, so select that option and click next.

In Target contract address put the address of your deployed NFT contract from Remix, and click next.

This will then bring you to the Upkeep Details page, where you can give your upkeep a name. Also, ensure that your Admin Address is the wallet you are currently using in Metamask. You will also need to fund your upkeep with at least 3 LINK. If you don’t have any LINK you can obtain some from the Chainlink Faucet.

Lastly you want to click Register Upkeep at the bottom of the page and confirm the transactions. You will see a pop-up like this appear.

After a few seconds, it will update once it’s been finalised and the button will appear with View Upkeep which you should click.

When we deployed our smart contract, we'd put 120 seconds as the updateInterval. So every 2 minutes, Chainlink Automation will call checkUpkeep() in our smart contract to run the custom logic we have written there. So after a couple of minutes, if you head back to Opensea and look at your minted NFT select refresh metadata and reset your cache as we did before. You will then see that your NFT has started changing and eventually, it will be fully bloomed.

Last updated