ARCs:specs:arc-0003.md•25.9 kB
---
arc: 3
title: Conventions Fungible/Non-Fungible Tokens
description: Parameters Conventions for Algorand Standard Assets (ASAs) for fungible tokens and non-fungible tokens (NFTs).
author: Fabrice Benhamouda (@fabrice102)
discussions-to: https://github.com/algorandfoundation/ARCs/issues/3
status: Final
type: Standards Track
category: ARC
sub-category: Asa
created: 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 <a href="https://www.ietf.org/rfc/rfc2119.txt">RFC-2119</a>.
> Comments like this are non-normative.
An [ARC-3](./arc-0003.md) 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 <a href="https://www.ietf.org/rfc/rfc3986.txt">RFC-3986</a> 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 compatibility 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:
```plain
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 <a href="https://doi.org/10.6028/NIST.FIPS.180-4">NIST FIPS 180-4</a>.
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 <a href="https://doi.org/10.6028/NIST.FIPS.180-4">NIST FIPS 180-4</a>)
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 <a href="https://eips.ethereum.org/EIPS/eip-1155"> ERC-1155 Metadata URI JSON Schema</a> 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 <a href="https://docs.opensea.io/docs/metadata-standards">OpenSea metadata format</a>.
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",
"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 <a href="https://w3c.github.io/webappsec-subresource-integrity">W3C subresource integrity specification</a>.
Details on how to generate those digests can be found on the <a href="https://developer.mozilla.org/en-US/docs/Web/Security/Subresource_Integrity">MDN Web Docs</a> (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.
```json
{
"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:
```json
{
...
"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`:
```json
{
...
"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:
```json
{
"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.
```json
{
"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:
```python
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:
```json
{
"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`:
```json
{
"name": "Espacio Publicitario",
"description": "Cada token representa un espacio publicitario único en la ciudad."
}
```
File `fr.json`:
```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 <a href="https://eips.ethereum.org/EIPS/eip-1155"> ERC-1155 Metadata URI JSON Schema</a> 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 <a href="https://creativecommons.org/publicdomain/zero/1.0/">CCO</a>.