# 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](https://www.ietf.org/rfc/rfc2119.txt).

> Comments like this are non-normative.

An [ARC-3](https://dev.algorand.co/arc-standards/arc-0003) 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 apply after such a replacement.
    - **MUST** follow [RFC-3986](https://www.ietf.org/rfc/rfc3986.txt) 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_

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

```
    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](https://w3c.github.io/webappsec-subresource-integrity).
Details on how to generate those digests can be found on the [MDN Web Docs](https://developer.mozilla.org/en-US/docs/Web/Security/Subresource_Integrity) (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:

```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

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`.
