EIPs/EIPS/eip-777.md
Jacques Dafflon d871450375 Automatically merged updates to draft EIP(s) 777
Hi, I'm a bot! This change was automatically merged because:

 - It only modifies existing Draft or Last Call EIP(s)
 - The PR was approved or written by at least one author of each modified EIP
 - The build is passing
2018-07-30 21:29:38 +01:00

42 KiB

eip title author discussions-to status type category created requires
777 A New Advanced Token Standard Jordi Baylina <jordi@baylina.cat>, Jacques Dafflon <jacques.dafflon@gmail.com>, Thomas Shababi <tom@truelevel.io> https://github.com/ethereum/EIPs/issues/777 Draft Standards Track ERC 2017-11-20 820

Simple Summary

This EIP defines standard interfaces and behaviors for token contracts.

The repository containing the reference implementation for this standard can be found at github.com/jacquesd/ERC777 and installed via npm with npm install erc777.

Abstract

This standard defines a new way to interact with a token contract while remaining backward compatible with ERC20.

It defines advanced features to interact with tokens. Namely, operators to send tokens on behalf of another address—contract or regular account—and send/receive hooks to offer token holders more control over their tokens.

It takes advantage of ERC820 to find out whether and where to notify contracts and regular addresses when they receive tokens as well as to allow compatibility with already-deployed contracts.

Motivation

This standard tries to improve the widely used ERC20 token standard. The main advantages of this standard are:

  1. Uses the same philosophy as Ether in that tokens are sent with send(dest, value, data).
  2. Both contracts and regular addresses can control and reject which token they send by registering a tokensToSend hook. (Rejection is done by throwing in the hook function.)
  3. Both contracts and regular addresses can control and reject which token they receive by registering a tokensReceived hook. (Rejection is done by throwing in the hook function.)
  4. The tokensReceived hook also avoids the double call needed in the ERC20 standard (approve/transferFrom) to send tokens to a contract.
  5. The token holder can "authorize" and "revoke" operators which can send tokens on their behalf. These operators are intended to be verified contracts such as an exchange, a cheque processor or an automatic charging system.
  6. Every token transaction contains a data bytes field and a similar operatorData to be used freely by the token holder and the operator respectively to pass data to the recipient.
  7. It is backward compatible with wallets that do not contain the tokensReceived hook function by deploying a proxy contract implementing the tokensReceived hook for the wallet.

Specification

ERC777Token (Token Contract)

interface ERC777Token {
    function name() public view returns (string);
    function symbol() public view returns (string);
    function totalSupply() public view returns (uint256);
    function balanceOf(address owner) public view returns (uint256);
    function granularity() public view returns (uint256);

    function defaultOperators() public view returns (address[]);
    function authorizeOperator(address operator) public;
    function revokeOperator(address operator) public;
    function isOperatorFor(address operator, address tokenHolder) public view returns (bool);

    function send(address to, uint256 amount, bytes data) public;
    function operatorSend(address from, address to, uint256 amount, bytes data, bytes operatorData) public;

    function burn(uint256 amount, bytes data) public;
    function operatorBurn(address from, uint256 amount, bytes data, bytes operatorData) public;

    event Sent(
        address indexed operator,
        address indexed from,
        address indexed to,
        uint256 amount,
        bytes data,
        bytes operatorData
    );
    event Minted(address indexed operator, address indexed to, uint256 amount, bytes operatorData);
    event Burned(address indexed operator, address indexed from, uint256 amount, bytes data, bytes operatorData);
    event AuthorizedOperator(address indexed operator, address indexed tokenHolder);
    event RevokedOperator(address indexed operator, address indexed tokenHolder);
}

The token contract MUST implement the above interface. The implementation MUST follow the specifications described below.

The token contract MUST register the ERC777Token interface with its own address via ERC820. If the contract has a switch to enable or disable ERC777 functions, every time the switch is triggered, the token MUST register or unregister the ERC777Token interface for its own address accordingly via ERC820. (Unregistering implies setting the address to 0x0.)

The basic unit token MUST be 1018 (i.e., ERC20's decimals MUST be 18). All functions MUST consider any amount of tokens (such as balances and amount to send, mint, or burn) in the basic unit.

View Functions

The view functions detailed below MUST be implemented.

name function

 function name() public view returns (string)

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

returns: Name of the token

symbol function

function symbol() public view returns (string)

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

returns: Symbol of the token

totalSupply function

function totalSupply() public view returns (uint256)

Get the total number of minted tokens.

The total supply MUST be equal to the sum of the balances of all addresses—as returned by the balanceOf function.

returns: Total supply of tokens currently in circulation.

balanceOf function

function balanceOf(address tokenHolder) public view returns (uint256)

Get the balance of the account with address tokenHolder.

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.

granularity function

function granularity() public view returns (uint256)

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

The following rules MUST be applied regarding the granularity:

  • The granularity value MUST be at creation time.
  • The granularity value MUST NOT be changed ever.
  • The granularity value MUST be greater or equal to 1.
  • Any minting, send or burning 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 throw.

NOTE: Most of the tokens SHOULD be fully partitionable. I.e., this function SHOULD return 1 unless there is a good reason for not allowing any partition of the token.

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

NOTE: defaultOperators and isOperatorFor are also view functions, defined under the operators for consistency.

ERC20 compatibility requirement:
The decimals of the token MUST always be 18. For a pure ERC777 token the ERC20 decimal function is OPTIONAL, and its existence SHALL NOT be relied upon when interacting with the token contract. (The decimal value of 18 is implied.) For an ERC20 enabled token, the decimal function is REQUIRED and MUST return 18.

ERC20 compatibility requirement:
The name, symbol, totalSupply, and balanceOf view functions MUST be backward compatible with ERC20.

Operators

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

When an address becomes an operator for a token holder, an AuthorizedOperator event MUST be emitted. The AuthorizedOperator's operator (topic 1) and tokenHolder (topic 2) MUST be the addresses of the operator and the token holder respectively.

When a token holder revokes an operator, a RevokedOperator event MUST be emitted. The RevokedOperator's operator (topic 1) and tokenHolder (topic 2) MUST be the addresses of the operator and the token holder respectively.

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

The token MAY define default operators. A default operator is an implicitly authorized operator for all token holders. AuthorizedOperator events MUST NOT be emitted when defining the default operators. The rules below apply to default operators:

  • The token contract MUST define default operators at creation time.
  • The default operators MUST be invariants. I.e., the token contract MUST NOT add or remove default operators ever.
  • AuthorizedOperator events MUST NOT be emitted when defining default operators.
  • A token holder MUST be allowed revoke a default operator (unless the token holder is the default operator in question).
  • A token holder MUST be allowed to re-authorize a previously revoked default operator.
  • When a default operator is explicitly authorized or revoked for a specific token holder, an AuthorizedOperator or RevokedOperator event (respectively) MUST be emitted.

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 operator MUST pass the data from a token holder as is when sending and burning tokens. If the token holder does not provide any data, the operator MUST pass either an empty data field or MUST NOT pass any data (depending on whether the function requires the token holder's data or not).

NOTE: It is not expected for the token contract to validate the origin of the data field when the operator and token holder are different. However, the operator MUST not pass any information in the data field which does not originate from the token holder.

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

defaultOperators function

function defaultOperators() public view returns (address[])

Get the list of default operators as defined by the token contract.

NOTE: If the token contract does not have any default operators, this function MUST return an empty list.

returns: List of addresses of all the default operators

authorizeOperator function

function authorizeOperator(address operator) public

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

An AuthorizedOperator event MUST be emitted on a successful call to this function.

NOTE: The token holder (msg.sender) is always an operator for itself. This right SHALL NOT be revoked. Hence this function MUST throw 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).

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

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

revokeOperator function

function revokeOperator(address operator) public

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

Revoke a third party operator's rights to send tokens on behalf of msg.sender.

A RevokedOperator event MUST be emitted on a successful call to this function.

NOTE: The token holder (msg.sender) is always an operator for itself. This right SHALL NOT be revoked. Hence this function MUST throw 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.

isOperatorFor function

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

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.

NOTE: To know which addresses are operators for a given token holder, one MUST call isOperatorFor with the token holder for each default operator and parse the AuthorizedOperator, and RevokedOperator events for the token holder in question.

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.
  • The token holder MAY communicate any information in the data.
  • The operator MAY communicate any information in the operatorData.
  • The token contract MUST NOT modify the data nor the operatorData.
  • Operators MUST NOT modify the data provided by token holders.
  • The token contract MUST call the tokensToSend hook of the token holder, provided the token holder registers an ERC777TokensRecipient implementation via ERC820.
  • The token contract MUST call the tokensReceived hook of the recipient, provided the recipient registers an ERC777TokensRecipient implementation via ERC820.
  • When calling tokensToSend, tokensReceived or both:
    • operator MUST be the address which initiated the send. (Generally the msg.sender of the send function call.)
    • from MUST be the address of the token holder.
    • to MUST be the address of the recipient.
    • data MUST be the data provided by the token holder.
    • operatorData MUST be the data provided by the operator.

The token contract MUST throw 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 has a granularity smaller than the granularity defined by the token contract.
  • The address of the token holder or the recipient is 0x0.
  • Any of the resulting balance 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.

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 and operatorData fields are free byte ranges provided by the token holder and the operator respectively. The data field is intended for the recipient. Typically, the recipient SHOULD indicate which data it expects to receive. (Similarly to the data field when sending ether.) The operatorData field 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

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

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 by the token holder (from)
operatorData: information attached to the send by the operator

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

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

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 by the token holder

operatorSend function

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

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

The operator MUST be msg.sender.

Reminder: If the operator address is not an authorized operator of the from address the send MUST throw.

NOTE: The operator MAY pass any information via operatorData. The operator MUST only pass data given to it by the token holder to the data parameter. The token holder MAY provide this data to the operator beforehand through another medium.

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
to: token recipient
amount: number of tokens to send
data: information attached to the send, previously provided by the token holder
operatorData: information attached to the send by the operator

Minting Tokens

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

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

  • 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.
  • The token contract MUST call the tokensReceived hook of the recipient, provided the recipient registers an ERC777TokensRecipient implementation via ERC820.
  • When calling tokensReceived:
    • operator MUST be the address which initiated the minting action. (Generally the msg.sender of the minting function call.)
    • from MUST be 0x0.
    • to MUST be the address of the recipient.
    • data MUST be empty.
    • operatorData MUST contain the data, provided by the address which initiated the minting action (i.e., the operator).

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

  • The resulting recipient balance after the mint has a granularity smaller than the granularity defined by the token contract.
  • The recipient is a contract, and it does not implement the ERC777TokensRecipient interface via ERC820.
  • The address of the recipient is 0x0.

ERC20 compatibility requirement:
While a Sent event MUST NOT be emitted when minting, if the token contract is ERC20 backward compatible, a Transfer event with the from parameter set to 0x0 SHOULD be emitted as defined in the ERC20 standard.

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.

Minted event

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

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
operatorData: information attached to the minting by the operator

Burning Tokens

Burning tokens is the act of destroying existing tokens. ERC777 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 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.
  • The token contract MUST call the tokensToSend hook of the token holder, provided the token holder registers an ERC777TokensSender implementation via ERC820.
  • When calling tokensToSend:
    • operator MUST be the address which initiated the burning action. (Generally the msg.sender of the burning function call.)
    • from MUST be the address of the token holder.
    • to MUST be 0x0.
    • data MUST be the data provided by the token holder.
    • operatorData MUST contain data, provided by the address which initiated the burning action (i.e., the operator).

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

  • The address initiating the burn of tokens from a token holder MUST be an operator of the token holder.

  • The resulting token holder balance after the burn has a granularity smaller than 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.

ERC20 compatibility requirement:
While a Sent event MUST NOT be emitted when burning, if the token contract is ERC20 enabled, a Transfer event with the to parameter set to 0x0 MUST be emitted as defined in the ERC20 standard.

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.

Burned event

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

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
amount: number of tokens burned
data: information attached to the burn by the token holder
operatorData: information attached to the burn by the operator

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

burn function

function burn(uint256 amount, bytes data) public;

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
data: information attached to the burn by the token holder

operatorBurn function

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

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

The operator MUST be msg.sender.

Reminder: If the operator address is not an authorized operator of the from address the burn MUST throw.

NOTE: The operator MAY pass any information via operatorData. The operator MUST only pass to data data given to it by the token holder. The token holder MAY provide this data to the operator beforehand through another medium.

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

ERC777TokensSender 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 ERC777TokensSender interface described below via ERC820.

interface ERC777TokensSender {
    function tokensToSend(
        address operator,
        address from,
        address to,
        uint value,
        bytes data,
        bytes operatorData
    ) public;
}

NOTE: A regular address MAY register a different address—the address of a contract—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

function tokensToSend(address operator, address from, address to, uint value, bytes data, bytes operatorData) public

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

The following rules apply to 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.
  • data MUST contain the extra information provided by the token holder (if any) for a send or 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 throwing. (I.e., reject the withdrawal of tokens from its account.)

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

NOTE: An address can register at most one implementation at any given time for all ERC777 tokens. Hence the ERC777TokensSender 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.

ERC20 compatibility requirement:
This 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.

ERC777TokensRecipient 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 debits from their address MAY register the address of a contract implementing the ERC777TokensSender interface described below via ERC820.

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

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

  • MUST throw 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 but said address MUST implement the interface on its behalf.

tokensReceived

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

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

The following rules apply to 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 update—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.
  • data MUST contain the extra information provided by the token holder (if any) for a send.
  • data MUST be empty for a mint.
  • 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 throwing. (I.e., reject the reception of tokens.)

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

NOTE: An address can register at most one implementation at any given time for all ERC777 tokens. Hence the ERC777TokensRecipient 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.

ERC20 compatibility requirement:
This 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.

Note On Gas Consumption

Dapps and wallets SHOULD first estimate the gas required when sending, minting, or burning tokens—using eth_estimateGas—to avoid running out of gas during the transaction.

Image  beige logo  white logo  light grey logo  dark grey logo  black logo
Color beige white light grey dark grey black
Hex #C99D66 #FFFFFF #EBEFF0 #3C3C3D #000000

The logo MAY be used, modified and adapted to promote valid ERC777 token implementations and ERC777 compliant technologies such as wallets and dapps.

ERC777 token contract authors MAY create a specific logo for their token based on this logo.

The logo MUST NOT be used to advertise, promote or associate in any way technology—such as tokens—which is not ERC777 compliant.

The logo for the standard can be found in the /assets/eip-777/logo folder in SVG and PNG formats. The PNG version of the logo offers a few sizes in pixels. If needed, other sizes MAY be created by converting from SVG into PNG.

Rationale

This standard solves some of the shortcomings of ERC20 while maintaining backward compatibility with ERC20. It avoids the problems and vulnerabilities of he EIP223.

It goes a step further by allowing operators (generally contracts) which can manage the tokens in the same way that the ERC20 with infinite approve was allowed. Finally, it adds hooks to provide further control to token holders over their tokens. Note that, the usage of ERC820 provides backward compatibility with wallets and existing contracts without having to be redeployed thanks proxy contracts implementing the hooks.

Backward Compatibility

This EIP does not introduce backward incompatibilities and is backward compatible with the older ERC20 token standard.

This EIP does not use transfer and transferFrom and uses send and operatorSend to avoid confusion and mistakes when deciphering which token standard is being used.

This standard allows the implementation of ERC20 functions transfer, transferFrom, approve and allowance alongside to make a token fully compatible with ERC20.

The token MAY implement decimals() for backward compatibility with ERC20. If implemented, it MUST always return 18.

Therefore a token contract MAY implement both ERC20 and ERC777 in parallel. The specification of the view functions (such as name, symbol, balanceOf, totalSupply) and internal data (such as the mapping of balances) overlap without problems. Note however that the following functions are mandatory in ERC777 and MUST be implemented: name, symbol balanceOf and totalSupply (decimal is not part of the ERC777 standard).

The state-modifying functions from both standards are decoupled and can operate independently from each other. Note that ERC20 functions SHOULD be limited to only being called from old contracts.

If the token implements ERC20, it MUST register the ERC20Token interface with its own address via ERC820. If the contract has a switch to enable or disable ERC20 functions, every time the switch is triggered, the token MUST register or unregister its own address accordingly the ERC20Token interface via ERC820. (Unregistering implies setting the address to 0x0.)

The difference for new contracts implementing ERC20 is that tokensToSend and tokensReceived hooks take precedence over ERC20. Even with an ERC20 transfer and transferFrom call, the token contract MUST check via ERC820 if the from and the to address implement tokensToSend and tokensReceived hook respectively. If any hook is implemented, it MUST be called. Note that when calling ERC20 transfer on a contract, if the contract does not implement tokensReceived, the transfer call SHOULD still be accepted even if this means the tokens will probably be locked.

The table below summarizes the different actions the token contract MUST take when sending, minting and transferring token via ERC777 and ERC20:

ERC820 to address ERC777 Sending And Minting ERC20 transfer/transferFrom
ERC777TokensRecipient
registered
regular address MUST call tokensReceived
contract
ERC777TokensRecipient
not registered
regular address SHOULD accept SHOULD accept
contract MUST throw

There is no particular action to take if tokensToSend is not implemented. The transfer MUST proceed and only be canceled if another condition is not respected such as lack of funds or a throw in tokensReceived (if present).

During a send, mint and burn, the respective Sent, Minted and Burned events MUST be emitted. Furthermore, if the token contract declares that it implements ERC20Token via ERC820, the token contract SHOULD emit a Transfer event for minting and MUST emit a Transfer event for sending and burning (as specified in the ERC20 standard). During an ERC20's transfer or transferFrom functions, a valid Sent event MUST be emitted.

Hence for any movement of tokens, two events MAY be emitted: an ERC20 Transfer and an ERC777 Sent, Minted or Burned (depending on the type of movement). Third-party developers MUST be careful not to consider both events as separate movements. As a general rule, if an application considers the token as an ERC20 token, then only the Transfer event MUST be taken into account. If the application considers the token as an ERC777 token, then only the Sent, Minted and Burned events MUST be considered.

Test Cases

The repository with the reference implementation contains all the tests.

Implementation

The GitHub repository jacquesd/ERC777 contains the reference implementation.

Copyright and related rights waived via CC0.