metadata refers to "auxiliary data" that describes some "main data".
It's a vague term that developers use for a lot of different things, but it
means something specific when talking about NFTs.
In the case of an NFT, let's say a photograph NFT, the "main data" is the image
file. This is likely a binary file format, such as PNG, and is typically stored
on some cloud/decentralized server. The
metadata is then the "auxiliary data"
about the photograph: its title, description, traits, and most importantly,
the link to the PNG file wherever it may be stored.
The metadata is stored in a JSON format, such as this:
There are several different supported metadata schemas. The one shown above follows one of the formats outlined in the OpenSea Metadata Standards.
Remember the smart contract function
tokenURI from the previous section? The
tokenURI function returns the uri of a metadata JSON file like the one shown
above! This is how wallets, marketplaces, and other smart contracts know where
to find the image for the NFT: they first call the
tokenURI function to read
the metadata JSON file, and then they display the link from the
image field of
For an ERC-721 collection of 10000 NFTs, each token will typically have its own metadata JSON file — this means 10000 separate JSON files. Where do all of these get stored?
There are typically 3 places where metadata files are stored:
- "On-chain" - Directly on the blockchain as part of a smart contract
- IPFS - An immutable, decentralized file storage solution, https://ipfs.io/
- Custom API - A centralized server that can serve arbitrary data
Let's walk through each of these options.
The metadata JSON files can be stored or generated as part of your smart contract. In general, storing data on the blockchain is very expensive, so this approach isn't extremely common. However, for projects where the metadata is relatively simple and can be generated on-the-fly, e.g. Loot, then this approach is a great option.
tokenURI function must return a valid uri that represents a JSON file;
after generating the metadata fields (title, image, attributes, etc), that data
must then be converted into a base64-encoded JSON file.
As an example, this is what it looks like when calling
tokenURI for token 10
Loot smart contract on Etherscan.
Decoding this data gives:
Note that the
image uri is also encoded as a base64 SVG file. Even the image
is generated as part of the smart contract!
IPFS is a decentralized storage protocol. It's conceptually similar to consumer storage services like Dropbox or Google Drive, only instead of a single company storing all of the files, anybody can store the files. Storage providers are incentivized to store files because they earn filecoin for their storage space.
There are a couple features that make IPFS well-suited for storing NFT-related files, compared to e.g. Dropbox:
- Immutable - Files stored on IPFS can't be changed. An IPFS uri always refers to the exact same file data. Note that files can potentially disappear if there are no providers storing them.
- Permissionless - Much like the blockchain, IPFS is a permissionless system: anybody can store files and communicate with other peers on the network, and no centralized authority can take files down. (NFT collectors don't have to worry about their NFTs being censored)
Additionally, IPFS introduces its own
ipfs:// uri scheme that's supported by
platforms like OpenSea.
As an example, let's consider Bored Ape Yacht Club again. Calling the
function for token 10 returns an
If you put
in your browser address bar... probably nothing will happen. A few browsers,
like Brave, natively support IPFS and will show the file, but most won't.
However, the organization behind IPFS hosts a
https:// server for accessing
files from your browser, in this case:
Once again we should see a familiar JSON format:
You'll also notice that this uri ends in the number "10". You can try changing it to any other number 0 through 9999. In order to get the uris to start with the same string, all 10000 metadata files are stored together on IPFS in a single folder.
For smaller NFT collections, it's also common to upload files individually and use a smart contract that supports setting a completely different uri for each token id.
There's a great IPFS storage provider, https://nft.storage/, that's free for the foreseeable future (details on their site)
Lastly, you can set up a custom API that serves arbitrary JSON metadata. For developers, this is likely the easiest option; for non-developers, IPFS may be simpler.
The advantage of this approach is speed, cost, and flexibility. As an example,
721.so serves a simple set of NFT metadata for testing purposes. For any
token id from 0 to infinity, the API will return valid metadata. This would be
impossible with IPFS, since you could never upload infinity files.
For example, token 123456789, https://www.721.so/api/example/metadata/123456789:
Choosing a Storage Option
The following table highlights a few key factors to consider when storing metadata:
Typically NFT collectors prefer "on-chain" metadata when possible. Data on the
blockchain is immutable (can't be changed) and can never disappear,
which is exactly what you want for most NFTs. However, this option only really
makes sense for certain kinds of very simple NFTs, where the
image is embedded
into the metadata (e.g. Loot). The image in this case is almost always a small
SVG file, rather than PNG files, video files, or interactive multimedia, since
otherwise it would simply be too expensive.
Most projects store metadata files on IPFS. Data stored on IPFS is immutable, but can disappear. Somebody has to pay a storage provider, or run their own server, to keep the files available. Typically today the NFT creator pays for IPFS storage, however conceptually it may make more sense for the collector to pay for storage of the NFTs they buy.
Custom APIs are simple and inexpensive to set up as a developer. They're also convenient because you can easily fix bugs that you may not encounter until after the NFT sale begins. However, the downside for collectors is that the data is not immutable and can disappear at any time. Many projects still use custom APIs however, and ultimately it's up to the project and its collectors to decide if the fact that data can change or disappear is important or not.
Arweave is another popular decentralized storage provider besides IPFS. It promises to store files forever for a 1-time payment. It's not as widely supported as IPFS, and it's unclear if this 1-time-payment hypothesis will work long term, but it's definitely an interesting option to consider.
Configurable Smart Contracts
Many NFT smart contracts allow configuring uris at the smart contract level, so functionally the NFTs can still change or disappear even if they're stored using an immutable storage system like IPFS.
This configuration feature is useful for a few reasons:
- There may be a bug in the metadata, and so it can be useful to be able to change it and fix things.
- You may want to do a "reveal", where people mint NFTs before the individual traits have been revealed. After all NFTs have been minted, you can change the uris to point to the real metadata.
- You may actually want to update the NFTs in the future, if that's a feature of your collection.
For example, Bored Ape Yacht Club and all Studio 721 contracts include the smart
setBaseURI, which allows changing token uris.
Bored Ape Yacht Club also includes a separate hash of all of their metadata files, which they call "provenance", so that it's obvious if something changes. There's a newer approach that can do this in the token uris themselves, by uploading a Car file to IPFS, which is what Studio 721 Artkit does.
The contract owner can also theoretically call the
renounceOwnershipfunction to revoke their own ability to change the uris, though this rarely happens in practice. Most NFT creators have good intentions, so the ability to change the uris is only used for legitimate reasons, and is generally supported by collectors.