eip | title | author | discussions-to | status | type | category | created | requires |
---|---|---|---|---|---|---|---|---|
777 |
A New Advanced Token Standard |
Jacques Dafflon <[email protected]>, Jordi Baylina <[email protected]>, Thomas Shababi <[email protected]> |
Draft |
Standards Track |
ERC |
2017-11-20 |
820 |
This EIP defines standard interfaces and behaviors for token contracts.
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.
This standard tries to improve the widely used ERC20 token standard. The main advantages of this standard are:
- Uses the same philosophy as Ether in that tokens are sent with
send(dest, value, data)
. - Both contracts and regular addresses can control and reject which token they send by registering a
tokensToSend
hook. (Rejection is done byrevert
ing in the hook function.) - Both contracts and regular addresses can control and reject which token they receive by registering a
tokensReceived
hook. (Rejection is done byrevert
ing in the hook function.) - The
tokensReceived
hook allows to send tokens to a contract and notify it in a single transaction, unlike ERC20 which require a double call (approve
/transferFrom
) to achieve this. - 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.
- Every token transaction contains
data
andoperatorData
bytes fields to be used freely to pass data from the token holder and the operator, respectively. - It is backward compatible with wallets that do not contain the
tokensReceived
hook function by deploying a proxy contract implementing thetokensReceived
hook for the wallet.
interface ERC777Token {
function name() external view returns (string);
function symbol() external view returns (string);
function totalSupply() external view returns (uint256);
function balanceOf(address owner) external view returns (uint256);
function granularity() external view returns (uint256);
function defaultOperators() external view returns (address[]);
function authorizeOperator(address operator) external;
function revokeOperator(address operator) external;
function isOperatorFor(address operator, address tokenHolder) external view returns (bool);
function send(address to, uint256 amount, bytes data) external;
function operatorSend(address from, address to, uint256 amount, bytes data, bytes operatorData) external;
function burn(uint256 amount, bytes data) external;
function operatorBurn(address from, uint256 amount, bytes data, bytes operatorData) external;
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 data, 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 smallest unit—for all interactions with the token contract—MUST be 1
. I.e. all amounts and balances MUST be unsigned integers. The display denomination—to display any amount to the end user—MUST be 1018 of the smallest.
In other words the technical denomination is similar to a wei and the display denomination is similar to an ether. It is equivalent to an ERC20's decimals
function returning 18
. E.g. if a token contract holds a balance of 500,000,000,000,000,000
(0.5×1018) for a user, the user interface SHOULD show 0.5
tokens to the user. If the user wishes to send 0.3
tokens, the contract MUST be called with an amount of 300,000,000,000,000,000
(0.3×1018).
The view
functions detailed below MUST be implemented.
name
function
function name() external view returns (string)
Returns the name of the token, e.g., "MyToken"
.
returns: Name of the token.
symbol
function
function symbol() external view returns (string)
Returns the symbol of the token, e.g., "MYT"
.
returns: Symbol of the token.
totalSupply
function
function totalSupply() external view returns (uint256)
Get the total number of minted tokens.
NOTE: The total supply MUST be equal to the sum of the balances of all addresses—as returned by the balanceOf
function.
NOTE: The total supply MUST be equal to the sum of all the minted tokens as defined in all the Minted
events minus the sum of all the burned tokens as defined in all the Burned
events. (This applies as well to tokens minted when the token contract is created.)
returns: Total supply of tokens currently in circulation.
balanceOf
function
function balanceOf(address tokenHolder) external 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() external view returns (uint256)
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 or burned at any time.
The following rules MUST be applied regarding the granularity:
- The granularity value MUST be set 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
revert
.
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 fraction 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 compatible token, the decimal
function is REQUIRED and MUST return 18
. (In ERC20, the decimals
function is OPTIONAL. If the function is not present, the decimals
value is not clearly defined and may be assumed to be 0
. Hence for compatibility reasons, decimals
MUST be implemented for ERC20 compatible tokens.)
ERC20 compatibility requirement:
The name
, symbol
, totalSupply
, and balanceOf
view
functions MUST be backward compatible with ERC20.
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
orRevokedOperator
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 returntrue
. - If an address is not an operator for a token holder,
isOperatorFor
MUST returnfalse
. - 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 theAuthorizedOperator
Event. - 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 theRevokedOperator
Event.
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.
NOTE: A token holder MAY have multiple operators at the same time.
event AuthorizedOperator(address indexed operator, address indexed tokenHolder)
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 oftokenHolder
.
tokenHolder
: Address of a token holder which authorized theoperator
address as an operator.
event RevokedOperator(address indexed operator, address indexed tokenHolder)
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 oftokenHolder
.
tokenHolder
: Address of a token holder which revoked theoperator
address as an operator.
The defaultOperators
, authorizeOperator
, revokeOperator
and isOperatorFor
functions described below MUST be implemented to manage operators.
Token contracts MAY implement other functions to manage operators.
function defaultOperators() external 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) external
Set a third party operator
address as an operator of msg.sender
to send and burn tokens on its behalf.
NOTE: The token holder (msg.sender
) is always an operator for itself. This right SHALL NOT be revoked. Hence 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 formsg.sender
.
revokeOperator
function
function revokeOperator(address operator) external
Remove the right of the operator
address to be an operator for msg.sender
and to send and burn tokens on its behalf.
NOTE: The token holder (msg.sender
) is always an operator for itself. This right SHALL NOT be revoked. Hence 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 formsg.sender
.
function isOperatorFor(address operator, address tokenHolder) external view returns (bool)
Indicate whether the operator
address is an operator of the tokenHolder
address.
parameters
operator
: Address which may be an operator oftokenHolder
.
tokenHolder
: Address of a token holder which may have theoperator
address as an operator.returns:
true
ifoperator
is an operator oftokenHolder
andfalse
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.
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 theSent
Event. - The operator MAY include information in the
operatorData
. - The token contract MUST call the
tokensToSend
hook of the token holder if the token holder registers anERC777TokensSender
implementation via ERC820. - The token contract MUST call the
tokensReceived
hook of the recipient if the recipient registers anERC777TokensRecipient
implementation via ERC820. - The
data
andoperatorData
MUST be immutable during the entire send process—hence the samedata
andoperatorData
MUST be used to call both hooks and emit theSent
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 sentamount
.
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 finallytokensReceived
MUST be called afterward. Thus abalanceOf
call withintokensToSend
returns the balance of the address before the send and abalanceOf
call withintokensReceived
returns the balance of the address after the send.
NOTE: The data
field contains information provided by the token holder—similar
to the data field in a regular ether send transaction.
The tokensToSend()
hook, the tokensReceived()
, or both
MAY use the information to decide if they wish to reject the transaction.
NOTE: The operatorData
field is analogous to the data
field except it SHALL be provided by the operator.
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
.
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 provided by the token holder.
operatorData
: Information provided 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) external
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 provided by the token holder.
operatorSend
function
function operatorSend(address from, address to, uint256 amount, bytes data, bytes operatorData) external
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 (or0x0
to setfrom
tomsg.sender
).
to
: Token recipient.
amount
: Number of tokens to send.
data
: Information provided by the token holder.
operatorData
: Information provided by the operator.
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 theMinted
Event. - The token contract MUST call the
tokensReceived
hook of the recipient if the recipient registers anERC777TokensRecipient
implementation via ERC820. - The
data
andoperatorData
MUST be immutable during the entire mint process—hence the samedata
andoperatorData
MUST be used to call thetokensReceived
hook and emit theMinted
event. - The
data
field MUST be empty.
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
ERC777TokensRecipient
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(es) receiving the initial supply. This means one or more Minted
events must be emitted and the tokensReceived
hook of the recipient(s) MUST be called.
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 mintedamount
.
NOTE: Minting an amount of zero (0
) tokens is valid and MUST be treated as a regular mint.
NOTE: The operatorData
field contains information provided by the operator—similar
to the data field in a regular ether send transaction.
The tokensReceived()
hooks MAY use the information to decide if it wish to reject the transaction.
event Minted(address indexed operator, address indexed to, uint256 amount, bytes data, 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.
data
: Information provided by the token holder.
operatorData
: Information provided by the operator.
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 theBurned
Event. - The token contract MUST call the
tokensToSend
hook of the token holder if the token holder registers anERC777TokensSender
implementation via ERC820. - The
operatorData
MUST be immutable during the entire burn process—hence the sameoperatorData
MUST be used to call thetokensToSend
hook and emit theBurned
event.
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
.
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
SHOULD be emitted. The ERC20 standard does not define the concept of burning tokens, but this is a commonly accepted practice.
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 burnedamount
.
NOTE: Burning an amount of zero (0
) tokens is valid and MUST be treated as a regular burn.
NOTE: The data
field contains information provided by the token holder—similar
to the data field in a regular ether send transaction.
The tokensToSend()
hook, the tokensReceived()
, or both
MAY use the information to decide if they wish to reject the transaction.
NOTE: The operatorData
field is analogous to the data
field except it SHALL be provided by the operator.
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 whose tokens are burned.
amount
: Number of tokens burned.data
: Information provided by the token holder.
operatorData
: Information provided 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) external;
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 provided by the token holder.
operatorBurn
function
function operatorBurn(address from, uint256 amount, bytes data, bytes operatorData) external;
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 (or0x0
to setfrom
tomsg.sender
).
amount
: Number of tokens to burn.
data
: Information provided by the token holder.
operatorData
: Information provided by the operator.
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).
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,
uint256 amount,
bytes data,
bytes operatorData
) external;
}
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, uint256 amount, bytes data, bytes operatorData) external
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 and0x
for a burn.
amount
: Number of tokens the token holder balance is decreased by.
data
: Information provided by the token holder.
operatorData
: Information provided by the operator.
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 be0x0
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.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
revert
ing. (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.
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 ERC777TokensSender
interface described below via ERC820.
interface ERC777TokensRecipient {
function tokensReceived(
address operator,
address from,
address to,
uint256 amount,
bytes data,
bytes operatorData
) external;
}
If the recipient is a contract, which has not registered an ERC777TokensRecipient
implementation; the token contract:
- MUST
revert
if thetokensReceived
hook is called from a mint or send call. - SHOULD accept if the
tokensReceived
hook is called from an ERC20transfer
ortransferFrom
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, uint256 amount, bytes data, bytes operatorData) external
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 and0x
for a mint.
to
: token recipient.
amount
: Number of tokens the recipient balance is increased by.
data
: Information provided by the token holder.operatorData
: Information provided by the operator.
The following rules apply when calling the tokensReceived
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 be0x0
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
revert
ing. (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.
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 | |||||
---|---|---|---|---|---|
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
.
This standard solves some of the shortcomings of ERC20 while maintaining backward compatibility with ERC20. It avoids the problems and vulnerabilities of 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.
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 revert |
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 revert
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 burning and MUST emit a Transfer
event for sending (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.
The repository with the reference implementation contains all the tests.
The GitHub repository jacquesd/ERC777 contains the reference implementation. The reference implementation is also available via npm and can be installed with npm install erc777
.
Copyright and related rights waived via CC0.