EIP3569 - Sealed NFT Metadata Standard

# Simple Summary

The Sealed NFT Metadata Extension provides a mechanism to immortalize NFT metadata in a cost-effective manner.

# Abstract

This standard accomplishes three things; it provides a way for potential collectors to verify that the NFT metadata will not change, allows creators to immortalize metadata for multiple tokens at one time, and allows metadata for many NFTs to be read and cached from one file. A creator can call the seal function for a range of one or many sequential NFTs. Included as an argument is a URI which points to a decentralized storage service like IPFS and will be stored in the smart contract. The URI will return a JSON object in which the keys are token IDs and the values are either a string which is a URI pointing to a metadata file stored on a decentralized file system, or raw metadata JSON for each token ID. The token ID(s) will then be marked as sealed in the smart contract and cannot be sealed again. The seal function can be called after NFT creation, or during the NFT creation process.

# Motivation

In the original ERC-721 standard, the metadata extension specifies a tokenURI function which returns a URI for a single token ID. This may be hosted on IPFS or might be hosted on a centralized server. There's no guarantee that the NFT metadata will not change. This is the same for the ERC-1155 metadata extension. In addition to that - if you want to update the metadata for many NFTs you would need to do so in O(n) time, which as we know is not financially feasible at scale. By allowing for a decentralized URI to point to a JSON object of many NFT IDs we can solve this issue by providing metadata for many tokens at one time rather than one at a time. We can also provide methods which give transparency into whether the NFT has be explicitly "sealed" and that the metadata is hosted on a decentralized storage space.

There is not a way for the smart contract layer to communicate with a storage layer and as such we need a solution which provides a way for potential NFT collectors on Ethereum to verify that their NFT will not be "rug pulled". This standard provides a solution for that. By allowing creators to seal their NFTs during or after creation, they are provided with full flexibility when it comes to creating their NFTs. Decentralized storage means permanence - in the fast-moving world of digital marketing campaigns, or art projects mistakes can happen. As such, it is important for creators to have flexibility when creating their projects. Therefore, this standard allows creators to opt in at a time of their choosing. Mistakes do happen and metadata should be flexible enough so that creators can fix mistakes or create dynamic NFTs (see Beeple's CROSSROAD NFT). If there comes a time when the NFT metadata should be immortalized, then the creator can call the seal method. Owners, potential owners, or platforms can verify that the NFT was sealed and can check the returned URI. If the sealedURI return value is not hosted on a decentralized storage platform, or the isSealed method does not return true for the given NFT ID then it can be said that one cannot trust that these NFTs will not change at a future date and can then decide if they want to proceed with collecting the given NFT.

# Specification

The key words “MUST”, “MUST NOT”, “REQUIRED”, “SHALL”, “SHALL NOT”, “SHOULD”, “SHOULD NOT”, “RECOMMENDED”, “MAY”, and “OPTIONAL” in this document are to be interpreted as described in RFC 2119.

interface SealedMetadata {
  /**
    @notice This function is used to set a sealed URI for the given range of tokens.
    @dev
      - If the sealed URI is being set for one token then the fromTokenId and toTokenId
      values MUST be the same.

      - If any token within the range of tokens specified has already
      been sealed then this function MUST throw.

      - This function MAY be called at the time of NFT creation, or after the NFTs have been created.

      - It is RECOMMENDED that this function only be executable by either the creator of the smart contract,
        or the creator of the NFTs, but this is OPTIONAL and should be implemented based on use case.

      - This function MUST emit the Sealed event

      - The URI argument SHOULD point to a JSON file hosted within a decentralized file system like IPFS

    @param fromTokenId The first token in a consecutive range of tokens
    @param toTokenId The ending token in a consecutive range of tokens
    @param uri A URI which points to a JSON file hosted on a decentralized file system.
  */
  function seal(uint256 fromTokenId, uint256 toTokenId, string memory uri) external;

  /**
    @notice This function returns the URI which the sealed metadata can be found for the given token ID
    @dev
      - This function MUST throw if the token ID does not exist, or is not sealed

    @param tokenId Token ID to retrieve the sealed URI for

    @return The sealed URI in which the metadata for the given token ID can be found
  */
  function sealedURI(uint256 tokenId) external view returns (string);

  /**
    @notice This function returns a boolean stating if the token ID is sealed or not
    @dev This function should throw if the token ID does not exist

    @param tokenId The token ID that will be checked if sealed or not

    @return Boolean stating if token ID is sealed
  */
  function isSealed(uint256 tokenId) external view returns (bool)

  /// @dev This emits when a range of tokens is sealed
  event Sealed(uint256 indexed fromTokenId, uint256 indexed toTokenId, string memory uri);

}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50

# Sealed Metadata JSON Format

The sealed metadata JSON file MAY contain metadata for many different tokens. The top level keys of the JSON object MUST be token IDs.


type ERC721Metadata = {
  name?: string;
  image?: string;
  description?: string;
}

type SealedMetaDataJson = {
  [tokenId: string]: string | ERC721Metadata;
}

const sealedMetadata: SealedMetaDataJson = {
    '1': {
        name: 'Metadata for token with ID 1'
    },
    '2': {
        name: 'Metadata for token with ID 2'
    },
    // Example pointing to another file
    '3': 'ipfs://SOME_HASH_ON_IPFS'
};
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21

# Rationale

Rationale for rule not explicitly requiring that sealed URI be hosted on decentralized filestorage

In order for this standard to remain future proof there is no validation within the smart contract that would verify the sealed URI is hosted on IPFS or another decentralized file storage system. The standard allows potential collectors and platforms to validate the URI on the client.

Rationale to include many NFT metadata objects, or URIs in one JSON file

By including metadata for many NFTs in one JSON file we can eliminate the need for many transactions to set the metadata for multiple NFTs. Given that this file should not change NFT platforms, or explorers can cache the metadata within the file.

Rationale for emitting Sealed event

Platforms and explorers can use the Sealed event to automatically cache metadata, or update information regarding specified NFTs.

Rationale for allowing URIs as values in the JSON file

If a token's metadata is very large, or there are many tokens you can save file space by referencing another URI rather than storing the metadata JSON within the top level metadata file.

# Backwards Compatibility

There is no backwards compatibility with existing standards. This is an extension which could be added to existing NFT standards.

# Security Considerations

There are no security considerations related directly to the implementation of this standard.

Copyright and related rights waived via CC0 (opens new window).

▲ Powered by Vercel