New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

AIP #004 - Aion Token Standard #4

Open
sampajotphipps opened this Issue Sep 12, 2018 · 5 comments

Comments

Projects
None yet
5 participants
@sampajotphipps
Collaborator

sampajotphipps commented Sep 12, 2018

Title: Aion Token Standard
Author(s): Sam Pajot-Phipps, Yao Sun, Stephane Gosselin
Type: ASC (Aion Standards and Conventions)
Status: DRAFT
Creation Date: September 12th, 2018
Contact Information: sam@aion.network, qoire@protonmail.com, s.gosselin@queensu.ca

Summary

A fungible token standard to meet the functionality requirements of current dApp developers and enable cross-chain token movements.

Value Proposition

To enable the creation and management of innovative fungible cross-chain digital assets on the Aion blockchain and other connected networks.

Motivation

The primary motivation for the proposal is to provide the dApp's that are building on the Aion blockchain with a common standard to implement fungible tokens while enabling cross-chain functionality for their asset with minimal effort. The secondary motivation is to define the required contract interfaces and events to communicate with bridges and identify any upgrade requirements for current token standards on remote blockchains to allow communication with bridges.

Non-Goals

The scope of this standard is limited to on-chain functionality for fungible tokens. This standard does not address how cross-chain token movements should be reflected on third party services or tools such as blockchain explorers, wallets or exchanges.

Success Metrics

There are two key indicators of success for this standard:

  1. Number of contract deployments
  2. Number of deployed contracts with defined operators for cross-chain movements

Description

Fungible tokens have proven to be one of the core building blocks of the current decentralized web era. This next-generation fungible token standard reflects the evolving needs of dApp developers for core functionality and security.

The Aion Fungible Token Standard has been designed to address the shortcomings of token standards on other existing blockchains by blurring the line between layer one and layer two. At the core of the design is the ability to perform cross chain token transfers while maintaining security and stable total supply. Additional features include safe transfers, token callbacks, and mint/burn interface.

This standard aims to provide a reliable interface which 3rd party tools can use when building products on top of Aion.

High-Level Architecture

This diagram provides a high-level overview of the proposed contract architecture related to fungible tokens to enable cross-chain movements and communicate with bridges.

Note: The scope of this AIP is limited to the ATS contract highlighted. In the diagram below the Aion blockchain is the Home chain.

Specification

Definitions

  • Bridge Operator: A organization(s) / project (s) / account that execute the required functions to enable a cross-chain transaction on behalf of the token holder
  • Bridge Registry: A smart contract registry that maintains a mapping of bridgeId, Bridge Contracts and networkId
  • Bridge Contract: A smart contract designed to receive, verify and emit events and signature bundles from/to a specific bridge
  • Token Issuer: The project / user / account that owns the Token Contract
  • Token Contract: A smart contract that is deployed using this token standard
  • Token Holder: An account / user that has ownership over a token balance
  • Token Sender: An account / user that is sending a token
  • Token Receiver: An account / user that is receiving a token
  • Home chain: The blockchain network where the cross-chain transaction originates and is emitted from
  • Remote chain: The blockchain network that is the destination for the cross-chain transaction

Methods

General

The constant functions detailed below MUST be implemented.

name function

Returns the name of the token, e.g., "MyToken".

returns: Name of the token.

 function name() public constant returns (string)

symbol function

Returns the symbol of the token, e.g., "MYT".

returns: Symbol of the token.

function symbol() public constant returns (string)

totalSupply function

Get the total number of minted tokens across all chains.

returns: Total supply of tokens currently in circulation.

function totalSupply() public constant returns (uint128)

balanceOf function

Get the balance of the account with address tokenHolder on Home chain.

NOTE: balanceOf(address(this)) is the frozen token supply and MUST be equal to totalSupply minus liquidSupply.

NOTE: The balance MUST be zero (0) or higher.

parameters
tokenHolder: Address for which the balance is returned.

returns: Amount of token held by tokenHolder in the token contract.

function balanceOf(address tokenHolder) public constant returns (uint128)

granularity function

Get the smallest part of the token that's not divisible.

In other words, the granularity is the smallest number of tokens (in the basic unit) which MAY be minted, sent, burned, frozen, or thawed in any transaction.

The following rules MUST be applied regarding the granularity:

  • The granularity value MUST be set at creation time.
  • The granularity value MUST be the same across all chains.
  • The granularity value MUST NOT be changed ever.
  • The granularity value MUST be greater or equal to 1.
  • Any minting, send, burning, freezing, or thawing of tokens MUST be a multiple of the granularity value.
  • Any operation that would result in a balance that's not a multiple of the granularity value MUST be considered invalid, and the transaction MUST revert.

NOTE: Most of the tokens SHOULD have a granularity of 1 unless there is a good reason for not allowing divisibility of the token.

returns: The smallest non-divisible part of the token.

function granularity() public constant returns (uint128)

Operators

An operator is an address which is allowed to send, burn and freeze tokens on behalf of another address.

The following rules apply to any operator:

  • An address MUST always be an operator for itself. Hence an address MUST NOT ever be revoked as its own operator.
  • If an address is an operator for a token holder, isOperatorFor MUST return true.
  • If an address is not an operator for a token holder, isOperatorFor MUST return false.
  • The token contract MUST emit an AuthorizedOperator event with the correct values when a token holder authorizes an address as its operator as defined in the [AuthorizedOperator Event][authorizedoperator].
  • The token contract MUST emit a RevokedOperator event with the correct values when a token holder revokes an address as its operator as defined in the [RevokedOperator Event][revokedoperator].

NOTE: A token holder MAY have multiple operators at the same time.

NOTE: A token holder MAY authorize an already authorized operator. An AuthorizedOperator MUST be emitted each time.

NOTE: A token holder MAY revoke an already revoked operator. A RevokedOperator MUST be emitted each time.

AuthorizedOperator event

Indicates the authorization of operator as an operator for tokenHolder.

NOTE: This event MUST NOT be emitted outside of an operator authorization process.

parameters
operator: Address which became an operator of tokenHolder.
tokenHolder: Address of a token holder which authorized the operator address as an operator.

event AuthorizedOperator(address indexed operator, address indexed tokenHolder)

RevokedOperator event

Indicates the revocation of operator as an operator for tokenHolder.

NOTE: This event MUST NOT be emitted outside of an operator revocation process.

parameters
operator: Address which was revoked as an operator of tokenHolder.
tokenHolder: Address of a token holder which revoked the operator address as an operator.

event RevokedOperator(address indexed operator, address indexed tokenHolder)

The authorizeOperator, revokeOperator and isOperatorFor functions described below MUST be implemented to manage operators.
Token contracts MAY implement other functions to manage operators.

authorizeOperator function

Set a third party operator address as an operator of msg.sender to send, burn or freeze tokens on its behalf.

NOTE: The token holder (msg.sender) is always an operator for itself. This right MUST NOT be revoked. This function MUST revert if it is called to authorize the token holder (msg.sender) as an operator for itself (i.e. if operator is equal to msg.sender).

parameters
operator: Address to set as an operator for msg.sender.

function authorizeOperator(address operator) public

revokeOperator function

Remove the right of the operator address to be an operator for msg.sender and to send, burn or freeze tokens on its behalf.

NOTE: The token holder (msg.sender) is always an operator for itself. This right MUST NOT be revoked. This function MUST revert if it is called to revoke the token holder (msg.sender) as an operator for itself (i.e., if operator is equal to msg.sender).

parameters
operator: Address to rescind as an operator for msg.sender.

function revokeOperator(address operator) public

isOperatorFor function

Indicate whether the operator address is an operator of the tokenHolder address.

parameters
operator: Address which may be an operator of tokenHolder.
tokenHolder: Address of a token holder which may have the operator address as an operator.

returns: true if operator is an operator of tokenHolder and false otherwise.

function isOperatorFor(address operator, address tokenHolder) public constant returns (bool)

Cross Chain Movements

liquidSupply function

Indicate the total supply of tokens currently in circulation on this chain.

NOTE: The liquid supply MUST be equal to totalSupply minus balanceOf(address(this)).

NOTE: The liquid supply MUST be equal to the sum of the balances of all addresses other than address(this) as returned by the balanceOf function.

returns: Total supply of tokens currently in circulation on this chain.

function liquidSupply() public constant returns (uint128)

thaw function

[WIP: Define bridge registry interface]

Called by bridge contract through the bridge registry when tokens moved from remote chain.

NOTE: MUST emit Thawed event.

NOTE: MUST revert if called from an address not associated registered with bridgeId in bridge registry.

NOTE: MUST revert if called from bridgeId not approved in bridge registry.

parameters
localRecipient: Address of token recipient on home chain.
amount: Number of tokens to thaw.
bridgeId: Bridge used for relaying transaction.
bridgeData: Data from the bridge.
remoteSender: Address of token sender on remote chain.
remoteBridgeId: Bridge used for relaying transaction.
remoteData: Data submitted by remoteSender.

function thaw(address localRecipient, uint128 amount, bytes32 bridgeId, bytes bridgeData, bytes32 remoteSender, bytes32 remoteBridgeId, bytes remoteData) public

Thawed event

Indicate a thaw of amount of tokens from the from address on remote chain to the to address on home chain.

parameters
localRecipient: Address of token recipient on home chain.
amount: Number of tokens to thaw.
bridgeId: Bridge to used for relaying transaction.
bridgeData: Data from the bridge.
remoteSender: Address of token sender on remote chain.
remoteBridgeId: Bridge to used for relaying transaction.
remoteData: Data submitted by remoteSender.

event Thawed(address indexed localRecipient, uint128 amount, bytes32 indexed bridgeId, bytes bridgeData, bytes32 indexed remoteSender, bytes32 remoteBridgeId, bytes remoteData)

freeze function

[WIP: Define bridge registry interface]

Called by a token holder to move tokens to remote chain.

NOTE: MUST emit Froze event.

NOTE: MUST revert if bridgeId not approved in bridge registry.

parameters
remoteRecipient: Address on remote chain to receive tokens.
amount: Amount of tokens to send remote chain.
bridgeId: Bridge to use for relaying transaction.
localData: Data submitted by tokenHolder.

function freeze(bytes32 remoteRecipient, uint128 amount, bytes32 bridgeId, bytes localData) public

operatorFreeze function

[WIP: Define bridge registry interface]

Called by an operator to move tokens to remote chain on behalf of token holder.

NOTE: MUST emit Froze event.

NOTE: MUST revert if bridgeId not approved in bridge registry.

parameters
localSender: Address on home from which to send tokens.
remoteRecipient: Address on remote chain to receive tokens.
amount: Amount of tokens to send remote chain.
bridgeId: Bridge to use for relaying transaction.
localData: Data submitted by tokenHolder.

function operatorFreeze(address localSender, bytes32 remoteRecipient, uint128 amount, bytes32 bridgeId, bytes localData) public

Froze event

Indicate a freeze of amount of tokens from the from address on home chain to the to address on remote chain.

parameters
localSender: Address on home chain from which to send tokens.
remoteRecipient: Address on remote chain to receive tokens.
amount: Amount of tokens to send remote chain.
bridgeId: Bridge to use for relaying transaction.
localData: Data submitted by tokenHolder.

event Froze(address indexed localSender, bytes32 indexed remoteRecipient, uint128 amount, bytes32 indexed bridgeId, bytes localData)

Sending Tokens

When an operator sends an amount of tokens from a token holder to a recipient with the associated data and operatorData, the token contract MUST apply the following rules:

  • Any token holder MAY send tokens to any recipient.
  • The balance of the token holder MUST be decreased by the amount.
  • The balance of the recipient MUST be increased by the amount.
  • The balance of the token holder MUST be greater or equal to the amount—such that its resulting balance is greater or equal to zero (0) after the send.
  • The token contract MUST emit a Sent event with the correct values as defined in the [Sent Event][sent].
  • The operator MAY communicate any information in the operatorData.
  • The token contract MUST call the tokensToSend hook of the token holder if the token holder registers an ATSTokenSender implementation via [ERC820].
  • The token contract MUST call the tokensReceived hook of the recipient if the recipient registers an ATSTokenRecipient implementation via [ERC820].
  • The data and operatorData MUST be immutable during the entire send process—hence the same data and operatorData MUST be used to call both hooks and emit the Sent event.

The token contract MUST revert when sending in any of the following cases:

  • The operator address is not an authorized operator for the token holder.
  • The resulting token holder balance or recipient balance after the send is not a multiple of the granularity defined by the token contract.
  • The address of the token holder or the recipient is 0x0.
  • Any of the resulting balances becomes negative, i.e. becomes less than zero (0).

The token contract MAY send tokens from many token holders, to many recipients, or both. In this case:

  • The previous send rules MUST apply to all the token holders and all the recipients.
  • The sum of all the balances incremented MUST be equal to the total sent amount.
  • The sum of all the balances decremented MUST be equal to the total sent amount.
  • A Sent event MUST be emitted for every token holder and recipient pair with the corresponding amount for each pair.
  • The sum of all the amounts from the Sent event MUST be equal to the total sent amount.

NOTE: Mechanisms such as applying a fee on a send is considered as a send to multiple recipients: the intended recipient and the fee recipient.

NOTE: Transfer of tokens MAY be chained. For example, if a contract upon receiving tokens sends them further to another address. In this case, the previous send rules apply to each send, in order.

NOTE: Sending an amount of zero (0) tokens is valid and MUST be treated as a regular send.

Implementation Requirement:

  • The token contract MUST call the tokensToSend hook before updating the state.
  • The token contract MUST call the tokensReceived hook after updating the state.
    I.e., tokensToSend MUST be called first, then the balances MUST be updated to reflect the send, and finally tokensReceived MUST be called afterward. Thus a balanceOf call within tokensToSend returns the balance of the address before the send and a balanceOf call within tokensReceived returns the balance of the address after the send.

NOTE: The data field contains extra information intended for, and defined by the recipient— similar to the data field in a regular ether send transaction. Typically, data is used to describe the intent behind the send. The operatorData MUST only be provided by the operator. It is intended more for logging purposes and particular cases. (Examples include payment references, cheque numbers, countersignatures and more.) In most of the cases the recipient would ignore the operatorData, or at most, it would log the operatorData.

Sent event

Indicate a send of amount of tokens from the from address to the to address by the operator address.

NOTE: This event MUST NOT be emitted outside of a send or an [ERC20] transfer process.

parameters
operator: Address which triggered the send.
from: Token holder.
to: Token recipient.
amount: Number of tokens to send.
data: Information attached to the send, and intended for the recipient (to).
operatorData: Information attached to the send by the operator.

event Sent(address indexed operator, address indexed from, address indexed to, uint128 amount, bytes data, bytes operatorData)

The send and operatorSend functions described below MUST be implemented to send tokens.
Token contracts MAY implement other functions to send tokens.

NOTE: An address MAY send an amount of 0, which is valid and MUST be treated as a regular send.

send function

Send the amount of tokens from the address msg.sender to the address to.

The operator and the token holder MUST both be the msg.sender.

parameters
to: Token recipient.
amount: Number of tokens to send.
data: Information attached to the send, and intended for the recipient (to).

function send(address to, uint128 amount, bytes data) public

operatorSend function

Send the amount of tokens on behalf of the address from to the address to.

The operator MUST be msg.sender. The value of from MAY be 0x0, then the from (token holder) used for the send MUST be msg.sender (the operator).

Reminder: If the operator address is not an authorized operator of the from address, then the send process MUST revert.

NOTE: from and msg.sender MAY be the same address. I.e., an address MAY call operatorSend for itself. This call MUST be equivalent to send with the addition that the operator MAY specify an explicit value for operatorData (which cannot be done with the send function).

parameters
from: Token holder (or 0x0 to set from to msg.sender).
to: Token recipient.
amount: Number of tokens to send.
data: Information attached to the send, and intended for the recipient (to).
operatorData: Information attached to the send by the operator.

function operatorSend(address from, address to, uint128 amount, bytes data, bytes operatorData) public

Minting Tokens

Minting tokens is the act of producing new tokens. This standard intentionally does not define specific functions to mint tokens. This intent comes from the wish not to limit the use of the standard as the minting process is generally an implementation detail.

Nonetheless, the rules below MUST be respected when minting for a recipient:

  • Tokens MUST be minted on all chains such that totalSupply remains equal on all chains.
  • Tokens MUST be minted to address(this) when minting is initiated on other chain such that the correct liquidSupply is maintained.
  • Tokens MAY be minted for any recipient address.
  • The total supply MUST be increased by the amount of tokens minted.
  • The balance of 0x0 MUST NOT be decreased.
  • The balance of the recipient MUST be increased by the amount of tokens minted.
  • The token contract MUST emit a Minted event with the correct values as defined in the [Minted Event][minted].
  • The token contract MUST call the tokensReceived hook of the recipient if the recipient registers an ATSTokenRecipient implementation via [ERC820].
  • The data and operatorData MUST be immutable during the entire mint process—hence the same data and operatorData MUST be used to call the tokensReceived hook and emit the Minted event.

The token contract MUST revert when minting in any of the following cases:

  • The resulting recipient balance after the mint is not a multiple of the granularity defined by the token contract.
  • The recipient is a contract, and it does not implement the ATSTokenRecipient interface via [ERC820].
  • The address of the recipient is 0x0.

NOTE: The initial token supply at the creation of the token contract MUST be considered as minting for the amount of the initial supply to the address (or addresses) receiving the initial supply.

The token contract MAY mint tokens for multiple recipients at once. In this case:

  • The previous mint rules MUST apply to all the recipients.
  • The sum of all the balances incremented MUST be equal to the total minted amount.
  • A Minted event MUST be emitted for every recipient with the corresponding amount for each recipient.
  • The sum of all the amounts from the Minted event MUST be equal to the total minted amount.

NOTE: Minting an amount of zero (0) tokens is valid and MUST be treated as a regular mint.

NOTE: The data field contains extra information intended for, and defined by the recipient— similar to the data field in a regular ether send transaction. Typically, data is used to describe the intent behind the mint. The operatorData MUST only be provided by the operator. It is intended more for logging purposes and particular cases. (Examples include payment references, cheque numbers, countersignatures and more.) In most of the cases the recipient would ignore the operatorData, or at most, it would log the operatorData.

Minted event

Indicate the minting of amount of tokens to the to address by the operator address.

NOTE: This event MUST NOT be emitted outside of a mint process.

parameters
operator: Address which triggered the mint.
to: Token recipient.
amount: Number of tokens minted.
data: Information attached to the minting, and intended for the recipient (to).
operatorData: Information attached to the minting by the operator.

event Minted(address indexed operator, address indexed to, uint128 amount, bytes data, bytes operatorData)

Burning Tokens

Burning tokens is the act of destroying existing tokens. ATS explicitly defines two functions to burn tokens (burn and operatorBurn). These functions facilitate the integration of the burning process in wallets and dapps. However, the token contract MAY prevent some or all token holders from burning tokens for any reason. The token contract MAY also define other functions to burn tokens.

The rules below MUST be respected when burning the tokens of a token holder:

  • Tokens MUST be burned on all chains such that totalSupply remains equal on all chains.
  • Tokens MUST be burned from address(this) when burned is initiated on other chain such that the correct liquidSupply is maintained.
  • Tokens MAY be burned from any token holder address.
  • The total supply MUST be decreased by the amount of tokens burned.
  • The balance of 0x0 MUST NOT be increased.
  • The balance of the token holder MUST be decreased by amount of tokens burned.
  • The token contract MUST emit a Burned event with the correct values as defined in the [Burned Event][burned].
  • The token contract MUST call the tokensToSend hook of the token holder if the token holder registers an ATSTokenSender implementation via [ERC820].
  • The operatorData MUST be immutable during the entire burn process—hence the same operatorData MUST be used to call the tokensToSend hook and emit the Burned event.
  • The data field of the tokensToSend hook MUST be empty.

The token contract MUST revert when burning in any of the following cases:

  • The operator address is not an authorized operator for the token holder.
  • The resulting token holder balance after the burn is not a multiple of the granularity defined by the token contract.
  • The balance of token holder is inferior to the amount of tokens to burn (i.e., resulting in a negative balance for the token holder).
  • The address of the token holder is 0x0.

The token contract MAY burn tokens for multiple token holders at once. In this case:

  • The previous burn rules MUST apply to each token holders.
  • The sum of all the balances decremented MUST be equal to the total burned amount.
  • A Burned event MUST be emitted for every token holder with the corresponding amount for each token holder.
  • The sum of all the amounts from the Burned event MUST be equal to the total burned amount.

NOTE: Burning an amount of zero (0) tokens is valid and MUST be treated as a regular burn.

Burned event

Indicate the burning of amount of tokens from the from address by the operator address.

NOTE: This event MUST NOT be emitted outside of a burn process.

parameters
operator: Address which triggered the burn.
from: Token holder whose tokens are burned.
amount: Number of tokens burned.
operatorData: Information attached to the burn by the operator.

event Burned(address indexed operator, address indexed from, uint128 amount, bytes operatorData)

The burn and operatorBurn functions described below MUST be implemented to burn tokens.
Token contracts MAY implement other functions to burn tokens.

burn function

Burn the amount of tokens from the address msg.sender.

The operator and the token holder MUST both be the msg.sender.

parameters
amount: Number of tokens to burn.

function burn(uint128 amount) public;

operatorBurn function

Burn the amount of tokens on behalf of the address from.

The operator MUST be msg.sender. The value of from MAY be 0x0, then the from (token holder) used for the burn MUST be msg.sender (the operator).

Reminder: If the operator address is not an authorized operator of the from address, then the burn process MUST revert.

parameters
from: Token holder whose tokens will be burned (or 0x0 to set from to msg.sender).
amount: Number of tokens to burn.
operatorData: Information attached to the burn by the operator.

function operatorBurn(address from, uint128 amount, bytes operatorData) public;

NOTE: The operator MAY pass any information via operatorData. The operatorData MUST only be provided by the operator.

NOTE: from and msg.sender MAY be the same address. I.e., an address MAY call operatorBurn for itself. This call MUST be equivalent to burn with the addition that the operator MAY specify an explicit value for operatorData (which cannot be done with the burn function).

ATSTokenSender And The tokensToSend Hook

The tokensToSend hook notifies of any decrement of balance (send and burn) for a given token holder. Any address (regular or contract) wishing to be notified of token debits from their address MAY register the address of a contract implementing the ATSTokenSender interface described below via [ERC820].

interface ATSTokenSender {
    function tokensToSend(
        address operator,
        address from,
        address to,
        uint128 amount,
        bytes data,
        bytes operatorData
    ) public;
}

NOTE: A regular address MAY register a different address implementing the interface on its behalf. A contract MAY register either its address or the address of another contract but said address MUST implement the interface on its behalf.

tokensToSend

Notify a send or burn (if to is 0x0) of amount tokens from the from address to the to address by the operator address.

NOTE: This function MUST NOT be called outside of a burn, send or [ERC20] transfer process.

parameters
operator: Address which triggered the balance decrease (through sending or burning).
from: token holder.
to: token recipient for a send and 0x for a burn.
amount: Number of tokens the token holder balance is decreased by.
data: Extra information provided by the token holder.
operatorData: Extra information provided by the address which triggered the balance decrease.

function tokensToSend(address operator, address from, address to, uint128 amount, bytes data, bytes operatorData) public

The following rules apply when calling the tokensToSend hook:

  • The tokensToSend hook MUST be called every time the balance is decremented.
  • The tokensToSend hook MUST be called before the state is updated—i.e. before the balance is decremented.
  • operator MUST be the address which triggered the decrease of the balance.
  • from MUST be the address of the token holder whose balance is decreased.
  • to MUST be the address of the recipient whose balance is increased for a send.
  • to MUST be 0x0 for a burn.
  • amount MUST be the number of tokens the token holder balance is decreased by.
  • data MUST contain the extra information provided by the token holder (if any) for a send.
  • data MUST be empty for a burn.
  • operatorData MUST contain the extra information provided by the address which triggered the decrease of the balance (if any).
  • The token holder MAY block a decrease of its balance by reverting. (I.e., reject the withdrawal of tokens from its account.)

NOTE: Multiple token holders MAY use the same implementation of ATSTokenSender.

NOTE: An address can register at most one implementation at any given time for all tokens. Hence the ATSTokenSender MUST expect to be called by different token contracts. The msg.sender of the tokensToSend call is expected to be the address of the token contract.

ATSTokenRecipient And The tokensReceived Hook

The tokensReceived hook notifies of any increment of the balance (send and mint) for a given recipient. Any address (regular or contract) wishing to be notified of token credits to their address MAY register the address of a contract implementing the ATSTokenSender interface described below via [ERC820].

interface ATSTokenRecipient {
    function tokensReceived(
        address operator,
        address from,
        address to,
        uint128 amount,
        bytes data,
        bytes operatorData
    ) public;
}

If the recipient is a contract, which has not registered an ATSTokenRecipient implementation; the token contract:

  • MUST revert if the tokensReceived hook is called from a mint or send call.
  • SHOULD accept if the tokensReceived hook is called from an ERC20 transfer or transferFrom call.

NOTE: A regular address MAY register a different address—the address of a contract—implementing the interface on its behalf. A contract MUST register either its address or the address of another contract and said address MUST implement the interface on its behalf.

tokensReceived

Notify a send or mint (if from is 0x0) of amount tokens from the from address to the to address by the operator address.

NOTE: This function MUST NOT be called outside of a mint, send or [ERC20] transfer process.

parameters
operator: Address which triggered the balance increase (through sending or minting).
from: token holder for a send and 0x for a mint.
to: token recipient.
amount: Number of tokens the recipient balance is increased by.
data: Extra information provided by the token holder for a send and nothing (empty bytes) for a mint,.
operatorData: Extra information provided by the address which triggered the balance increase.

function tokensReceived(address operator, address from, address to, uint128 amount, bytes data, bytes operatorData) public

The following rules apply when calling the tokensToSend hook:

  • The tokensReceived hook MUST be called every time the balance is incremented.

  • The tokensReceived hook MUST be called after the state is updated—i.e. after the balance is incremented.

  • operator MUST be the address which triggered the increase of the balance.

  • from MUST be the address of the token holder whose balance is decreased for a send.

  • from MUST be 0x0 for a mint.

  • to MUST be the address of the recipient whose balance is increased.

  • amount MUST be the number of tokens the recipient balance is increased by.

  • operatorData MUST contain the extra information provided by the address which triggered the increase of the balance (if any).

  • The token holder MAY block an increase of its balance by reverting. (I.e., reject the reception of tokens.)

NOTE: Multiple token holders MAY use the same implementation of ATSTokenRecipient.

NOTE: An address can register at most one implementation at any given time for all tokens. Hence the ATSTokenRecipient MUST expect to be called by different token contracts. The msg.sender of the tokensReceived call is expected to be the address of the token contract.

Solidity Interface

/// @title ASC-ATS Aion Fungible Token Standard
/// @dev See https://github.com/qoire/ATS

pragma solidity 0.4.15;

interface ATS {

    function name() public constant returns (string);
    function symbol() public constant returns (string);
    function granularity() public constant returns (uint128);
    function totalSupply() public constant returns (uint128);

    function balanceOf(address tokenHolder) public constant returns (uint128);
    function isOperatorFor(address operator, address tokenHolder) public constant returns (bool);

    function authorizeOperator(address operator) public;
    function revokeOperator(address operator) public;

    function send(address to, uint128 amount, bytes senderData) public;
    function burn(uint128 amount, bytes senderData) public;

    function operatorSend(address from, address to, uint128 amount, bytes senderData, bytes operatorData) public;
    function operatorBurn(address from, uint128 amount, bytes senderData, bytes operatorData) public;

    function liquidSupply() public constant returns (uint128);
    function thaw(address localRecipient, uint128 amount, bytes32 bridgeId, bytes bridgeData, bytes32 remoteSender, bytes32 remoteBridgeId, bytes remoteData) public;    
    function freeze(bytes32 remoteRecipient, uint128 amount, bytes32 bridgeId, bytes localData) public;
    function operatorFreeze(address localSender, bytes32 remoteRecipient, uint128 amount, bytes32 bridgeId, bytes localData) public;

    event Sent(
        address indexed operator,
        address indexed from,
        address indexed to,
        uint128 amount,
        bytes senderData,
        bytes operatorData
    );

    event Thawed(address indexed localRecipient, uint128 amount, bytes32 indexed bridgeId, bytes bridgeData, bytes32 indexed remoteSender, bytes32 remoteBridgeId, bytes remoteData);
    event Froze(address indexed localSender, bytes32 indexed remoteRecipient, uint128 amount, bytes32 indexed bridgeId, bytes localData);
    event Minted(address indexed operator, address indexed to, uint128 amount, bytes operatorData);
    event Burned(address indexed operator, address indexed from, uint128 amount, bytes senderData, bytes operatorData);

    event AuthorizedOperator(address indexed operator, address indexed tokenHolder);
    event RevokedOperator(address indexed operator, address indexed tokenHolder);
}

Logic

This standard is based on ERC-777 from Ethereum. The following modifications have been made for additional functionality.

  • defaultOperators removed to guarantee operators are always opt-in.
  • liquidSupply(), thaw(), freeze(), operatorFreeze() functions added for cross chain transfers.
  • burn(), operatorBurn() functions added for applications using slashing.

Interface Registration

[WIP: Define token registry]

Cross Chain Functionality

[WIP: Define bridge registry interface]

Cross-chain token transfers implies the total supply of a token is potentially shared across a number of distinct blockchains. This total supply must be invariant across all chains, as such if tokens are minted or burned on one chain, the equivalent must be done on other chains. Our approach introduces a distinction between totalSupply and liquidSupply where totalSupply represents the sum of liquid tokens across all chains and liquidSupply represents the sum of liquid tokens on the home chain. As such, totalSupply minus liquidSupply is the sum of tokens which are used on other chains and therefore currently frozen on the home chain. This frozen balance is held in the ATS contract and is equal to balanceOf(address(this)).

To send a cross chain token transfer from the home chain to the remote chain, the sender must freeze the token on home chain and specify the remote chain and remote token receiver address using the freeze or operatorFreeze functions. The Bridge Contract then relays the transaction to the destination chain and thaws the appropriate token amount.

To send a cross chain token transfer from a remote chain to home chain, the sender must first freeze their remote tokens by sending them to the storage contract and emitting an event which specifies the destination address on the home chain and the Bridge Contract to use. The Bridge Contract then relays the transaction to the home chain and calls the thaw function.

NOTE: An ATS token contract must register an approved Bridge Contract by calling setPermission(bytes32 bridgeId, bool status) in the Bridge Registry. [WIP: review security implication for async permission changes between chains]

Risks & Assumptions

This standard is natively compatible with the ERC-777 standard from Ethereum and can be made compatible with the ERC-20 standard as an implementation detail.

[ERC20] compatibility notes:
The decimals of the token MUST always be 18. For a pure ATS token the [ERC20] decimal function is OPTIONAL, and its existence MUST NOT be relied upon when interacting with the token contract. (The decimal value of 18 is implied.) For an [ERC20] compatible token, the decimal function is REQUIRED and MUST return 18. (In [ERC20], the decimals function is OPTIONAL, but if the value of the function is not present, it is not clearly defined and may be assumed to be 0. Hence for compatibility reasons, decimals MUST be implemented for [ERC20] compatible tokens.)

The name, symbol, totalSupply, and balanceOf constant functions MUST be backward compatible with [ERC20].

While a Sent event MUST NOT be emitted when minting or burning, if the token contract is [ERC20] backward compatible, a Transfer event with the from or to parameter set to 0x0 SHOULD be emitted. The [ERC20] standard does not define the concept of burning tokens, but this is a commonly accepted practice.

The tokensToSend and tokensReceived hook takes precedence over [ERC20] and MUST be called (if registered) when calling [ERC20]'s transfer and transferFrom event. When called from a transfer, operator MUST be the same value as the from. When called from a transferFrom, operator MUST be the address which issued the transferFrom call.

Test Cases

N/A

Implementations

Reference Implementation

Dependencies

The implementation of the Bridge Registry standard is a dependency to enable cross-chain movements. The Bridge Registry will be a new AIP.

The implementation of the Bridge Contract standard is a dependency to enable cross-chain movements. The Bridge Operator will be a new AIP.

The implementation of the Token Registry standard will be proposed as a new AIP.

Copyright

All AIP’s are public domain. Copyright waiver: https://creativecommons.org/publicdomain/zero/1.0/

sampajotphipps added a commit that referenced this issue Sep 12, 2018

sampajotphipps added a commit that referenced this issue Sep 12, 2018

@qoire

This comment has been minimized.

Show comment
Hide comment
@qoire

qoire Sep 12, 2018

Collaborator

Tangential discussion, can we simply rename this to directly track the issue number? It doesnt make sense for us to arbitrarily add or subtract numbers, if we follow the issue number everyone understands why its numbered such, and you can directly reference it as so: #4.

Collaborator

qoire commented Sep 12, 2018

Tangential discussion, can we simply rename this to directly track the issue number? It doesnt make sense for us to arbitrarily add or subtract numbers, if we follow the issue number everyone understands why its numbered such, and you can directly reference it as so: #4.

@sampajotphipps sampajotphipps changed the title from AIP #002 - Aion Token Standard to AIP #004 - Aion Token Standard Sep 12, 2018

sampajotphipps added a commit that referenced this issue Sep 12, 2018

sampajotphipps added a commit that referenced this issue Sep 12, 2018

sampajotphipps added a commit that referenced this issue Sep 12, 2018

sampajotphipps added a commit that referenced this issue Sep 12, 2018

@sampajotphipps

This comment has been minimized.

Show comment
Hide comment
@sampajotphipps

sampajotphipps Sep 12, 2018

Collaborator

@qoire Good point. I've made the change.

Collaborator

sampajotphipps commented Sep 12, 2018

@qoire Good point. I've made the change.

@nakakaka9

This comment has been minimized.

Show comment
Hide comment
@nakakaka9

nakakaka9 Sep 14, 2018

In the part where you discuss ATSTokenRecipient and tokensReceived hook, you have this line

"The following rules apply when calling the tokensToSend hook: "

I think it should be tokensReceived instead of tokensToSend, right?

nakakaka9 commented Sep 14, 2018

In the part where you discuss ATSTokenRecipient and tokensReceived hook, you have this line

"The following rules apply when calling the tokensToSend hook: "

I think it should be tokensReceived instead of tokensToSend, right?

@dev-dennis-guye

This comment has been minimized.

Show comment
Hide comment
@dev-dennis-guye

dev-dennis-guye Sep 20, 2018

I think that it would be beneficial to include a Created event so that third parties would be able to monitor tokens that may be deployed on the network.

dev-dennis-guye commented Sep 20, 2018

I think that it would be beneficial to include a Created event so that third parties would be able to monitor tokens that may be deployed on the network.

@thegostep

This comment has been minimized.

Show comment
Hide comment
@thegostep

thegostep Sep 20, 2018

@nakakaka9

I think it should be tokensReceived instead of tokensToSend, right?

Yes you are right, good catch.

@dev-dennis-guye

I think that it would be beneficial to include a Created event so that third parties would be able to monitor tokens that may be deployed on the network.

Our plan is to include this in the token registry. Whenever there is a change in the contract interface, an event must be emitted to broadcast the new interface (see ERC-820).

thegostep commented Sep 20, 2018

@nakakaka9

I think it should be tokensReceived instead of tokensToSend, right?

Yes you are right, good catch.

@dev-dennis-guye

I think that it would be beneficial to include a Created event so that third parties would be able to monitor tokens that may be deployed on the network.

Our plan is to include this in the token registry. Whenever there is a change in the contract interface, an event must be emitted to broadcast the new interface (see ERC-820).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment