Photo by fabio on Unsplash

How we moved NFT Metadata off IPFS and into the Smart Contract

Millionaire's Mining Club
4 min readMar 31, 2022

--

When Millionaire’s Mining Club first launched the NFT Metadata was stored on IPFS. At the time this seemed like the right choice, but later on IPFS started to limit our flexibility. That’s when we started to explore storing the NFT Metadata in the smart contract.

What is NFT Metadata?

According to the ERC-721 NFT standard, NFTs can have an optional tokenURI function. tokenURI has an input of tokenId and returns a string URI containing additional metadata that describes the NFT.

This tokenURI how platforms like OpenSea can discover things like images, properties, stats, details, etc about the NFT.

Here’s an example:

{
"name": "Founders Edition",
"description": "Founders Edition Nifty Miner",
"external_url": "https://miningclub.app/token/0",
"image": "https://gateway.pinata.cloud/ipfs/QmTyeUQzwCnAPx9ZruQA9isdHP4YDdmbr1boqDSXFJGBbp/founders-electric-blue+.png",
"background_color": "ffffff",
"attributes": [
{
"trait_type": "Mint Number",
"value": 1,
"display_type": "number"
},
{
"trait_type": "Edition",
"value": "Founders"
},
{
"trait_type": "Rarity",
"value": "Ultra Rare"
},
{
"trait_type": "Highlight",
"value": "Electric Blue"
},
{
"trait_type": "Quality",
"value": "Perfect"
}
]
}

This is the metadata that describes the Founders Edition NFT (with tokenId of 0) on OpenSea.

Why Store Metadata on IPFS?

We started storing the metadata on IPFS initially. Mostly because this was the standard. It’s how most projects store the metadata. This typically works for most projects that have immutable NFTS (or NFTs that do not change).

Pinata provided an https URI wrapper on top of IPFS, giving OpenSea and easy way to query the NFT Metadata.

Why Not Store Metadata on IPFS?

With Millionaire’s Mining Club, IPFS became a limitation. Every time the NFT data changed, a new set of metadata would have to be pushed to IPFS.

Querying IPFS can be very slow.

IPFS also requires a machine always on and connected to the IPFS network to serve the files. This usually involves a pinning service like Pinata. Pinata would also have to be updated every time we made a change to IPFS.

The Nifty Miner NFTs needed more flexibility. They need the ability to update the Metadata instantly and have that update triggered from the Smart Contract itself. This was quickly becoming complicated with IPFS.

How to Store Metadata in the Smart Contract

The Smart Contract knows everything about the NFT and can easily update the properties. It made sense to find a solution to also store the metadata in the smart contract.

Beyond the standard URIs we are typically used to, there are Data URIs.

Example Data URI: data:application/json;base64,eyJoZWxsbyI6IndvcmxkIn0=

This Data URI is of type application/json, which is also base64 encoded. The base64 encoding was important for OpenSea to parse the URI.

The contents of this Data URI are:

{"hello":"world"}

Limitations of Storing Metadata in the Smart Contract

The first limitation is the size of the URI. There’s a best practices limit of 2,000 characters for a URI. So it’s best to stay as much under that limit as possible. If NFT meta data exceeds this limit, the Data URI solution will not work.

Large amounts of string data now has to be stored in the smart contract. This will increase the cost of deploying smart contracts, but more importantly will put the smart contract in danger of exceeding the 24KB limit on smart contract size.

Changes to the Metadata may require a redeployment of the Smart Contracts.

Our Solution

Our solution was to deploy a separate JsonMetadata Smart Contract. This will keep the bulky size out of our main NFT Smart Contract to keep us under that 24KB limit.

The JsonMetadata methods can then be called from the main NFT Smart Contract and return the Data URI.

Here’s a small clip from the JsonMetadata smart contract.

import "@openzeppelin/contracts-upgradeable/access/AccessControlUpgradeable.sol";
import "@openzeppelin/contracts/utils/Base64.sol";
contract JsonMetadata is AccessControlUpgradeable {
string public _baseUri;
string constant _dataUri = "data:application/json;base64,";
function getVipCardUri() public view returns (string memory) {
bytes memory json = abi.encodePacked(
'{"description":"VIP Card","external_url":"',
_baseUri,
'vip-card.png","image":"',
_baseUri,
'vip-card.png","animation_url":"',
_baseUri,
'vip-card.glb","name":"VIP Card","background_color":"ffffff","attributes":[{"trait_type":"Status","value":"VIP"},{"trait_type":"Access","value":"Whitelist"}]}'
);
return string(abi.encodePacked(_dataUri, Base64.encode(json)));
}
}

Summary

Storing NFT Metadata on IPFS was limiting to our dynamic NFTs. The metadata needed to be just as dynamic as the NFTs.

Data URIs offered a solution to store the NFT Metadata directly in the smart contracts.

Even though Data URIs have some limitations, we believe Millionaire’s Mining Club NFTs will be able to stay within those limit. The pros definitely outweigh the cons.

Socials

As with most NFT projects, all the good stuff happens in Discord. Come and say hi! 🍻

🌐 https://miningclub.app/
🎮 https://discord.gg/p3njfbSjA9
🐦 https://twitter.com/miningclubapp
📷 https://www.instagram.com/miningclubapp/

--

--