Skip to content
Permalink
master
Switch branches/tags
Go to file
 
 
Cannot retrieve contributors at this time
FATIP Title Status Category Author Created
1 Non-Fungible Token Standard Accepted Token Standard Devon Katz <devonk@dbgrow.com> 2018-07-27

Overview

This document describes the FAT-1 token standard: A non-fungible (NF) token standard. This this standard allows for token systems supporting unique, indivisible tokens issued in whole units. It is most equivalent to Ethereum's ERC-721 token standard.

Motivation

Offer an efficient non-fungible token system, allowing attachment of arbitray metadata to issued tokens. Allow unique, indivisible, and traceable tokenized assets.

Specification

FATIP-1 draws heavily upon the FATIP-0 fungible token standard for it's data structures and signing.

Token Chain

A single Factom chain is used to hold the initialization of the token and all subsequent transaction. The Factom Blockchain provides the underlying consensus mechanism for the content and ordering of transactions. Clients shall apply transactions in the order that they appear in the Factom Token Chain.

Token Chain ID

The Token Chain ID is calculated by using the Token ID and the Issuer's Identity Chain ID as described in FATIP-100.

As the purpose of the first entry in the Token Chain is solely intended to create a chain with the appropriate Name IDs, the content of the first entry in a Token Chain is ignored.

Token Chain Entries

Factom Chains are permissionless, meaning that anyone can pay to submit any entry on any chain. Thus implementations must parse all entries and determine their validity.

Global Requirements

All valid entries must adhere to the following.

On Token Chain

Only entries submitted on the Token Chain may be valid. Thus all entries must be properly submitted and paid for according to Factom's rules. Implementations shall not prematurely apply pending Factom entries to the token state.

Strict JSON Parsing

In order to ensure consistency across implementations, all JSON must be parsed very strictly. In addition to the JSON structure being well-formed with all required fields present, the following are strictly prohibited:

  • unknown fields
  • fields with unexpected types
  • duplicate field names

Note that duplicate field names are not strictly prohibited by the JSON specification. Parsing duplicate field names may differ by implementation. See Implementation Notes for some mechanisms to detect and prohibit duplicate field names if the JSON library does not support their detection natively.

External IDs and Signatures

All valid entries must have External IDs with valid signatures that conform to the specifications defined by FATIP-103. The required set of signatures depends on the entry and is defined below for each entry type.

Non-Fungible Token IDs

Non-fungible tokens issued on FATIP-1 are uniquely identified by an non-negative integer ID. For example, token ID 5.

Token ID Ranges

FATIP-1 has the unique ability to specify sequential ranges of non-fungible token IDs during issuance and transaction. For example, the range {"min": 0, "max":4410} represents the range of token IDs 0 to 4410, inclusive.

Token ID Range Object Validation

  • The range object must contain the keys min and max. No additional keys are allowed.
  • The values of min and max must be non-negative integers
  • min must be less than max

Token ID Range Collections

In transactions, multiple ID ranges and single IDs are allowed for more flexibility. For example [{"min": 0, "max":4410}, 9999] represents the range of token IDs 0 to 4410, inclusive, plus token ID 9999.

Token ID Range Collection Validation

  • The collection may not be empty
  • Each element in the collection must be a valid token ID or ID range object
  • There may be no duplicate single token IDs
    • For example, [0, 0] is invalid
  • There may be no overlap between a range and a single token ID
    • For example, [{"min": 0, "max":5}, 5] is invalid
  • There may be no overlap between ranges
    • For example, [{"min": 0, "max":5}, {"min": 2, "max":6}] is invalid.

Token Initialization Entry

Initialization Entry Content Example

{
  "type": "FAT-1",
  "supply": 10000000,
  "symbol": "ENFT",
  "metadata": {"receiptId": "fv1ykh3e98uc"}
}

Initialization Entry Field Summary & Validation

Name Type Description Validation Required
type string The type of this token issuance Must equal 'FAT-1' Y
supply number The maximum possible number of tokens that can ever be issued Must be greater than 0, or -1 for an unlimited supply. All other values are invalid. Y
symbol string The display symbol of the token Must be A-Z, and 1-4 characters in length N
metadata any Optional metadata defined by the Issuer This may be any valid JSON type. N

Signing

The Initialization Entry must be signed by the Issuer's key established by the Identity Chain referenced in the Token Chain's Name IDs. Thus the Identity Chain must exist and be properly set up with a key at the time that the Initialization Entry is submitted. See FATIP-101 for details on key selection from the Identity Chain and FATIP-103 for details on signing and External IDs.

Token Transaction Entry

Transaction ID

The unique ID of a transaction is its Factom entry hash. The Transaction ID is a hash of all of the entry data including the content and the External IDs. Entries possessing the same Transaction IDs are duplicates and so are ignored to prevent replay attacks.

Transaction Entry Content Example

{
   "inputs:": {
      "FA1zT4aFpEvcnPqPCigB3fvGu4Q4mTXY22iiuV69DqE1pNhcaLYM": [0, {"min": 10, "max":4410}],
      "FA2y6VYYPR9Y9Vyy1ZuZqWWRXGXLeuvsLWGkDxq3Ed7yc11dbBKV": [4411]
   },
   "outputs:": {
      "FA3aECpw3gEZ7CMQvRNxEtKBGKAos3922oqYLcHQ9Nqw9e8f3LIO": [0,{"min": 10, "max":4410}, 4411]
   },
   "metadata": {"memo": "thanks for the unique tokens!"},
    "tokenmetadata": [
        {
        "ids":[0, 11],
        "metadata": "These are the good ones ;)"
    	}
    ]
}

Transaction Entry JSON Field Summary & Validation

Name Type Description Validation Required
inputs object The inputs of the transaction Mapping of Public Factoid Address => Token ID Range Collection. Range collection must be valid. May not be empty or contain duplicate Addresses. Y
outputs object The outputs of the transaction Mapping of Public Factoid Address => Token ID Range Collection. Token ID Range Collection must be valid. May not be empty or contain duplicate Addresses or any Addresses used in the inputs. All tokens listed in inputs must occur in outputs Y
metadata any Optional metadata defined by user This may be any valid JSON type. N
tokenmetadata array Optional metadata linked to the tokens being dispersed May only be included for a coinbase transaction (issuance). N
tokenmetadata[*] object An object containing the metadata for a collection of IDs or ID ranges Must contain at least one element if tokenmetadata is specified. Y*
tokenmetadata[*].ids array A valid token ID range collection Must be a valid token ID range collection. Required if tokenmetadata[*] is specified. Y*
tokenmetadata[*].metadata any The metadata to associate with the IDs specified in ids This may be any valid JSON type. Required if tokenmetadata[*] is specified. Y*

* = conditional on an optional field, see validation column for criteria

For a Transaction to be well-formed it must follow the above defined structure with all required fields. Additionally, the token amounts must be conserved. In other words the sum of the inputs must be exactly equal to the sum of the outputs. Finally duplicate addresses within a Transaction are prohibited. This includes duplicate addresses within the inputs or outputs as well as duplicate addresses between the inputs or outputs. Thus tokens may not be sent from an address to itself within the same transaction. This reduces the complexity of implementations.

Note: It is not required for token ID ranges to be issued continuously, or starting from 0

Signing Set

Transactions are signed according to FATIP-103. The signing set is the set of keys corresponding to the addresses in the inputs. A transaction must include an RCD/Signature pair for each input in the transaction. The order of the addresses in the inputs field does not matter, so the RCD/Signature pairs may appear in the External IDs in any order, so long as the signatures are properly salted with their External ID position, as specified in FATIP-103.

Addresses

FAT-0 uses Factom's Factoid Address RCD key pairs based on ed25519 cryptography to send and receive tokens.

Reserved Coinbase & Burn Address

The public Factoid Address FA1zT4aFpEvcnPqPCigB3fvGu4Q4mTXY22iiuV69DqE1pNhdF2MC, corresponding to the Factoid private key with all zeros (Fs1KWJrpLdfucvmYwN2nWrwepLn8ercpMbzXshd1g8zyhKXLVLWj) is a reserved address used for two purposes: issuing and burning tokens.

Burning

Any tokens sent to the Coinbase address are forever burned and irrecoverable, even by the Issuer of the token.

Issuance

Only Coinbase Transactions may use the Coinbase address as an input. Coinbase Transactions issue and distribute new tokens to other addresses. To clarify, tokens sent from this address are not previously burned tokens, but new tokens adding to the circulating supply.

Coinbase Transactions

Coinbase Transactions are special Transaction Entries generated by the Token Issuer that issue new tokens to addresses. Coinbase Transactions may not increase the total supply (circulating and burned) beyond the maximum supply declared in the Initialization Entry.

Coinbase Transactions follow the same structure as normal transactions described above, but with the Coinbase Address as the sole input in the inputs object. Coinbase Transactions may not include any other address in the inputs object. Just as with normal transactions, the input addresses and output addresses may not intersect. Thus a Coinbase Transaction may not directly burn the tokens it issues.

Coinbase Signing Set

A signature from the current key establised by the Issuer's Identity key is required. See FATIP-101 and FATIP-103.

Transaction Validation Requirements

All Transactions must meet all of the T.x requirements.

Normal Transactions must additionally meet all of the N.x requirements.

Coinbase Transactions must additionally meet all of the C.x requirements.

The x.1.x requirements are generally data structure validations.

The x.2.x requirements are generally parsing and other content validations.

The x.3.x requirements are generally related to cryptographic validations.

T.x Requirements for all transactions

  • T.1.1: The content of the entry must be a single well-formed JSON.
  • T.1.2: The JSON must contain all required fields listed in the above table, all fields and their members must be of the correct type. No unspecified fields may be present. No duplicate field names are allowed.
  • T.1.3: The same Factoid Address may not occur in both inputs and outputs. Neither inputs or outputs may have any duplicate keys.
  • T.2.1: All token ID's represented in the inputs token ID collection must appear in the outputs token ID collection.
  • T.2.2: The entry hash of the transaction entry must be unique among all previously valid transactions belonging to this token.
  • T.3.1: The External IDs must follow the cryptographic and structural requirements defined by FATIP-103.

N.x Requirements for normal account-to-account transactions

Normal transactions must meet all T.x and N.x requirements.

  • N.2.1: The Coinbase address may not be an input.
  • N.2.2: The balances of each input address specified in inputs must own its respective token IDs or token ID ranges being transacted.
  • N.3.1: For each input address, there exists a corresponding valid RCD/Signature pair in the External IDs as specified by FATIP-103. No additional RCD/Signature pairs beyond those that correspond with an input may be included.

C.x Requirements for Coinbase distribution transactions

Coinbase transactions must meet all T.x and C.x requirements.

  • C.1.1: The Coinbase address must be the only input.
  • C.2.1: The quantity of tokens being issued plus the quantities of tokens issued in all previous coinbase transactions must be less than or equal to supply from the Issuance entry (if not unlimited). Ranges are considered in expanded form (For example {"min": 0,"max": 2} is considered 3 tokens in the supply).
  • C.2.2: No token ID's contained in inputs may already be in circulation.
  • C.3.1: The entry must be signed by the Issuer's currently established key in the Identity chain according to FATIP-101 and FATIP-103.

Computing the Current State

Implementations must maintain the state of the balances of all addresses in order to evaluate the validity of a transaction. The current state can be built by iterating through all entries in the token chain in chronological order and updating the state for any valid transaction.

The following pseudo code describes how to compute the current state of all balances. A transaction must be applied entirely or not at all. Entries that are not valid transactions are simply ignored. Transactions must be evaluated in the order that they appear in the token chain. This assumes the token has already been properly initialized.

for entry in token_chain.entries:
    if entry.is_valid_transaction():
        if !entry.is_coinbase_transaction():
            for input in entry.inputs:
                balances[input.address].concat(input.ids)
        for output in entry.outputs:
            balances[output.address].remove(output.ids)

Implementation

  • FAT Daemon & CLI (fatd) - Official FAT token daemon, API server, and CLI implementation
    • A program written in Go that tracks and validates FAT Token chains, and provides up an API for other applications to access FAT's data.
  • fat-js - Official FAT Javascript library for NodeJS & Browser
  • FAT Wallet - Official FAT Wallet UI

Duplicate JSON field names

Most JSON libraries do not consider duplicate field names to be an error. Thus they can go undetected if not mitigated through other detection mechanisms. One simple way to detect duplicate field names is to compare the length of the minified JSON with the expected length of the JSON after parsing all of the fields. The expected length is easy to determine once all of the fields are parsed. See the fatd reference implementation of fat0.Transaction or fat0.Initialization for examples of this.

A less efficient way to detect this is to parse the JSON, then regenerate the JSON and compare the lengths of the JSON with the minified original.

In both cases it is necessary to minify the JSON since users may add any valid whitespace to their JSON.

Large Issuances & Realistic "Infinite" Supply

In the official fatd implementation, large scale token issuances can cause the daemon to hang for several seconds to process the first time they are encountered(one time only, first sync). It is currently recommended to split up large issuances into chunks spread out over many blocks, if possible, to improve user experience of syncing a token for the first time. The recommended issuance limit is approximately 10K tokens per block per initialized token.

While this standard specifies an infinite supply option, the current official implementation is software-limited by the absolute sqlite row limit of 2^64 rows per initialized token, as one row is required per issued non-fungible token.

Copyright

Copyright and related rights waived via CC0.