FATIP | Title | Status | Category | Author | Created |
---|---|---|---|---|---|
0 | Fungible Token Standard | Draft | Token Standard | Devon Katz <devonk@dbgrow.com> | 7-23-2018 |
This document describes the functionality, datastructures, and validation rules of the FAT-0 token standard. FAT-0 is a data-only, fungible token standard built on Factom chains and entries. Its functionality is most alike to the ERC-20 token standard of Ethereum.
Create a decentralized, open token standard on top of the Factom protocol. The FAT-0 standard allows issuance and trade tokenized assets between Factoid addresses.
Following the Factom ethos, FAT standards are data-only protocols, and can be implemented on top of any system that supports immutable ordered data, such as Factom.
FAT-0 fills a critical gap between Colored Coins (BTC) and ERC (Ethereum) tokens. Implementation of the FAT-0 standard on Factom results in an immutable, fixed cost token system that is inexpensive to build, issue, and transfer tokens on:
- $0.012 USD to issue a new FAT-0 token
- Additional $0.001 USD/KB of optional user defined content up to 10KB
- $0.001 USD to transact an unlimited number of tokens
- Additional $0.001 USD/KB of optional user defined content up to 10KB
Because the standard supports arbitrary user defined data, it offers a base for more complex user defined behavior based on the underlying data. Custom token systems may be built on top of the basic functionality of this standard.
Applications include ICOs, digital currencies, digital goods, and accounting.
A human readable string unique to the Issuer's Identity Chain ID. This ID and
the Issuer's Identity Chain ID are the only pieces of data needed to retrieve
the token's data from Factom. It should be something short and memorable
related to your token. For example: mytoken
is the token ID of the FAT-0 test
token: My Token (MYT).
TODO: Add example issuer id for this example token.
Note that two Issuers could use the same Token ID since the Issuers will have different Identity Chain IDs. It is up to users to decide which Issuer's token they wish to use. Trust in an Issuer is necessary to trust the integrity of future token distribution decisions.
FAT-0 uses Factom's Factoid Address key pairs based on ed25519 cryptography to send and receive tokens.
The public Factoid Address FA1zT4aFpEvcnPqPCigB3fvGu4Q4mTXY22iiuV69DqE1pNhdF2MC
,
representing the public address corresponding to the Factoid private key with all zeros (Fs1KWJrpLdfucvmYwN2nWrwepLn8ercpMbzXshd1g8zyhKXLVLWj
) is a reserved address for minting and
burning tokens.
Only coinbase transactions are allowed from this address, which must be signed by the identity of the token issuer. Tokens sent to this address are provably un-spendable (burnt). This address is not allowed to send tokens from its balance under any circumstance.
A Factom chain is used to hold the token issuance and transactions of the token. The Factom Blockchain provides a fixed ordering of transactions and issuances, a requirement for determining consensus around the state of balances and transaction validation.
Token Chain ID extends
FATIP-100
The Token Chain ID can be calculated by using the Issuer's Identity Chain ID and the Token ID. The token chain has the following External IDs.
External ID Index | External ID Value |
---|---|
0 | "token" |
1 | <Hex Encoded Token ID> |
2 | "issuer" |
3 | <Hex Encoded Issuer Identity Root Chain ID> |
For example, for FAT-0 token mytoken
:
Chain ID = sha256(sha256("token") | sha256("mytoken") | sha256("issuer") | sha256("888888dd9e60c1f0216f753caf5c9b5be4c9ca69db27a6c33d30dce3fe5ee709"))
Chain ID = 0x85ab32f9748bf4c24cedb301bc5a4938eb5cd4a5b0bb46b70027bf983f579a2e
Where sha256d is a double round of sha256 hashing: sha256d(x) = sha256(sha256(x))
Token Issuance Entry extends
FATIP-101
A data structure called the Token Issuance holds the information about the FAT-0 token such as it's name, symbol, and supply.
The official issuance entry is the first well-formed entry on the token's token chain that has a valid signature from the Issuer's SK1 key identified by the Issuer's Identity Chain.
There may only be one issuance entry per FAT-0 token. It should be noted that due to the implementation of FATIP-100 in this spec, the signed issuance entry cannot be the first entry in the token chain, since it's ExtIDS must be exact. The issuance entry may occur at any position in the chain. Invalid entries are simply ignored.
From FATIP-101, the data
being signed shall be the Chain ID of the token chain concatenated with the entry content. The issuer
shall be the hex encoded Issuer Identity Root Chain ID used in chain ID derivation, and the IDKey
used shall be the IDKey of issuer
at the time of entry to Factom.
{
"type": "FAT-0",
"supply": 10000000,
"name": "Example Token",
"symbol": "EXT",
"salt": "874220a808090fb736f345dd5d67ac26eab94c9c9f51b708b05cdc4d42f65aae",
}
Name | Type | Description | Validation | Required |
---|---|---|---|---|
type | string | The type of this token issuance | Must equal 'FAT-0' | Y |
supply | number | The maximum possible number of tokens that can be in circulation | Must be greater than 0 if included. Omission means supply is unlimited | N |
name | string | The display name of the token | none | N |
symbol | string | The display symbol of the token | must be A-Z, and 1-4 characters in length | N |
salt | string | Random string to salt the issuance | user defined. Optional | N |
External ID Index | Type | Description | Validation | Required |
---|---|---|---|---|
0 | raw binary data | Raw ed25519 signature output of signing issuance chainID + entry content. |
ed25519 signature validation (chainID + entry content, idKey , ExtID[0] ) |
Y |
Token Transaction Entry extends
FATIP-101
Token Transaction Entries represent the signed transfer of an amount of FAT-0 tokens from one Factoid address to another. Anyone who wishes to make a transaction must create a well-formed entry with a valid signature on the token's chain.
The order of transactions is solely determined by the order the transaction entries appear in the token chain. Factom provides a locked, time ordered record of submission for each transaction. The presense of a Transaction Entry in the chain is necessary but not sufficient for the transaction to be considered valid. Sufficient balance, valid signatures, as well as some other requirements to prevent replay attacks are described below.
FAT-0 transactions borrow cryptographic elements from Factoid Transactions, including it's RCD (Redeem Condition Datastructure) address format, and signatures. FAT-0 transactions are multi-input, multi-output (MIMO).
For standard transactions, RCDs and corresponding signatures are provided in the External IDs of the Transaction Entry. For coinbase transactions that initially distribute tokens, the signatures are provided in the structure of the entry as described by FATIP-101.
The unique ID of a transaction is its entry hash in the Factom blockchain. This is a unique hash of all of the data in the content and the External IDs of the entry.
{
"inputs:":{
"FA1zT4aFpEvcnPqPCigB3fvGu4Q4mTXY22iiuV69DqE1pNhdF2MC": 100,
"FA2y6VYYPR9Y9Vyy1ZuZqWWRXGXLeuvsLWGkDxq3Ed7yc11dbBKV": 50
},
"outputs:":{
"FA3aECpw3gEZ7CMQvRNxEtKBGKAos3922oqYLcHQ9NqXHudC6YBM": 150
},
"salt":"80d87a8bd5cf2a3eca9037c2229f3701eed29360caa975531ef5fe476b1b70b5"
}
Name | Type | Description | Validation | Required |
---|---|---|---|---|
inputs |
object | The inputs of this this transaction | Mapping of Public Factoid Address => Input amount. Amount must be a positive nonzero integer (units of of 1^-10 of a token). Must contain at least 1 key-value pair. No duplicate keys. | Y |
outputs |
object | The outputs of this transaction | Mapping of Public Factoid Address => Input amount. Amount must be a positive nonzero integer (units of of 1^-10 of a token). Must contain at least 1 key-value pair. No duplicate keys. | Y |
salt |
string | Random string to salt the transaction | Must be at least 1 character long | Y |
Transaction Entries must include exactly two External IDs for every input address listed in the transaction: the RCD that hashes to the corresponding input address, and a signature.
In the case of normal transaction, all inputs must have a corresponding RCD in an even External ID.
In the case of coinbase transactions, the RCD in the 0th External ID must hash to the ID Key of the issuing Identity as per FATIP-101.
Since the Transaction Entry Hash MUST be unique, implementations must ignore any entries that do not contain EXACTLY twice as many External IDs as inputs. This prevents replay of transactions through manipulating the Transaction Entry Hash by appending additional External IDs to a previously posted valid transaction.
In the following table X
is any even number less than 2n
.
External ID Index | External ID Value (RAW DATA) |
---|---|
0 | RCD corresponding to one of the inputs or Issuer ID Key |
1 | ed25519 signature by key of RCD in preceding External ID |
2 | RCD corresponding to one of the inputs |
3 | ed25519 signature by key of RCD in preceding External ID |
... | |
X |
RCD corresponding to one of the inputs |
X + 1 |
ed25519 signature by key of RCD in preceding External ID |
All of the RCD and Signature External IDs in a Transaction Entry shall be raw data and not use any encoding. The Factom Explorer now properly displays External IDs that contain raw data as hex encoded data. So there is little reason to encode the data we put in the External IDs.
The RCD is a data structure that hashes to the payload of a Factoid Address.
The RCD data structure is 33 bytes long. The first byte is the RCD type
(0x01
), the remaining 32 bytes are a ed25519 public key.
Factoid Addresses contain a header and a checksum, which is described futher in the Factom Data Structures Documentation.
To validate that an RCD "corresponds" to a Factoid Address,
- Convert the Factoid Address string from base58 to raw bytes.
- Remove the first 2 bytes header bytes and the last four bytes checksum bytes.
- The remaining 32 bytes must be the double SHA256 hash of the raw bytes of the RCD.
The cryptographic signature algorithm is the same as that used in a Factoid Transaction.
From the Factom Documentation:
- Factoids use Ed25519 with Schnorr signatures. They have many benefits over the ECDSA signatures used in Bitcoin.
- The signatures are enforced to be canonical. This will limit malleability by attackers without the private key.
A signed transaction should only be valid once, so signed data must be protected against replay within a given FAT chain and across various FAT chains. For this reason both the index of the External ID and the chain id is used to salt the signature. This prevents reordering the External IDs and replaying a transaction on a different chain.
Let i
be the integer index of the External ID that the signature will be
place in the Factom Entry.
The signed data shall be the concatenation of the following:
- The shortest (no leading zeros) string decimal representation of
int(i/2)
- The raw bytes of the chain ID
- The content of the entry
For example, if there were two inputs, and +
is concatenation of bytes, then
the valid External IDs would be,
ExtID[0] = 0x01 + pub_key0
ExtID[1] = ed25519_sign(priv_key0, "0" + chain_id + entry_content)
ExtID[2] = 0x01 + pub_key1
ExtID[3] = ed25519_sign(priv_key1, "1" + chain_id + entry_content)
There are two types of transactions which have slightly different validation requirements: Normal transactions between Factoid Addresses, and Coinbase transactions that mint tokens. Coinbase transactions require an additional signature of the entry content by the issuer, stored in the transaction's ExtIds.
A Coinbase transaction is identified by whether it has an input from the
Coinbase address: FA1zT4aFpEvcnPqPCigB3fvGu4Q4mTXY22iiuV69DqE1pNhdF2MC
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 and sequentially, updating the state for any transaction that meets all of the necessary Transaction Validation Requirements.
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.
for entry in transaction_chain.entries:
if entry.is_valid_transaction():
if !entry.is_coinbase_transaction():
for input in entry.inputs:
balances[input.address] -= input.amount
for output in entry.outputs:
balances[output.address] += output.amount
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.
In general, requirements are ordered by the computational and programmatic ease of checking.
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.1.1: The content of the entry must be a single well-formed JSON.
- T.1.2: The JSON must contain all required transaction fields listed in the above table. The fields must comply with all stated validation criteria. No unspecified fields may be present.
- T.1.3: The entry MUST contain exactly
2n
External IDs, wheren
is the number of keys in theinputs
object. This is required to prevent replay attacks. - T.1.4: A Factoid Address may only appear ONCE in the
inputs
andoutputs
objects combined. This means that the "inputs" and "outputs" may not share any common keys (Factoid Addresses), nor each have any duplicates. - T.2.1: The sum of all values(amounts) in the
inputs
object must be equal to the sum of all values(amounts) in theoutputs
object. - T.2.2: The entry hash of the transaction entry must be unique among all transactions belonging to this token.
- T.3.1: If
i
is odd then thei
th External ID must be the raw data of a valid signature verified against the public key stored in the last 32 bytes of the RCD in thei-1
th External ID. The signed message shall be the following three things concatenated in order:- The shortest (no leading "0") decimal string representation of int(i/2)
- The raw bytes of the chain id
- The raw bytes of the content of the entry
The first prevents replay attacks from reordering External IDs, and the second prevents replay attacks across FAT token chains.
Normal transactions must meet all T.x and N.x requirements.
All references to External ID indexes use 0
-based indexing.
- N.2.1: The Coinbase address
(
FA1zT4aFpEvcnPqPCigB3fvGu4Q4mTXY22iiuV69DqE1pNhdF2MC
) may not exist as a key ininputs
. - N.2.2: The Factoid Addresses (keys) in
inputs
must all have balances greater than or equal to their respective input amounts. - N.3.1: For each input address, there exists an External ID with even index which is the raw data of an RCD which hashes to the raw data payload of the human readable Factoid Address input.
Coinbase transactions must meet all T.x and C.x requirements.
- C.1.1: The
inputs
object may contain only the public coinbase address as a key (FA1zT4aFpEvcnPqPCigB3fvGu4Q4mTXY22iiuV69DqE1pNhdF2MC
) - C.2.1: The coinbase input amount plus the sum of tokens issued in all
previous Coinbase transactions must be less than or equal to
supply
from the Issuance entry (if not unlimited). - C.3.1: The RCD in the first External ID must hash to the PK1 key of the Token Issuer's Identity Chain.
No implementation notes at this time.
Copyright and related rights waived via CC0.