Conventions Fungible/Non-Fungible Tokens | Algorand Developer Portal

Conventions Fungible/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:

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

    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](https://doi.org/10.6028/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”).

- 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](https://doi.org/10.6028/NIST.FIPS.180-4))

There are no requirements regarding the manager account of the ASA, or its 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](https://eips.ethereum.org/EIPS/eip-1155) 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](https://docs.opensea.io/docs/metadata-standards).

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:

```json
{

"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",

},

"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. Concretely, 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:

{

"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:

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:

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

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:

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.