Dynamic NFTs on Ethereum

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.

moody ghost dynamic NFT

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.

Dynamic nfts moody ghosts

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.

Dynamic nfts on ethereum moody ghosts

How does a dynamic NFT work

dynamic nft process flow in Solidity

A dynamic NFT can have the following process flow.

  1. A person or process makes a request to a smart contract for a NFT tokens URI
  2. The smart contract receives the request for processing
  3. The contract makes a call for on-chain data and results are processed
  4. The smart contract makes a call for off-chain data using an Oracle and results are processed
  5. 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.

dynamic nft images and json on 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

dynamic nft token uri changes

URI for token 1 returns bUri

dynamic nft token uri changes

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.

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

Next Review – ERC721 contract that supports sales royalties