diff --git a/README.md b/README.md index ef7980e2..18d8f552 100644 --- a/README.md +++ b/README.md @@ -24,6 +24,8 @@ implemented or considered for implementation. ## [Serialization formats](./serializations.md) +## [Native tokens](./tokens/tokens.md) + ## Aeternity node The Aeternity node is the reference implementation of the Aeternity protocol. Since we don't diff --git a/node/api/api_encoding.md b/node/api/api_encoding.md index 54aef7e2..f37f29b6 100644 --- a/node/api/api_encoding.md +++ b/node/api/api_encoding.md @@ -43,6 +43,7 @@ tag. | kh | base58 | Key block hash | | mh | base58 | Micro block hash | | nm | base58 | Name | +| nt | base58 | Aeternity Native Token (ANT) | | ok | base58 | Oracle pubkey | | oq | base58 | Oracle query id | | or | base64 | Oracle response | diff --git a/serializations.md b/serializations.md index dec37773..76a04c8a 100644 --- a/serializations.md +++ b/serializations.md @@ -141,6 +141,7 @@ ambiguity. | 4 | oracle | | 5 | contract | | 6 | channel | +| 7 | token | In Erlang notation, the `id()` type pattern is: ``` @@ -252,6 +253,12 @@ subsequent sections divided by object. | Sophia byte code | 70 | | Generalized accounts attach transaction | 80 | | Generalized accounts meta transaction | 81 | +| Aeternity Native Token (ANT) | 90 | +| ANT create transaction | 91 | +| ANT mint transaction | 92 | +| ANT finalize transaction | 93 | +| Token trade transaction | 94 | +| Token burn transaction | 95 | | Key block | 100 | | Micro block | 101 | | Light micro block | 102 | @@ -282,6 +289,24 @@ subsequent sections divided by object. ] ``` +#### Accounts (version 4, Normal accounts with flags and tokens, from XXX release) +``` +[ :: int() +, :: int() +, :: int() +, :: [{id(),int()}] +``` + +#### Accounts (version 5, Generalized accounts with tokens, from XXX release) +``` +[ :: int() +, :: int() +, :: int() +, :: id() +, :: binary() +, :: [{id(),int()}] +``` + ### Signed transaction ``` [ :: [binary()] @@ -309,6 +334,28 @@ The recipient must be one of the following: * A contract identifier. * A name identifier, whose related name entry has an identifier as value of pointer with key `account_pubkey`. If multiple pointer entries are present for such key, then the first of such entries is used. +` + +### Spend token transaction From the XXX release +``` +[ :: id() +, :: id() +, :: id() +, :: int() +, :: int() +, :: int() +, :: int() +, :: binary() +] +``` + +The recipient must be one of the following: +* An account identifier. +* An oracle identifier. +* A contract identifier. +* A name identifier, whose related name entry has an identifier as value of pointer with key `account_pubkey`. + If multiple pointer entries are present for such key, then the first of such entries is used. +` #### Oracles ``` @@ -1013,3 +1060,74 @@ NOTE: , :: binary() ] ``` + +#### Aeternity Native Token (ANT) +``` +{ creator :: id() +, meta_data :: binary() +, contract :: id() +, total_supply :: int() +, parent :: id() TODO: Decide if we should have hierarchical tokens +, final :: bool() +} +``` + + +#### ANT create transaction +``` +[ :: id() +, :: binary() +, :: id() +, :: int() +, :: id() +, :: id() TODO: Decide on hierarchical tokens +, :: bool() +, :: int() +, :: int() +, :: int() +] +``` + +#### ANT mint transaction +``` +[ :: id() +, :: id() +, :: int() +, :: id() +, :: bool() +, :: int() +, :: int() +, :: int() +] +``` + +#### ANT finalize transaction +``` +[ :: id() +, :: id() +, :: int() +, :: int() +, :: int() +] +``` + +#### Token trade transaction + +``` +[ :: [{ :: id(), :: id(), :: int(), :: id()}] +, :: int() +, :: int() +, :: int() +] +``` + +#### Token burn transaction +``` +[ :: id() +, :: int() +, :: int() +, :: int() +, :: int() +, :: int() +] +``` diff --git a/tokens/tokens.md b/tokens/tokens.md new file mode 100644 index 00000000..ef6a5a7c --- /dev/null +++ b/tokens/tokens.md @@ -0,0 +1,207 @@ +# Aeternity Native Tokens + +## Overview + +We need to distinguish between a specification of a token, and a +countable amount of the token. In this document we use **ANT** +(Aeternity Native Token) to represent the specification, and **native +token** or simply **token** to represent the countable asset. + +Informally, when you need a new type of token for whatever purpose, +you create an ANT describing the token, and then you can mint new +tokens and start sending them to accounts. + +Owners of the tokens can spend them to other accounts, or trade them +(atomically) for other assets with other accounts. If tokens are no +longer wanted you can burn them. + +Only the owner of the ANT can mint new tokens. Only the owner can +finalize the ANT, preventing more minting. An ANT can also be +created with a final supply already minted. + +An ANT can only be destroyed (TODO: design decision) if the total +token supply of the ANT is zero (i.e., all tokens are burnt). + +Aeons are not part of the ANT system. There are no consensus +controlled exchange rate between tokens and aeons. Fees cannot be +payed by native tokens, but only by aeons. Other services could be +payed for with tokens (e.g., contracts accepting tokens as +payment). (TODO: Design decision: Should oracles be allowed to set +fees in tokens?) + +The owner of a token can decide to govern the behavior of the tokens +by connecting a contract when an ANT is created. The contract must +provide entrypoints according to a specified ACI where one or many of +the primitive operation (spend, trade, mint, etc) is called to decide +if the operation is allowed or not. + +## Definitions + +Note that when *account* is used below, the account can belong to a +contract, the account may be an oracle, etc. + +An Aeternity Native Token (ANT): +- is a *specification* of tokens +- is a *first class object* on the chain with its own state tree +- is *created* by an account which becomes its owner +- *governs the behavior* when interacting with tokens (trading, sending, minting, etc). +- can have a *connected contract* to govern the handling of its tokens + +A native token: +- has an ANT describing its behavior. +- is *countable* +- is recorded as a *balance in the account* that owns it (in the accounts state tree). +- is *mintable* (`mint`) +- is *transferable* (`spend`, `trade`) +- is *destroyable* (`burn`) + +## Tokens state tree + +The tokens state tree contains ANT objects. Tokens are stored in the +accounts state tree in the owner's account. + +### ANT object + +The token id is determined by the creator's account and nonce. +``` + id := Blake2b() +``` +where `Blake2b` is the 256 bit hash. The [API +serialization](../node/api/api_encoding.md) of an ANT id is tagged by +`nt_` + +The ANT contains the fields: +- `creator` - The account id of the creator +- `meta_data` - A byte array field, uninterpreted, but under consensus +- `contract` - A contract id if there is a governing contract, or the empty binary otherwise +- `total_supply` - A counter of the currently available amount of the token +- `parent` - The id of the parent ANT (TODO: Hierarchical tokens?) +- `final` - If the ANT is final, new tokens cannot be minted. Can flip to true, but never back to false again. + +The `meta data` is an uninterpreted string but token minters are +encouraged to use the following json object: (TODO: Extend). The +intention of the meta data is to have a consensus controlled way of +providing information usable by tools (e.g., displaying an intended +denominator, a display name, etc). +``` +{ + "type" : "object", + "properties": { "name": {"type" : "string"}, + } +} +``` + +A contract governing the usage of tokens must have a non-empty subset +of the following ACI: + +``` + spend(recipient : address, payload : Type) : boolean + trade([(from : address, to: address, Option(token : address))], payload : Type) : boolean + mint(amount: integer) : boolean + burn(amount : integer) : boolean +``` + +The contract may contain other endpoints as well, but at least one of +the entrypoints above must be implemented. The contract can only be +attached at create time. + +If a contract is provided, any transaction (spend, trade, mint, burn) +would call this contract and the transaction only goes through if the +result of the corresponding contract call returns true. Any other +transaction using the token (such as contract call) would only be +executed if a call to spend returns true. (TODO: Decide how this plays +with contract calls that tries to pass tokens as value, etc). + +Note that the `final` field in the ANT takes precedence over the +governing contract. If the ANT is final, no minting can occur. (TODO: +Perhaps the contract should have a `finalize` endpoint as well?) + +## ANT transactions + +### ANT create transaction (`ant_create_tx`) + +The ANT create transaction takes the argument: +- Meta data : string +- Amount +- Recipient +- Final +- Contract +- Parent (TODO: Hierarchical tokens?) + +The `amount` is the number of tokens to mint at create time. Set to +`0` if none should be minted. This can for example be combined with +setting the `final` argument to true, thereby immediately minting all +tokens that will ever exist. + +The `recipient` is the recipient of the minted tokens in `amount`. If +not provided, the tokens are given to the creator's account. + +The `parent` is a pointer to a parent token for hierarchical +tokens. (TODO: hierarchical tokens?) + +### ANT finalize transaction (`ant_finalize_tx`) + +The ANT finalize transaction takes the arguments: +- owner +- ANT + +The finalize transaction can only be submitted by the actual owner, +and only if the ANT is not already finalized. + +### ANT mint transaction (`ant_mint_tx`) +The ANT mint transaction takes the argument: +- owner +- ANT +- amount +- recipient +- final + +Only the `owner` can mint new tokens, but it can pass the minted +tokens to a recipient. If `final` is set to true, the ANT will be +finalized after the new tokens are minted. + +### ANT destroy transaction +TODO: Should we be able to destroy an ANT that has a `total_supply` of 0? + + +## Token transactions + +### Token trade transaction +The token trade transaction takes the argument: +- trades + +The `trades` field contans a non-empty list of token transfers. A +single token transfer consists of the fields: + +- Sender +- Receiver +- Amount +- An ANT id + +This construct makes it possible to atomically perform complicated +trade operations involving more than one ANT and also aeons (TODO: How +to signal that a trade concerns aeons) between multiple parties. + +The transaction must be signed by all senders. (TODO: Should we use +some alternative multisig format here?). + + +### Token burn transaction + +The token burn transaction contains the fields +- Account (owner of the tokens, not necessarily of the ANT) +- Amount +- ANT + +Destroy an `Amount` of `ANT` tokens currently owned by the +`account`. The burned amount is also counted from the `total_supply` +in the ANT object. + +### Other transactions on tokens + +Tokens can also be transfered from one account to another through any +other transaction that can pass an amount. You can: +- spend tokens through the `spend_tx` +- pass tokens as value in a `contract_call_tx` or `contract_create_tx` +- pass tokens as value in contract calls in a smart contract. +- spend tokens as query fees in `oracle_query_tx` (TODO: Might be a future extension).