Skip to content

Latest commit

 

History

History
468 lines (377 loc) · 25.3 KB

arc-0003.md

File metadata and controls

468 lines (377 loc) · 25.3 KB
arc title description author discussions-to status type category created
3
Conventions Fungible/Non-Fungible Tokens
Parameters Conventions for Algorand Standard Assets (ASAs) for fungible tokens and non-fungible tokens (NFTs).
Fabrice Benhamouda (@fabrice102)
Final
Standards Track
ARC
2021-08-07

Algorand Standard Asset Parameters Conventions for Fungible and Non-Fungible Tokens

Abstract

The goal of these conventions is to make it simpler for block explorers, wallets, exchanges, marketplaces, and more generally, client software to display the properties of a given ASA.

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.

Comments like this are non-normative.

An ARC-3 ASA has an associated JSON Metadata file, formatted as specified below, that is stored off-chain.

ASA Parameters Conventions

The ASA parameters should follow the following conventions:

  • Unit Name (un): no restriction but SHOULD be related to the name in the JSON Metadata file
  • Asset Name (an): MUST be:
    • (NOT RECOMMENDED) either exactly arc3 (without any space)
    • (NOT RECOMMENDED) or <name>@arc3, where <name> SHOULD be closely related to the name in the JSON Metadata file:
      • If the resulting asset name can fit the Asset Name field, then <name> SHOULD be equal to the name in the JSON Metadata file.
      • If the resulting asset name cannot fit the Asset Name field, then <name> SHOULD be a reasonable shorten version of the name in the JSON Metadata file.
    • (RECOMMENDED) or <name> where <name> is defined as above. In this case, the Asset URL MUST end with #arc3.
  • Asset URL (au): a URI pointing to a JSON Metadata file.
    • This URI as well as any URI in the JSON Metadata file:
      • SHOULD be persistent and allow to download the JSON Metadata file forever.
      • MAY contain the string {id}. If {id} exists in the URI, clients MUST replace this with the asset ID in decimal form. The rules below applies after such a replacement.
      • MUST follow RFC-3986 and MUST NOT contain any whitespace character
      • SHOULD use one of the following URI schemes (for compatibility and security): https and ipfs:
        • When the file is stored on IPFS, the ipfs://... URI SHOULD be used. IPFS Gateway URI (such as https://ipfs.io/ipfs/...) SHOULD NOT be used.
      • SHOULD NOT use the following URI scheme: http (due to security concerns).
      • MUST be such that the returned resource includes the CORS header
        Access-Control-Allow-Origin: *
        
        if the URI scheme is https

        This requirement is to ensure that client JavaScript can load all resources pointed by https URIs inside an ARC-3 ASA.

      • MAY be a relative URI when inside the JSON Metadata file. In that case, the relative URI is relative to the Asset URL. The Asset URL SHALL NOT be relative. Relative URI MUST not contain the character :. Clients MUST consider a URI as relative if and only if it does not contain the character :.
    • If the Asset Name is neither arc3 nor of the form <name>@arc3, then the Asset URL MUST end with #arc3.
    • If the Asset URL ends with #arc3, clients MUST remove #arc3 when linking to the URL. When displaying the URL, they MAY display #arc3 in a different style (e.g., a lighter color).
    • If the Asset URL ends with #arc3, the full URL with #arc3 SHOULD be valid and point to the same resource as the URL without #arc3.

      This recommendation is to ensure backward compatiblity with wallets that do not support ARC-3.

  • Asset Metadata Hash (am):
    • If the JSON Metadata file specifies extra metadata e (property extra_metadata), then am is defined as:

      am = SHA-512/256("arc0003/am" || SHA-512/256("arc0003/amj" || content of JSON Metadata file) || e)
      

      where || denotes concatenation and SHA-512/256 is defined in NIST FIPS 180-4. The above definition of am MUST be used when the property extra_metadata is specified, even if its value e is the empty string. Python code to compute the hash and a full example are provided below (see "Sample with Extra Metadata").

      Extra metadata can be used to store data about the asset that needs to be accessed from a smart contract. The smart contract would not be able to directly read the metadata. But, if provided with the hash of the JSON Metadata file and with the extra metadata e, the smart contract can check that e is indeed valid.

    • If the JSON Metadata file does not specify the property extra_metadata, then am is defined as the SHA-256 digest of the JSON Metadata file as a 32-byte string (as defined in NIST FIPS 180-4)

There are no requirements regarding the manager account of the ASA, or its the reserve account, freeze account, or clawback account.

Clients recognize ARC-3 ASAs by looking at the Asset Name and Asset URL. If the Asset Name is arc3 or ends with @arc3, or if the Asset URL ends with #arc3, the ASA is to be considered an ARC-3 ASA.

Pure and Fractional NFTs

An ASA is said to be a pure non-fungible token (pure NFT) if and only if it has the following properties:

  • Total Number of Units (t) MUST be 1.
  • Number of Digits after the Decimal Point (dc) MUST be 0.

An ASA is said to be a fractional non-fungible token (fractional NFT) if and only if it has the following properties:

  • Total Number of Units (t) MUST be a power of 10 larger than 1: 10, 100, 1000, ...
  • Number of Digits after the Decimal Point (dc) MUST be equal to the logarithm in base 10 of total number of units.

In other words, the total supply of the ASA is exactly 1.

JSON Metadata File Schema

The JSON Medata File schema follow the Ethereum Improvement Proposal ERC-1155 Metadata URI JSON Schema with the following main differences:

  • Support for integrity fields for any file pointed by any URI field as well as for localized JSON Metadata files.
  • Support for mimetype fields for any file pointed by any URI field.
  • Support for extra metadata that is hashed as part of the Asset Metadata Hash (am) of the ASA.
  • Adding the fields external_url, background_color, animation_url used by OpenSea metadata format.

Similarly to ERC-1155, the URI does support ID substitution. If the URI contains {id}, clients MUST substitute it by the asset ID in decimal.

Contrary to ERC-1155, the ID is represented in decimal (instead of hexadecimal) to match what current APIs and block explorers use on the Algorand blockchain.

The JSON Metadata schema is as follows:

{
    "title": "Token Metadata",
    "type": "object",
    "properties": {
        "name": {
            "type": "string",
            "description": "Identifies the asset to which this token represents"
        },
        "decimals": {
            "type": "integer",
            "description": "The number of decimal places that the token amount should display - e.g. 18, means to divide the token amount by 1000000000000000000 to get its user representation."
        },
        "description": {
            "type": "string",
            "description": "Describes the asset to which this token represents"
        },
        "image": {
            "type": "string",
            "description": "A URI pointing to a file with MIME type image/* representing the asset to which this token represents. Consider making any images at a width between 320 and 1080 pixels and aspect ratio between 1.91:1 and 4:5 inclusive."
        },
        "image_integrity": {
            "type": "string",
            "description": "The SHA-256 digest of the file pointed by the URI image. The field value is a single SHA-256 integrity metadata as defined in the W3C subresource integrity specification (https://w3c.github.io/webappsec-subresource-integrity)."
        },
        "image_mimetype": {
            "type": "string",
            "description": "The MIME type of the file pointed by the URI image. MUST be of the form 'image/*'."
        },
        "background_color": {
            "type": "string",
            "description": "Background color do display the asset. MUST be a six-character hexadecimal without a pre-pended #."
        },
        "external_url": {
            "type": "string",
            "description": "A URI pointing to an external website presenting the asset."
        },
        "external_url_integrity": {
            "type": "string",
            "description": "The SHA-256 digest of the file pointed by the URI external_url. The field value is a single SHA-256 integrity metadata as defined in the W3C subresource integrity specification (https://w3c.github.io/webappsec-subresource-integrity)."
        },
        "external_url_mimetype": {
            "type": "string",
            "description": "The MIME type of the file pointed by the URI external_url. It is expected to be 'text/html' in almost all cases."
        },
        "animation_url": {
            "type": "string",
            "description": "A URI pointing to a multi-media file representing the asset."
        },
        "animation_url_integrity": {
            "type": "string",
            "description": "The SHA-256 digest of the file pointed by the URI external_url. The field value is a single SHA-256 integrity metadata as defined in the W3C subresource integrity specification (https://w3c.github.io/webappsec-subresource-integrity)."
        },
        "animation_url_mimetype": {
            "type": "string",
            "description": "The MIME type of the file pointed by the URI animation_url. If the MIME type is not specified, clients MAY guess the MIME type from the file extension or MAY decide not to display the asset at all. It is STRONGLY RECOMMENDED to include the MIME type."
        },
        "properties": {
            "type": "object",
            "description": "Arbitrary properties (also called attributes). Values may be strings, numbers, object or arrays."
        },
        "extra_metadata": {
            "type": "string",
            "description": "Extra metadata in base64. If the field is specified (even if it is an empty string) the asset metadata (am) of the ASA is computed differently than if it is not specified."
        },
        "localization": {
            "type": "object",
            "required": ["uri", "default", "locales"],
            "properties": {
                "uri": {
                    "type": "string",
                    "description": "The URI pattern to fetch localized data from. This URI should contain the substring `{locale}` which will be replaced with the appropriate locale value before sending the request."
                },
                "default": {
                    "type": "string",
                    "description": "The locale of the default data within the base JSON"
                },
                "locales": {
                    "type": "array",
                    "description": "The list of locales for which data is available. These locales should conform to those defined in the Unicode Common Locale Data Repository (http://cldr.unicode.org/)."
                },
                "integrity": {
                    "type": "object",
                    "patternProperties": {
                        ".*": { "type": "string" }
                    },
                    "description": "The SHA-256 digests of the localized JSON files (except the default one). The field name is the locale. The field value is a single SHA-256 integrity metadata as defined in the W3C subresource integrity specification (https://w3c.github.io/webappsec-subresource-integrity)."
                }
            }
        }
    }
}

All the fields are OPTIONAL. But if provided, they MUST match the description in the JSON schema.

The field decimals is OPTIONAL. If provided, it MUST match the ASA parameter dt.

URI fields (image, external_url, animation_url, and localization.uri) in the JSON Metadata file are defined similarly as the Asset URL parameter au. However, contrary to the Asset URL, they MAY be relative (to the Asset URL). See Asset URL above.

Integrity Fields

Compared to ERC-1155, the JSON Metadata schema allows to indicate digests of the files pointed by any URI field. This is to ensure the integrity of all the files referenced by the ASA. Concretly, every URI field xxx is allowed to have an optional associated field xxx_integrity that specifies the digest of the file pointed by the URI.

The digests are represented as a single SHA-256 integrity metadata as defined in the W3C subresource integrity specification. Details on how to generate those digests can be found on the MDN Web Docs (where sha384 or 384 are to be replaced by sha256 and 256 respectively as only SHA-256 is supported by this ARC).

It is RECOMMENDED to specify all the xxx_integrity fields of all the xxx URI fields, except for external_url_integrity when it points to a potentially mutable website.

Any field with a name ending with _integrity MUST match a corresponding field containing a URI to a file with a matching digest. For example, if the field hello_integrity is specified, the field hello MUST exist and MUST be a URI pointing to a file with a digest equal to the digest specified by hello_integrity.

MIME Type Files

Compared to ERC-1155, the JSON Metadata schema allows to indicate the MIME type of the files pointed by any URI field. This is to allow clients to display appropriately the resource without having to first query it to find out the MIME type. Concretly, every URI field xxx is allowed to have an optional associated field xxx_integrity that specifies the digest of the file pointed by the URI.

It is STRONGLY RECOMMENDED to specify all the xxx_mimetype fields of all the xxx URI fields, except for external_url_mimetype when it points to a website. If the MIME type is not specified, clients MAY guess the MIME type from the file extension or MAY decide not to display the asset at all.

Clients MUST NOT rely on the xxx_mimetype fields from a security perspective and MUST NOT break or fail if the fields are incorrect (beyond not displaying the asset image or animation correctly). In particular, clients MUST take all necessary security measures to protect users against remote code execution or cross-site scripting attacks, even when the MIME type looks innocuous (like image/png).

The above restriction is to protect clients and users against malformed or malicious ARC-3.

Any field with a name ending with _mimetype MUST match a corresponding field containing a URI to a file with a matching digest. For example, if the field hello_mimetype is specified, the field hello MUST exist and MUST be a URI pointing to a file with a digest equal to the digest specified by hello_mimetype.

Localization

If the JSON Metadata file contains a localization attribute, its content MAY be used to provide localized values for fields that need it. The localization attribute should be a sub-object with three REQUIRED attributes: uri, default, locales, and one RECOMMENDED attribute: integrity. If the string {locale} exists in any URI, it MUST be replaced with the chosen locale by all client software.

Compared to ERC-1155, the localization attribute contains an additional optional integrity field that specify the digests of the localized JSON files.

It is RECOMMENDED that integrity contains the digests of all the locales but the default one.

Examples

Basic Example

An example of an ARC-3 JSON Metadata file for a song follows. The properties array proposes some SUGGESTED formatting for token-specific display properties and metadata.

{
    "name": "My Song",
    "description": "My first and best song!",
    "image": "https://s3.amazonaws.com/your-bucket/song/cover/mysong.png",
    "image_integrity": "sha256-47DEQpj8HBSa+/TImW+5JCeuQeRkm5NMpJWZG3hSuFU=",
    "image_mimetype": "image/png",
    "external_url": "https://mysongs.com/song/mysong",
    "animation_url": "https://s3.amazonaws.com/your-bucket/song/preview/mysong.ogg",
    "animation_url_integrity": "sha256-LwArA6xMdnFF3bvQjwODpeTG/RVn61weQSuoRyynA1I=",
    "animation_url_mimetype": "audio/ogg",
    "properties": {
        "simple_property": "example value",
        "rich_property": {
            "name": "Name",
            "value": "123",
            "display_value": "123 Example Value",
            "class": "emphasis",
            "css": {
                "color": "#ffffff",
                "font-weight": "bold",
                "text-decoration": "underline"
            }
        },
        "array_property": {
            "name": "Name",
            "value": [1,2,3,4],
            "class": "emphasis"
        }
    }
}

In the example, the image field MAY be the album cover, while the animation_url MAY be the full song or may just be a small preview. In the latter case, the full song MAY be specified by three additional properties inside the properties field:

{
    ...
    "properties": {
        ...
        "file_url": "https://s3.amazonaws.com/your-bucket/song/full/mysong.ogg",
        "file_url_integrity": "sha256-7IGatqxLhUYkruDsEva52Ku43up6774yAmf0k98MXnU=",
        "file_url_mimetype": "audio/ogg"
    }
}

An example of possible ASA parameters would be:

  • Asset Unit: mysong for example
  • Asset Name: My Song
  • Asset URL: https://example.com/mypict#arc3 or https://arweave.net/MAVgEMO3qlqe-qHNVs00qgwwbCb6FY2k15vJP3gBLW4#arc3
  • Metadata Hash: the 32 bytes of the SHA-256 digest of the above JSON file
  • Total Number of Units: 100
  • Number of Digits after the Decimal Point: 2

IPFS urls of the form ipfs://QmWS1VAdMD353A6SDk9wNyvkT14kyCiZrNDYAad4w1tKqT#arc3 may be used too but may cause issue with clients that do not support ARC-3 and that do not handle fragments in IPFS URLs.

Example of alternative versions for Asset Name and Asset URL:

  • Asset Name: My Song@arc3 or arc3
  • Asset URL: ipfs://QmWS1VAdMD353A6SDk9wNyvkT14kyCiZrNDYAad4w1tKqT or https://example.com/mypict or https://arweave.net/MAVgEMO3qlqe-qHNVs00qgwwbCb6FY2k15vJP3gBLW4

These alternative versions are less recommended as they make the asset name harder to read for clients that do not support ARC-3.

The above parameters define a fractional NFT with 100 shares. The JSON Metadata file MAY contain the field decimals: 2:

{
    ...
    "decimals": 2
}
Example with Relative URI and IPFS

When using IPFS, it is convenient to bundle the JSON Metadata file with other files references by the JSON Metadata file. In this case, because of circularity, it is necessary to use relative URI

An example of an ARC-3 JSON Metadata file using IPFS and relative URI is provided below:

{
    "name": "My Song",
    "description": "My first and best song!",
    "image": "mysong.png",
    "image_integrity": "sha256-47DEQpj8HBSa+/TImW+5JCeuQeRkm5NMpJWZG3hSuFU=",
    "image_mimetype": "image/png",
    "external_url": "https://mysongs.com/song/mysong",
    "animation_url": "mysong.ogg",
    "animation_url_integrity": "sha256-LwArA6xMdnFF3bvQjwODpeTG/RVn61weQSuoRyynA1I=",
    "animation_url_mimetype": "audio/ogg"
}

If the Asset URL is ipfs://QmWS1VAdMD353A6SDk9wNyvkT14kyCiZrNDYAad4w1tKqT/metadata.json:

  • the image URI is ipfs://QmWS1VAdMD353A6SDk9wNyvkT14kyCiZrNDYAad4w1tKqT/mysong.png.
  • the animation_url URI is ipfs://QmWS1VAdMD353A6SDk9wNyvkT14kyCiZrNDYAad4w1tKqT/mysong.ogg.
Example with Extra Metadata and {id}

An example of an ARC-3 JSON Metadata file with extra metadata and {id} is provided below.

{
    "name": "My Picture",
    "description": "Lorem ipsum...",
    "image": "https://s3.amazonaws.com/your-bucket/images/{id}.png",
    "image_integrity": "sha256-47DEQpj8HBSa+/TImW+5JCeuQeRkm5NMpJWZG3hSuFU=",
    "image_mimetype": "image/png",
    "external_url": "https://mysongs.com/song/{id}",
    "extra_metadata": "iHcUslDaL/jEM/oTxqEX++4CS8o3+IZp7/V5Rgchqwc="
}

The possible ASA parameters are the same as with the basic example, except for the metadata hash that would be the 32-byte string corresponding to the base64 string xsmZp6lGW9ktTWAt22KautPEqAmiXxow/iIuJlRlHIg=.

For completeness, we provide below a Python program that computes this metadata hash:

import base64
import hashlib

extra_metadata_base64 = "iHcUslDaL/jEM/oTxqEX++4CS8o3+IZp7/V5Rgchqwc="
extra_metadata = base64.b64decode(extra_metadata_base64)
json_metadata = """{
    "name": "My Picture",
    "description": "Lorem ipsum...",
    "image": "https://s3.amazonaws.com/your-bucket/images/{id}.png",
    "image_integrity": "sha256-47DEQpj8HBSa+/TImW+5JCeuQeRkm5NMpJWZG3hSuFU=",
    "image_mimetype": "image/png",
    "external_url": "https://mysongs.com/song/{id}",
    "extra_metadata": "iHcUslDaL/jEM/oTxqEX++4CS8o3+IZp7/V5Rgchqwc="
}"""

h = hashlib.new("sha512_256")
h.update(b"arc0003/amj")
h.update(json_metadata.encode("utf-8"))
json_metadata_hash = h.digest()

h = hashlib.new("sha512_256")
h.update(b"arc0003/am")
h.update(json_metadata_hash)
h.update(extra_metadata)
am = h.digest()

print("Asset metadata in base64: ")
print(base64.b64encode(am).decode("utf-8"))

Localized Example

An example of an ARC-3 JSON Metadata file with localized metadata is presented below.

Base metadata file:

{
    "name": "Advertising Space",
    "description": "Each token represents a unique Ad space in the city.",
    "localization": {
        "uri": "ipfs://QmWS1VAdMD353A6SDk9wNyvkT14kyCiZrNDYAad4w1tKqT/{locale}.json",
        "default": "en",
        "locales": [
            "en",
            "es",
            "fr"
        ],
        "integrity": {
            "es": "sha256-T0UofLOqdamWQDLok4vy/OcetEFzD8dRLig4229138Y=",
            "fr": "sha256-UUM89QQlXRlerdzVfatUzvNrEI/gwsgsN/lGkR13CKw="
        }
    }
}

File es.json:

{
    "name": "Espacio Publicitario",
    "description": "Cada token representa un espacio publicitario único en la ciudad."
}

File fr.json:

{
    "name": "Espace Publicitaire",
    "description": "Chaque jeton représente un espace publicitaire unique dans la ville."
}

Note that if the base metadata file URI (i.e., the Asset URL) is ipfs://QmWS1VAdMD353A6SDk9wNyvkT14kyCiZrNDYAad4w1tKqT/metadata.json, then the uri field inside the localization field may be the relative URI {locale}.json.

Rationale

These conventions are heavily based on Ethereum Improvement Proposal ERC-1155 Metadata URI JSON Schema to facilitate interoperobility.

The main differences are highlighted below:

  • Asset Name and Asset Unit can be optionally specified in the ASA parameters. This is to allow wallets that are not aware of ARC-3 or that are not able to retrieve the JSON file to still display meaningful information.
  • A digest of the JSON Metadata file is included in the ASA parameters to ensure integrity of this file. This is redundant with the URI when IPFS is used. But this is important to ensure the integrity of the JSON file when IPFS is not used.
  • Similarly, the JSON Metadata schema is changed to allow to specify the SHA-256 digests of the localized versions as well as the SHA-256 digests of any file pointed by a URI property.
  • MIME type fields are added to help clients know how to display the files pointed by URI.
  • When extra metadata are provided, the Asset Metadata Hash parameter is computed using SHA-512/256 with prefix for proper domain separation. SHA-512/256 is the hash function used in Algorand in general (see the list of prefixes in https://github.com/algorand/go-algorand/blob/master/protocol/hash.go). Domain separation is especially important in this case to avoid mixing hash of the JSON Metadata file with extra metadata. However, since SHA-512/256 is less common and since not every tool or library allows to compute SHA-512/256, when no extra metadata is specified, SHA-256 is used instead.
  • Support for relative URI is added to allow storing both the JSON Metadata files and the files it refers to in the same IPFS directory.

Valid JSON Metadata files for ERC-1155 are valid JSON Metadata files for ARC-3. However, it is highly recommended that users always include the additional RECOMMENDED fields, such as the integrity fields.

The asset name is either arc3 or suffixed by @arc3 to allow client software to know when an asset follows the conventions.

Security Considerations

Not Applicable

Copyright

Copyright and related rights waived via CCO.