ZRC | Title | Status | Type | Author | Created (yyyy-mm-dd) | Updated (yyyy-mm-dd) |
---|---|---|---|---|---|---|
7 | NFT Metadata Standard | Implemented | Standard | Neuti Yoo [email protected] Elliott Green [email protected] Jun Hao Tan [email protected] |
2021-10-11 | 2022-03-09 |
- I. What is Metadata and Token URI?
- II. Abstract
- III. Motivation
- IV. Specification
- V. References
- VI. Copyright
Metadata is data that provides information about other data. Metadata allows NFTs to have additional properties e.g. name, resource, and attributes. The example is the following:
{
"name": "Creature #101",
"resources": [
{ "uri": "ipfs://QmZILGa7zXUbixvYJpgkRkaSCYEBtSwgVtfzkoD3YkNsE1" }
],
"attributes": [
{
"trait_type": "Background",
"value": "Black"
},
{
"trait_type": "Eyes",
"value": "Big"
},
{
"trait_type": "Mouth",
"value": "Grin"
}
]
}
The above is a JSON blob of data with the metadata for the NFT. It is returned by a token URI which is an IPFS, HTTP, or data URL. The examples are the following:
ipfs://QmZIL4tcBsMqLRuCQtPmPe84bpSjrC3Ky7t3JWuHXYB4aA/1
ipfs://QmZILw65yBXgyfG2ZBg5TrfB2hPjrDQH3RCQFJGkARStAE
ar://ZILsR4OrYvODj7PD3czIAyNJalub0-vdV_JAg5NqA-o
https://ipfs.io/ipfs/QmZIL4tcBsMqLRuCQtPmPe84bpSjrC3Ky7t3JWuHXYB4aA/1
https://foo.mypinata.cloud/ipfs/QmZILYgURKVnLWBm1aXH1BqKqFgmj7j1K6MWAFTkf9xm8A/1
https://creatures-api.zilliqa.com/api/creature/1
data:application/json;base64,ewogICJuYW1lIjogIkNyZWF0dXJlICMxMDEiLAogICJyZXNvdXJjZXMiOiBbCiAgICB7ICJ1cmkiOiAiaXBmczovL1FtWklMR2E3elhVYml4dllKcGdrUmthU0NZRUJ0U3dnVnRmemtvRDNZa05zRTEiIH0KICBdCn0=
data:application/json,%7B%22name%22%3A%22Creature%20%23101%22%2C%22resources%22%3A%5B%7B%22uri%22%3A%22ipfs%3A%2F%2FQmZILGa7zXUbixvYJpgkRkaSCYEBtSwgVtfzkoD3YkNsE1%22%7D%5D%7D
Token URIs can be space-efficient and gas-efficient with the concatenation of ZRC-6 compliant base URI and token ID. The concatenated token URI is <base_uri><token_id>
.
Base URI |
---|
ipfs://QmZILCdt3yb6mZitzWBmQr65AW6Wska295Dg9nbS0M3UrI/ |
When the base URI is the above, the token URIs are the following:
Token ID | Token URI |
---|---|
1 | ipfs://QmZILCdt3yb6mZitzWBmQr65AW6Wska295Dg9nbS0M3UrI/1 |
2 | ipfs://QmZILCdt3yb6mZitzWBmQr65AW6Wska295Dg9nbS0M3UrI/2 |
3 | ipfs://QmZILCdt3yb6mZitzWBmQr65AW6Wska295Dg9nbS0M3UrI/3 |
Token URI optimization by using base URI
By using base URI, a ZRC-6 contract state can use less space for token URIs. Let's assume the following contract state contains n
token URIs where n
is the number of tokens:
"base_uri": "",
"token_uris": {
"1": "ipfs://QmZILCdt3yb6mZitzWBmQr65AW6Wska295Dg9nbS0M3UrI/1",
"2": "ipfs://QmZILCdt3yb6mZitzWBmQr65AW6Wska295Dg9nbS0M3UrI/2",
"3": "ipfs://QmZILCdt3yb6mZitzWBmQr65AW6Wska295Dg9nbS0M3UrI/3",
"4": "ipfs://QmZILCdt3yb6mZitzWBmQr65AW6Wska295Dg9nbS0M3UrI/4",
"5": "ipfs://QmZILCdt3yb6mZitzWBmQr65AW6Wska295Dg9nbS0M3UrI/5"
}
This can be optimized by using base_uri
as the following:
"base_uri": "ipfs://QmZILCdt3yb6mZitzWBmQr65AW6Wska295Dg9nbS0M3UrI/",
"token_uris": {}
It is space-efficient because the contract state can only contain a base URI, instead of n
token URIs. Also, it is gas-efficient since token_uris
is not mutated, resulting in less gas cost for minting or burning.
Limitations
However, there are cases where this optimization is not possible e.g., randomized or dynamic minting.
Note that a token can have its own token URI when a base URI cannot be used as the following:
Token ID | Token URI |
---|---|
1 | ipfs://QmZILCdt3yb6mZitzWBmQr65AW6Wska295Dg9nbS0M3UrI |
2 | ipfs://QmZILw65yBXgyfG2ZBg5TrfB2hPjrDQH3RCQFJGkARStAE |
3 | ipfs://QmZILGa7zXUbixvYJpgkRkaSCYEBtSwgVtfzkoD3YkNsE1 |
"base_uri": "",
"token_uris": {
"1": "ipfs://QmZILCdt3yb6mZitzWBmQr65AW6Wska295Dg9nbS0M3UrI"
"2": "ipfs://QmZILw65yBXgyfG2ZBg5TrfB2hPjrDQH3RCQFJGkARStAE"
"3": "ipfs://QmZILGa7zXUbixvYJpgkRkaSCYEBtSwgVtfzkoD3YkNsE1"
}
Also, it is possible that some tokens use the concatenated URIs while some tokens use their own token URIs as the following:
Token ID | Token URI |
---|---|
1 | ipfs://QmZILCdt3yb6mZitzWBmQr65AW6Wska295Dg9nbS0M3UrI/1 |
2 | ipfs://QmZILCdt3yb6mZitzWBmQr65AW6Wska295Dg9nbS0M3UrI/2 |
3 | ipfs://QmZILw65yBXgyfG2ZBg5TrfB2hPjrDQH3RCQFJGkARStAE |
"base_uri": "ipfs://QmZILCdt3yb6mZitzWBmQr65AW6Wska295Dg9nbS0M3UrI/",
"token_uris": {
"3": "ipfs://QmZILw65yBXgyfG2ZBg5TrfB2hPjrDQH3RCQFJGkARStAE"
}
In this case, Token ID 1
and Token ID 2
use the concatenated URIs with ZRC-6 base_uri
field while Token ID 3
uses its own token URI by using ZRC-6 token_uris
field.
ZRC-7 standardizes the NFT metadata structure.
The consistent metadata structure can help the NFT creators and builders to handle the NFT metadata more simply.
There are two types of metadata structure to be described: collection metadata, token metadata
Type | Description | Required |
---|---|---|
Collection Metadata | Contract-level metadata for the NFT collection. It is returned by a URI of this format: <base_uri>metadata.json . For example, if ipfs://QmZILGa7zXUbixvYJpgkRkaSCYEBtSwgVtfzkoD3YkNsE1/ is the ZRC-6 compliant base_uri , then collection metadata JSON file is returned by ipfs://QmZILGa7zXUbixvYJpgkRkaSCYEBtSwgVtfzkoD3YkNsE1/metadata.json . It can be space-efficient to use collection metadata. It's because the redundant data in the token metadata can be stored in the collection metadata instead. This is optional. If there is no base_uri , the collection metadata cannot be accessed. |
|
Token Metadata | Token-level metadata for a specific NFT. It is returned by a token URI e.g., ipfs://QmZILCdt3yb6mZitzWBmQr65AW6Wska295Dg9nbS0M3UrI/1 |
✓ |
Collection metadata must be structured as the following:
Property | Type | Description | Required |
---|---|---|---|
name |
String |
Name of the collection. | ✓ |
description |
String |
A human readable description of the collection. | |
external_url |
String |
A URL that points to an external website presenting the collection. | |
animation_url |
String |
A URL to a multi-media attachment for the collection. The examples of file extensions are GLTF, GLB, WEBM, MP4, M4V, OGV, OGG, MP3, WAV, and OGA. Also, animation_url can be HTML pages for interactive NFTs using JavaScript canvas, WebGL, etc. |
Minimal
{
"name": "Unique and Diverse Creatures"
}
Basic
{
"name": "Unique and Diverse Creatures",
"description": "10,000 unique and diverse creatures living on the blockchain.",
"external_url": "https://example.com/creature",
"animation_url": "https://animation.example.com/creature"
}
Other Properties
Note that it is valid to have other properties for several use cases.
{
"name": "Unique and Diverse Creatures",
"foo": "bar"
}
Token metadata must be structured as the following:
Property | Type | Description | Required |
---|---|---|---|
name |
String |
Name of the asset. | ✓ |
resources |
Array of Objects |
An array of resources. Each resource has the following properties:
|
✓ |
attributes |
Array of Objects |
An array of attributes. Each attribute has the following properties:
|
|
description |
String |
A human readable description of the asset. | |
external_url |
String |
A URL that points to an external website presenting the asset. | |
animation_url |
String |
A URL to a multi-media attachment for the asset. The examples of file extensions are GLTF, GLB, WEBM, MP4, M4V, OGV, OGG, MP3, WAV, and OGA. Also, animation_url can be HTML pages for interactive NFTs using JavaScript canvas, WebGL, etc. |
|
transitions |
Array of Objects |
An array of transitions that can be executed by the token owner. Each transition has the following properties:
|
Minimal
{
"name": "Creature #101",
"resources": [
{ "uri": "ipfs://QmZILGa7zXUbixvYJpgkRkaSCYEBtSwgVtfzkoD3YkNsE1" }
]
}
Basic
{
"name": "Creature #101",
"resources": [
{
"uri": "ipfs://QmZILGa7zXUbixvYJpgkRkaSCYEBtSwgVtfzkoD3YkNsE1",
"mime_type": "image/png"
}
],
"attributes": [
{
"trait_type": "Background",
"value": "Black"
},
{
"trait_type": "Eyes",
"value": "Big"
},
{
"trait_type": "Mouth",
"value": "Grin"
},
{
"display_type": "timestamp",
"trait_type": "Birthday",
"value": 1546360800
}
]
}
{
"name": "Sound #101",
"resources": [
{
"uri": "ipfs://QmZILGa7zXUbixvYJpgkRkaSCYEBtSwgVtfzkoD3YkNsE1",
"mime_type": "audio/mpeg"
}
],
"attributes": [
{
"trait_type": "Cover",
"value": "https://storage.googleapis.com/sound-prod.appspot.com/sound/101/cover.png",
"mime_type": "image/png",
"integrity": "sha256-8z5D++W8NDHzFm5rY4/JxkXlIlU2cSQ65XjighJVk9U="
}
]
}
Resource Integrity
Note that resource is stored on centralized storage.
{
"name": "Creature #101",
"resources": [
{
"uri": "ipfs://QmZILGa7zXUbixvYJpgkRkaSCYEBtSwgVtfzkoD3YkNsE1"
"mime_type": "image/png",
},
{
"uri": "https://storage.googleapis.com/creature-prod.appspot.com/creature/101.png"
"mime_type": "image/png",
"integrity": "sha256-8z5D++W8NDHzFm5rY4/JxkXlIlU2cSQ65XjighJVk9U="
}
],
}
Description
{
"name": "Creature #101",
"resources": [
{ "uri": "ipfs://QmZILGa7zXUbixvYJpgkRkaSCYEBtSwgVtfzkoD3YkNsE1" }
],
"description": "10,000 unique and diverse creatures living on the blockchain."
}
External & Animation URL
{
"name": "Creature #101",
"resources": [
{ "uri": "ipfs://QmZILGa7zXUbixvYJpgkRkaSCYEBtSwgVtfzkoD3YkNsE1" }
],
"external_url": "https://example.com/creature/101",
"animation_url": "https://animation.example.com/creature/101"
}
Transitions
{
"name": "Creature #101",
"resources": [
{ "uri": "ipfs://QmZILGa7zXUbixvYJpgkRkaSCYEBtSwgVtfzkoD3YkNsE1" }
],
"transitions": [
{
"vname": "Foo",
"params": [
{
"vname": "x",
"type": "Uint256",
"default_value": "1"
}
]
}
]
}
Other Properties
Note that it is valid to have other properties for several use cases.
{
"name": "Creature #101",
"resources": [
{ "uri": "ipfs://QmZILGa7zXUbixvYJpgkRkaSCYEBtSwgVtfzkoD3YkNsE1" }
],
"id": 101
}
{
"name": "Creature #101",
"resources": [
{ "uri": "ipfs://QmZILGa7zXUbixvYJpgkRkaSCYEBtSwgVtfzkoD3YkNsE1" }
],
"properties": {
"base": "cat",
"rich_property": {
"name": "eyes",
"value": "big",
"display_value": "Big"
}
}
}
{
"name": "Creature #101",
"resources": [
{ "uri": "ipfs://QmZILGa7zXUbixvYJpgkRkaSCYEBtSwgVtfzkoD3YkNsE1" }
],
"attributes": [
{
"trait_type": "Pupil Color",
"value": "Deep Sea Green",
"colors": [
{
"name": "Pupil",
"value": "#07595c"
}
]
}
]
}
- OpenSea - Metadata Standards
- Metaplex - Token Metadata Standard
- ARCs - Algorand Standard Asset Parameters Conventions for Fungible and Non-Fungible Tokens
- ZRC-6 - Non-Fungible Token Standard
- EIP-721: Non-Fungible Token Standard
- EIP-1155: Multi Token Standard
- RFC 1738 - Uniform Resource Locators (URL)
- RFC 2397 - The "data" URL scheme
- RFC 6838 - Media Type Specifications and Registration Procedures
- IANA - Media Types
- Mozilla - MIME types (IANA media types)
- W3C - Subresource Integrity
- IPFS - Best Practices for Storing NFT Data using IPFS
Copyright and related rights waived via CC0.