Native Tokens - Scaling DeFi, improving UX

Hello everybody, as per the good suggestion of @yani , I herewith present you the concepts we discussed in 2019 regarding so called “Native Tokens”. It turned out that Aeternity was way ahead of its time with this idea, as Ethereum proved that creating, handling and swapping tokens inside simple general purpose smart contracts is terribly inefficient and therefore expensive and not scalable. I’ll spare you the github links and will put things right up here for better discussability.

What: Short Summary

  • Make the creation, existence, transfer, destruction and trading of tokens a first-class object on the protocol, meaning, directly implemented into it.
  • Contract logic can, but does not have to be applied to certain of the above operations

Why: Scalabilit, Costs, UX

  • As per first estimates, gas costs of a token transfer would decrease by 90%.
  • DeFi token swaps happen as one atomic operation and the existence of buy or sell offers do not have to be stored in smart contracts any more. This reduces transaction amount and storage costs heavily.
  • Imagine the impact on components, from wallets to middleware, how easier and more clear interaction and data visualisation will be if one can be 100% sure one is dealing with a native token and not some contract maybe.



ANT: Aeternity Native Token, the token as an entity, living on the chain (contrary to a token created through a smart contract).
Token: The countable amount one owns of an ANT.

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.


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(<CreatorPubkey><CreateTxNonce>)

where Blake2b is the 256 bit hash. The API
of an ANT id is tagged by

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 spend transaction

A single token transfer consists of the fields:

  • Sender
  • Receiver
  • Amount
  • An ANT id

Token trade transaction

The token trade transaction takes the argument:

  • trades

The trades field contans a non-empty list of token transfers.

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

The advantage of trades is that they don’t have to live on-chain only, parties could sign them off-chain and broadcast them to the network to be executed.

TODO: Design decision: Shall trades also exist inside sophia, in a form that also holds data about asking/bidding price and offered/inquired asset/ANT? The layout of trades might need its separate discussion thread.

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

I don’t quite understand it yet, but I think native tokens are great.


as I am a fan of pointing to existing documents here the original specification on the respective branch on Github:

@LiuShao the point is that this would allow users to create tokens without relying on smart contract standards like AEX9. so creating, minting & burning tokens could would be part of the core protocol. in addition to performance improvements this would also allow us to deposit custom tokens into state channels and use them inside the state channels. currently, if you want to deal with custom tokens inside state channels these tokens only live inside the channel and do not exist on-chain.

personally I am very excited about ANTs and always wanted to see them implemented in aeternity.


What happens with AEX9 after this?

first of all ANTs aren’t implemented yet and this might take some time. independently AEX9 tokens can still co-exist.

maybe we can also propose a migration path if projects using AEX9 want to migrate to ANTs after they are released. I think we also had a discussion about that in the past


A migration path for AEX9 to ANTs is already in place if the AEX9 token implements the swappable extension: aeternity-fungible-token/fungible-token-full.aes at 8a08e18375f75114d56c296298f8ce3c80bfe107 · mradkov/aeternity-fungible-token · GitHub


I fully support ANTs with an integrated DEX for æternity blockchain. How about AMM? Would suggest to keep it simple at the beginning and only allow for AE<>ANT swaps and not ANT<>ANT swaps.

moved this thread to protocol development.