Skip to content
Draft
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
90 changes: 90 additions & 0 deletions serializations.md
Original file line number Diff line number Diff line change
Expand Up @@ -141,6 +141,7 @@ ambiguity.
| 4 | oracle |
| 5 | contract |
| 6 | channel |
| 7 | token |

In Erlang notation, the `id()` type pattern is:
```
Expand Down Expand Up @@ -252,6 +253,12 @@ subsequent sections divided by object.
| Sophia byte code | 70 |
| Generalized accounts attach transaction | 80 |
| Generalized accounts meta transaction | 81 |
| Token | 90 |
| Token create transaction | 91 |
| Token mint transaction | 92 |
| Token trade transaction | 93 |
| Token burn transaction | 94 |
| Token finalize transaction | 95 |
| Key block | 100 |
| Micro block | 101 |
| Light micro block | 102 |
Expand Down Expand Up @@ -282,6 +289,14 @@ subsequent sections divided by object.
]
```

#### Accounts (version 4, Normal accounts with flags and tokens, from XXX release)
```
[ <flags> :: int()
, <nonce> :: int()
, <balance> :: int()
, <tokens> :: [{id(),int()}]
```

### Signed transaction
```
[ <signatures> :: [binary()]
Expand Down Expand Up @@ -309,6 +324,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
```
[ <sender> :: id()
, <recipient> :: id()
, <token> :: id()
, <amount> :: int()
, <fee> :: int()
, <ttl> :: int()
, <nonce> :: int()
, <payload> :: 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
```
Expand Down Expand Up @@ -1013,3 +1050,56 @@ NOTE:
, <pubkey> :: binary()
]
```


#### Token create transaction
```
[ <creator> :: id()
, <meta_data> :: binary()
, <contract> :: id()
, <amount> :: int()
, <recipient> :: id()
, <parent> :: id()
, <final> :: bool()
, <ttl> :: int()
, <fee> :: int()
, <nonce> :: int()
]
```

#### Token mint transaction
```
[ <amount> :: int()
, <recipient> :: id()
, <final> :: bool()
, <ttl> :: int()
, <fee> :: int()
, <nonce> :: int()
]
```

#### Token trade transaction
```
[ <trades> :: [{id(), id(), int(), id()}]
, <ttl> :: int()
, <fee> :: int()
, <nonce> :: int()
]
```

#### Token burn transaction
```
[ <amount> :: int()
, <ttl> :: int()
, <fee> :: int()
, <nonce> :: int()
]
```

#### Token finalize transaction
```
[ <ttl> :: int()
, <fee> :: int()
, <nonce> :: int()
]
```
144 changes: 144 additions & 0 deletions tokens/tokens.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,144 @@
# 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, and is the owner
decides that enough is enough, the ANT can be finalized, preventing
more minting. An ANT can also be created with a final supply already
minted and given to the owner of the ANT.

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

Fees cannot be payed by native tokens, but only by aeons, but other
services can be payed for with them. (TODO: Oracle query fees, etc?)

If you need to govern the behavior of the tokens, a contract can be
connected to the ANT. The contract has functions that will be called
for example when tokens of the type is traded. The result of the call
decides if the trade can happen 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`)

## ANT transactions
An ANT can be:
- *created* through the `ant_create_tx`
- *finalized* through the `ant_finalize_tx`, preventing more minting.

## Token transactions

Tokens can be:
- *minted* through the `ant_mint_tx`.
- *destroyed* through the `ant_burn_tx`.
- *traded* atomically through the `ant_trade_tx`.

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

### ANT create transaction (`ant_create_tx`)

The ANT create transaction takes the mandatory argument:
- Meta data : string

The ANT create transaction takes the optional arguments:
- Contract
- Amount
- Recipient
- Parent (TODO: Decide what impact this have. Possibly delay having hierarchical tokens)
- Final

The `meta data` is an uninterpreted string but token minters are encouraged to
use the following json object:
```
{
"type" : "object",
"properties": { "name": {"type" : "string"},
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

and an identifier... sure, that could be the hash of the string, but it has to be the unique thing to work with in the contract. We could even allow a string of 3 unique characters, like in valutas... that's limited, but makes contracts very readable.

}
```

The `contract` has to provide the following ACI:
```
spend(recipient : address, payload : Type) : boolean
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What is the payload type here? A binary string, I guess, like in normal spend?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it probably could be something ABI encoded. Just as in oracles. 🤔

trade([(from : address, to: address, Optional(token : address))], payload : Type) : boolean
mint(amount: integer) : boolean
burn(amount : integer) : boolean
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Call.caller = the account burning the tokens, presumably. And same for spend.

```

The `amount` is the number of tokens to create.

The `recipient` an account to dump the tokens created, if not given the tokens are
spent to the creators account.

The `parent` is a pointer to a parent token for hierarchical tokens.

The `final` argument sets the final flag which would block future minting.

If a contract is provided then any transaction (spend, trade, mint, burn) would
call this contract and the transaction only goes through if the result of the
corresponfing 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.

## Tokens state tree

The tokens state tree contain token type objects.
Tokens are stored in the account state tree as a list of tuples of
token id:s and balances.

### Token state tree objects

- The token state tree contains
- Token type objects

#### The token type object

- Created by an token create transaction.

```
{ creator :: id()
, <meta_data> :: binary()
, <contract> :: id()
, <total_amount> :: int()
, <parent> :: id()
, <final> :: bool()

}
```