Skip to main content

Create a dynamic NFT

Data on Irys is permanent and immutable, but you can use Irys' mutable references to simulate mutability and create dynamic NFTs that evolve based on onchain or offchain actions.

With mutable references, you create a single, static URL that is linked to a series of transactions. You can add a new transaction to the series at any time, and the URL will always resolve to the most recent transaction.

In this guide, you will create a dynamic NFT using Irys' mutable references. Dynamic NFTs are NFTs whose metadata evolves over time. They are commonly used in:

  • Gaming projects where in-game assets evolve as players progress.
  • Loyalty programs where NFTs evolve as users accumulate points.

This tutorial focuses on creating a SuperMon NFT that can evolve during gameplay. The NFT starts with a basic appearance that can be upgraded twice. We will use the Irys CLI to "mutate" the metadata, simulating the automatic changes that would occur through player interactions in an actual game.

Prerequisites​

Install the Irys CLI to upload your images and metadata.

Create mutable references​

Mutable references are a way to simulate "mutability".

  1. Create a single static URL that is tied to a chain of transactions:

    const irys = await getIrys();
    const receiptOne = await irys.upload("First TX");
    console.log(`TX 1 uploaded https://gateway.irys.xyz/mutable/${receiptOne.id}`);
  2. Update the "chain" by posting a second transaction (from the same wallet) tagged with the original transaction's ID:

    const tags = [{ name: "Root-TX", value: receiptOne.id }];
    const receiptTwo = await irys.upload("Second TX", { tags });
    console.log(`TX 2 uploaded https://gateway.irys.xyz/mutable/${receiptOne.id}`);

Deploy your smart contract​

Deploy your NFT smart contract. The following is a simple example that you can use to mint the NFT that we'll create.

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

// Import OpenZeppelin's ERC721 and ERC721URIStorage contracts
// These URLs are compatible with Remix IDE
import "@openzeppelin/contracts/token/ERC721/ERC721.sol";
import "@openzeppelin/contracts/token/ERC721/extensions/ERC721URIStorage.sol";
import "@openzeppelin/contracts/access/Ownable.sol";

contract SuperMon is ERC721URIStorage {
uint256 private _tokenIdCounter;

// No arguments in the constructor, the owner will be the contract deployer
constructor() ERC721("SuperMon", "SMON") {
_tokenIdCounter = 0;
}

// Mint function to create a new NFT
function mint(address to, string memory uri) public {
uint256 tokenId = _tokenIdCounter;
_tokenIdCounter += 1;
_safeMint(to, tokenId);
_setTokenURI(tokenId, uri);
}
}

Deploy the smart contract using Remix, or any other environment.

Upload the images​

  1. Right-click each image and save it on your local drive.

    Image Level 1Image Level 2Image Level 3
  2. Fund the Irys Devnet node with 0.1 Linea Sepolia ETH to pay for your uploads.

    info

    In all of these CLI examples, ensure you replace the value of the -w parameter with your own private key.

    irys fund 100000000000000000 \
    -n devnet \
    -t linea-eth \
    -w 6dd5e....54a120201cb6a \
    --provider-url https://rpc.sepolia.linea.build
    note

    The fund command accepts a value in atomic units, 0.1 ETH is equal to 100000000000000000 in atomic units.

  3. Use the Irys CLI to upload each of the images to the Irys Devnet.

    irys upload image-level-1.png \
    -n devnet \
    -t linea-eth \
    -w 6dd5e....54a120201cb6a \
    --provider-url https://rpc.sepolia.linea.build

    irys upload image-level-2.png \
    -n devnet \
    -t linea-eth \
    -w 6dd5e....54a120201cb6a \
    --provider-url https://rpc.sepolia.linea.build

    irys upload image-level-3.png \
    -n devnet \
    -t linea-eth \
    -w 6dd5e....54a120201cb6a \
    --provider-url https://rpc.sepolia.linea.build

Upload the metadata​

  1. Create three metadata files similar to the ones below. Make sure to change the value of the image field to match the URLs generated in the previous step.

    {
    "name": "SuperMon",
    "symbol": "SMON",
    "image": "https://gateway.irys.xyz/QH3rksVhbFg5L9vvjGzb4POUibCEG-TGPInmofp-O-o",
    "description": "Super dooper, changing shapes, changing power",
    "attributes": [
    {
    "trait_type": "supermon-level",
    "value": "1"
    }
    ]
    }
    {
    "name": "SuperMon",
    "symbol": "SMON",
    "image": "https://gateway.irys.xyz/QH3rksVhbFg5L9vvjGzb4POUibCEG-TGPInmofp-O-o",
    "description": "Super dooper, changing shapes, changing power",
    "attributes": [
    {
    "trait_type": "supermon-level",
    "value": "2"
    }
    ]

    }
    {
    "name": "SuperMon",
    "symbol": "SMON",
    "image": "https://gateway.irys.xyz/QH3rksVhbFg5L9vvjGzb4POUibCEG-TGPInmofp-O-o",
    "description": "Super dooper, changing shapes, changing power",
    "attributes": [
    {
    "trait_type": "supermon-level",
    "value": "3"
    }
    ]

    }
  2. Upload just the first file using the Irys CLI:

    irys upload metadata-level-1.json \
    -n devnet \
    -t linea-eth \
    -w 6dd5e....54a120201cb6a \
    --provider-url https://rpc.sepolia.linea.build

The CLI will return a URL similar to https://gateway.irys.xyz/NDtKvjlmZL2iXUPmX6P-BuvtnvAEFkUiQWG8ToyK5FM. To convert that to a mutable references URL, interpolate it by adding /mutable/ after the domain and before the transaction ID.

Your final URL will be similar to https://gateway.irys.xyz/mutable/NDtKvjlmZL2iXUPmX6P-BuvtnvAEFkUiQWG8ToyK5FM.

Mint the NFT​

To mint your NFT in Remix:

  1. Under Deployed Contracts, locate your contract and expand it to see its functions.
  2. Under the Mint function, enter the wallet address you want to mint the NFT to and the metadata URL (e.g. https://gateway.irys.xyz/mutable/NDtKvjlmZL2iXUPmX6P-BuvtnvAEFkUiQWG8ToyK5FM) from the previous step.
  3. Select Transact.

You can now view the NFT on the Opensea Testnet.

Mutate the metadata​

To "mutate" the NFT, upload a new version of the metadata tagging it as having a Root-TX equal to the transaction ID of your first transaction. In this example, we pass the value of NDtKvjlmZL2iXUPmX6P-BuvtnvAEFkUiQWG8ToyK5FM, however make sure to change this to match your unique transaction ID.

irys upload metadata-level-2.json \
-n devnet \
-t linea-eth \
-w 6dd5e....54a120201cb6a \
--tags Root-TX NDtKvjlmZL2iXUPmX6P-BuvtnvAEFkUiQWG8ToyK5FM \
--provider-url https://rpc.sepolia.linea.build

Return to Opensea and request that it refresh your metadata.

Give it a few minutes and your updated NFT should be visible.

Free metadata uploads​

This tutorial uses the Irys Devnet where uploads are kept for ~60 days and are paid for with free faucet currencies. When deploying production projects, you'll use Irys' mainnet where uploads are permanent.

Uploads of less than 100 KiB are free on Irys, which is more than enough for most metadata files. This means projects can let users "evolve" their NFTs without having to pay gas fees.

Caching​

Wallets and NFT websites typically cache metadata to optimize performance, this can affect the visibility of updates to dynamic NFTs. While OpenSea offers a feature for users to manually request metadata refreshes, not all platforms provide this level of control. When building dynamic NFT projects, make sure to thoroughly test and understand the implications of caching on your platform.