Dynamic NFTs (Non-Fungible Tokens) are the next step in the non-fungible token evolution. They combine the uniqueness of a non-fungible token with dynamic inputs of on-chain and/or off-chain data. Dynamic NFTs are designed to evolve or change over time and the secret of their dynamic abilities lies in their smart contracts.
In this tutorial we will:
- Discuss static vs dynamic NFTs
- Create a dynamic NFT in Solidity
If this is your first time learning about NFTs or would like to create an NFT project we recommend you begin your journey by reading How to create and deploy an NFT collection and/or ERC721 contract that supports sales royalties.
Static vs. Dynamic NFTs
The majority of NFTs created are static. A static NFT is an NFT with permanently immutable traits recorded on the blockchain that cannot be altered. These types of NFTs are still images, videos, gif files, music, unlockable elements, etc. For example, a static NFT could be a picture of a basketball player shooting a ball into a basket.
Dynamic NFTs or dNFTs are the next stage in the evolution of the NFT space. They combine the verifiable unique qualities of NFTs with dynamic data inputs. The dynamic inputs to these NFTs can be from on-chain or off-chain computations. Oracles are a key building block in providing dynamic data elements to NFTs from the real world. For example, a dynamic NFT could be of a basketball player’s statistics that update in real time to reflect that player’s actual performance.
Smart contracts make it possible for NFTs to evolve over time. When an NFT token is requested the smart contract determines what data to provide back to the user. The smart contract can evaluate on-chain data and/or off-chain data using an Oracle to determine how to respond. This makes dynamic NFTs very exciting as you can have an NFT that changes, updates, expands over time.
Example of a dynamic NFT
There are not that many dynamic NFTs in the market place. One example is the Moody Ghost collection on Open Sea. Please note that this is not an endorsement for the Moody Ghost project only an example of a dynamic NFT. The smart contract evaluates On-chain data to trigger the ghost’s mouth expression to change.
Below is an example of the same Moody Ghost NFT with different mouth expressions that are triggered by on-chain data. Read the smart contract to determine how this works.
To experiment and see a Moody Ghost change expressions go to Open Sea and refresh the metadata for a particular NFT. Then reload the web page. The NFT picture and traits will change if warranted.
How does a dynamic NFT work
A dynamic NFT can have the following process flow.
- A person or process makes a request to a smart contract for a NFT tokens URI
- The smart contract receives the request for processing
- The contract makes a call for on-chain data and results are processed
- The smart contract makes a call for off-chain data using an Oracle and results are processed
- The contract evaluates the on-chain and/or off-chain data and returns media 1 or media 2 to the requestor
Note: On-chain data, off-chain data or a combination of both can be used to create a dynamic NFT.
Create a dynamic NFT
Lets create a dynamic NFT in Solidity. For this tutorial we will create a very basic ERC721 NFT token contract. This token contract is a bare bones contract to allow us to focus on the components of the contract which make it dynamic.
The token URI function is where the logic resides to switch between two URIs. In this particular example the function evaluates on-chain data (block timestamp) to determine which URI to return to the requestor. In this particular example there are two sets of images and json files deployed to ipfs.
Compile and deploy the dynamic NFT example contract below in Remix. Read the comments in the code to understand how the smart contract works.
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.12;
//import Open Zepplin contracts
import "https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/token/ERC721/ERC721.sol";
import "https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/utils/Strings.sol";
contract NFT is ERC721 {
using Strings for uint256;
uint256 private _tokenIds;
string public baseExtension = ".json";
//create two URIs.
//the contract will switch between these two URIs
string aUri = "URIaaaaaaaaaa";
string bUri = "URIbbbbbbbbbb";
constructor() ERC721("NAMEOFTOKEN", "TOKENSYMBOL") {}
//use the mint function to create an NFT
function mint()
public
returns (uint256)
{
_tokenIds += 1;
_mint(msg.sender, _tokenIds);
return _tokenIds;
}
//the token URI function will contain the logic to determine what URI to show
function tokenURI(uint256 tokenId) public view virtual override returns (string memory)
{
require(
_exists(tokenId),
"ERC721Metadata: URI query for nonexistent token"
);
//if the block timestamp is divisible by 2 show the aURI
if (block.timestamp % 2 == 0) {
return bytes(aUri).length > 0
? string(abi.encodePacked(aUri, tokenId.toString(), baseExtension))
: "";
}
//if the block timestamp is NOT divisible by 2 show the bURI
return bytes(bUri).length > 0
? string(abi.encodePacked(bUri, tokenId.toString(), baseExtension))
: "";
}
}
Try it in Remix
After you deploy the contract above in Remix mint a token using the mint function. Then use the tokenURI function to obtain the URI for token 1. Invoke the tokenURI function multiple times to see the URI change. The above example illustrates one way to create a dynamic NFT.
URI for token 1 returns aUri
URI for token 1 returns bUri
Evolving NFTs
There are multiple ways to implement a dynamic NFT and the example above is one approach. Another method is to store all metadata on the blockchain so it can not be change. Your requirements and use case will dictate your technical implementation.
This code is for learning and entertainment purposes only so do not use in a production environment. This code has not been audited so use at your own risk. Remember smart contracts are experimental and could contain bugs.
Resources
Blockchain Networks
Below is a list of EVM compatible Mainnet and Testnet blockchain networks. Each link contains network configuration, links to multiple faucets for test ETH and tokens, bridge details, and technical resources for each blockchain. Basically everything you need to test and deploy smart contracts or decentralized applications on each chain. For a list of popular Ethereum forums and chat applications click here.
Ethereum test network configuration and test ETH faucet information | |
Optimistic Ethereum Mainnet and Testnet configuration, bridge details, etc. | |
Polygon network Mainnet and Testnet configuration, faucets for test MATIC tokens, bridge details, etc. | |
Binance Smart Chain Mainnet and Testnet configuration, faucets for test BNB tokens, bridge details, etc. | |
Fanton networt Mainnet and Testnet configuration, faucets for test FTM tokens, bridge details, etc. | |
Kucoin Chain Mainnet and Testnet configuration, faucets for test KCS tokens, bridge details, etc. |
Web3 Software Libraries
You can use the following libraries to interact with an EVM compatible blockchain.
- Python: Web3.py Python library for interacting with Ethereum. Web3.py examples
- Js: web3.js Ethereum JavaScript API
- Java: web3j Web3 Java Ethereum Ðapp API
- PHP: web3.php A php interface for interacting with the Ethereum blockchain and ecosystem.
Nodes
Learn how to run a Geth node. Read getting started with Geth to run an Ethereum node.
Fix a transaction
How to fix a pending transaction stuck on Ethereum or EVM compatible chain