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
This commit is contained in:
Jacques Dafflon 2018-07-31 17:35:49 +02:00 committed by EIP Automerge Bot
parent fc6d56f495
commit b41669ede2

View File

@ -1,7 +1,7 @@
---
eip: 777
title: A New Advanced Token Standard
author: Jordi Baylina <jordi@baylina.cat>, Jacques Dafflon <jacques.dafflon@gmail.com>, Thomas Shababi <tom@truelevel.io>
author: Jacques Dafflon <jacques.dafflon@gmail.com>, Jordi Baylina <jordi@baylina.cat>, Thomas Shababi <tom@truelevel.io>
discussions-to: https://github.com/ethereum/EIPs/issues/777
status: Draft
type: Standards Track
@ -14,8 +14,6 @@ requires: 820
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][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].
@ -29,11 +27,11 @@ It takes advantage of [ERC820] to find out whether and where to notify contracts
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.
2. Both contracts and regular addresses can control and reject which token they send by registering a `tokensToSend` hook. (Rejection is done by `revert`ing 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 `revert`ing in the hook function.)
4. 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.
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.
6. Every token transaction contains a `data` bytes field and a similar `operatorData` to be used freely 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
@ -67,8 +65,8 @@ interface ERC777Token {
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 Minted(address indexed operator, address indexed to, uint256 amount, bytes data, bytes operatorData);
event Burned(address indexed operator, address indexed from, uint256 amount, bytes operatorData);
event AuthorizedOperator(address indexed operator, address indexed tokenHolder);
event RevokedOperator(address indexed operator, address indexed tokenHolder);
}
@ -91,7 +89,7 @@ The `view` functions detailed below MUST be implemented.
Returns the name of the token, e.g., `"MyToken"`.
> <small>**returns:** Name of the token</small>
> <small>**returns:** Name of the token.</small>
**`symbol` function**
@ -101,7 +99,7 @@ function symbol() public view returns (string)
Returns the symbol of the token, e.g., `"MYT"`.
> <small>**returns:** Symbol of the token</small>
> <small>**returns:** Symbol of the token.</small>
**`totalSupply` function**
@ -113,6 +111,8 @@ Get the total number of minted tokens.
The total supply MUST be equal to the sum of the balances of all addresses&mdash;as returned by the `balanceOf` function.
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.
> <small>**returns:** Total supply of tokens currently in circulation.</small>
**`balanceOf` function**
@ -126,7 +126,7 @@ Get the balance of the account with address `tokenHolder`.
The balance MUST be zero (`0`) or higher.
> <small>**parameters**</small>
> <small>`tokenHolder`: Address for which the balance is returned</small>
> <small>`tokenHolder`: Address for which the balance is returned.</small>
>
> <small>**returns:** Amount of token held by `tokenHolder` in the token contract.</small>
@ -144,7 +144,7 @@ The following rules MUST be applied regarding the *granularity*:
- 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.
- 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 partition of the token.
@ -182,9 +182,43 @@ 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).
- 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*: 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*.
*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.
**`AuthorizedOperator` event** <a id="authorizedoperator"></a>
``` solidity
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.
> <small>**parameters**</small>
> <small>`operator`: Address which became an *operator* of `tokenHolder`.</small>
> <small>`tokenHolder`: Address of a token holder which authorized the `operator` address as an *operator*.</small>
**`RevokedOperator` event** <a id="revokedoperator"></a>
``` solidity
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.
> <small>**parameters**</small>
> <small>`operator`: Address which was revoked as an *operator* of `tokenHolder`.</small>
> <small>`tokenHolder`: Address of a token holder which revoked the `operator` address as an *operator*.</small>
The `defaultOperators`, `authorizeOperator`, `revokeOperator` and `isOperatorFor` functions described below MUST be implemented to manage *operators*.
Token contracts MAY implement other functions to manage *operators*.
@ -199,7 +233,7 @@ 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.
> <small>**returns:** List of addresses of all the *default operators*
> <small>**returns:** List of addresses of all the *default operators*.
**`authorizeOperator` function**
@ -207,36 +241,26 @@ Get the list of *default operators* as defined by the token contract.
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.
Set a third party `operator` address as an *operator* of `msg.sender` to send and burn 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.
*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`).
> <small>**parameters**</small>
> <small>`operator`: Address to set as an *operator* for `msg.sender`.</small>
**`revokeOperator` function**
``` solidity
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.
Remove the right of the `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`.
*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`).
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`).
> <small>**parameters**</small>
> <small>**parameters**</small>
> <small>`operator`: Address to rescind as an *operator* for `msg.sender`.</small>
**`isOperatorFor` function** <a id="isOperatorFor"></a>
``` solidity
@ -247,7 +271,7 @@ Indicate whether the `operator` address is an *operator* of the `tokenHolder` ad
> <small>**parameters**</small>
> <small>`operator`: Address which may be an *operator* of `tokenHolder`.</small>
> <small>`tokenHolder`: Address of a token holder which may have the `operator` address as an `operator`.</small>
> <small>`tokenHolder`: Address of a token holder which may have the `operator` address as an *operator*.</small>
>
> <small>**returns:** `true` if `operator` is an *operator* of `tokenHolder` and `false` otherwise.
@ -262,29 +286,21 @@ When an *operator* sends an `amount` of tokens from a *token holder* to a *recip
- 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`&mdash;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 *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 call the `tokensToSend` hook of the *token holder* if the *token holder* registers an `ERC777TokensRecipient` implementation via [ERC820].
- The token contract MUST call the `tokensReceived` hook of the *recipient* if the *recipient* registers an `ERC777TokensRecipient` implementation via [ERC820].
- The `data` and `operatorData` MUST be immutable during the entire send process&mdash;hence the same `data` and `operatorData` MUST be used to call both hooks and emit the `Sent` event.
The token contract MUST throw when sending in any of the following cases:
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 has a granularity smaller than the *granularity* defined by the token contract.
- 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 balance becomes negative, i.e. becomes less than zero (`0`).
- 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 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 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.
@ -294,14 +310,14 @@ The token contract MAY send tokens from many *token holders* to many *recipients
*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` 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`.
*NOTE*: The `data` field contains extra information intended for, and defined by the recipient&mdash; 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** <a id="sent"></a>
@ -314,12 +330,12 @@ Indicate a send of `amount` of tokens from the `from` address to the `to` addres
*NOTE*: This event MUST NOT be emitted outside of a send or an [ERC20] transfer process.
> <small>**parameters**</small>
> <small>`operator`: address which triggered the send</small>
> <small>`from`: token holder</small>
> <small>`to`: token recipient</small>
> <small>`amount`: number of tokens to send</small>
> <small>`data`: information attached to the send by the token holder (`from`)</small>
> <small>`operatorData`: information attached to the send by the `operator`</small>
> <small>`operator`: Address which triggered the send.</small>
> <small>`from`: Token holder.</small>
> <small>`to`: Token recipient.</small>
> <small>`amount`: Number of tokens to send.</small>
> <small>`data`: Information attached to the send, and intended for the recipient (`to`).</small>
> <small>`operatorData`: Information attached to the send by the `operator`.</small>
The `send` and `operatorSend` functions described below MUST be implemented to send tokens.
Token contracts MAY implement other functions to send tokens.
@ -337,9 +353,9 @@ 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`.
> <small>**parameters**</small>
> <small>`to`: token recipient</small>
> <small>`amount`: number of tokens to send</small>
> <small>`data`: information attached to the send by the *token holder*</small>
> <small>`to`: Token recipient.</small>
> <small>`amount`: Number of tokens to send.</small>
> <small>`data`: Information attached to the send, and intended for the recipient (`to`).</small>
**`operatorSend` function**
@ -349,20 +365,18 @@ function operatorSend(address from, address to, uint256 amount, bytes data, byte
Send the `amount` of tokens on behalf of the address `from` to the address `to`.
The *operator* MUST be `msg.sender`.
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 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.
*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).
> <small>**parameters**</small>
> <small>`from`: token holder</small>
> <small>`to`: token recipient</small>
> <small>`amount`: number of tokens to send</small>
> <small>`data`: information attached to the send, previously provided by the *token holder*</small>
> <small>`operatorData`: information attached to the send by the `operator`</small>
> <small>`from`: Token holder (or `0x0` to set `from` to `msg.sender`).</small>
> <small>`to`: Token recipient.</small>
> <small>`amount`: Number of tokens to send.</small>
> <small>`data`: Information attached to the send, and intended for the recipient (`to`).</small>
> <small>`operatorData`: Information attached to the send by the `operator`.</small>
#### **Minting Tokens**
@ -375,17 +389,12 @@ Nonetheless, the rules below MUST be respected when minting for a *recipient*:
- 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*, 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 call the `tokensReceived` hook of the *recipient* if the *recipient* registers an `ERC777TokensRecipient` implementation via [ERC820].
- The `data` and `operatorData` MUST be immutable during the entire mint process&mdash;hence the same `data` and `operatorData` MUST be used to call the `tokensReceived` hook and emit the `Minted` event.
The token contract MUST throw when minting in any of the following cases:
The token contract MUST `revert` 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 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`.
@ -399,10 +408,14 @@ The token contract MAY mint tokens for multiple *recipients* at once. In this ca
- 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&mdash; 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** <a id="minted"></a>
``` solidity
event Minted(address indexed operator, address indexed to, uint256 amount, bytes operatorData)
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.
@ -410,10 +423,11 @@ Indicate the minting of `amount` of tokens to the `to` address by the `operator`
*NOTE*: This event MUST NOT be emitted outside of a mint process.
> <small>**parameters**</small>
> <small>`operator`: address which triggered the mint</small>
> <small>`to`: token recipient</small>
> <small>`amount`: number of tokens minted</small>
> <small>`operatorData`: information attached to the minting by the `operator`</small>
> <small>`operator`: Address which triggered the mint.</small>
> <small>`to`: Token recipient.</small>
> <small>`amount`: Number of tokens minted.</small>
> <small>`data`: Information attached to the minting, and intended for the recipient (`to`).</small>
> <small>`operatorData`: Information attached to the minting by the `operator`.</small>
#### **Burning Tokens**
@ -426,24 +440,19 @@ The rules below MUST be respected when burning the tokens of a *token holder*:
- 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*, 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 call the `tokensToSend` hook of the *token holder* if the *token holder* registers an `ERC777TokensSender` implementation via [ERC820].
- The `operatorData` MUST be immutable during the entire burn process&mdash;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 throw when burning in any of the following cases:
The token contract MUST `revert` 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 *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` MUST be emitted as defined in the [ERC20] standard.
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:
@ -452,10 +461,12 @@ The token contract MAY burn tokens for multiple *token holders* at once. In this
- 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** <a id="burned"></a>
``` solidity
event Burned(address indexed operator, address indexed from, uint256 amount, bytes data, bytes operatorData)
event Burned(address indexed operator, address indexed from, uint256 amount, bytes operatorData)
```
Indicate the burning of `amount` of tokens from the `from` address by the `operator` address.
@ -463,11 +474,10 @@ Indicate the burning of `amount` of tokens from the `from` address by the `opera
*NOTE*: This event MUST NOT be emitted outside of a burn process.
> <small>**parameters**</small>
> <small>`operator`: address which triggered the burn</small>
> <small>`from`: token holder</small>
> <small>`amount`: number of tokens burned</small>
> <small>`data`: information attached to the burn by the token holder</small>
> <small>`operatorData`: information attached to the burn by the `operator`</small>
> <small>`operator`: Address which triggered the burn.</small>
> <small>`from`: Token holder whose tokens are burned.</small>
> <small>`amount`: Number of tokens burned.</small>
> <small>`operatorData`: Information attached to the burn by the `operator`.</small>
The `burn` and `operatorBurn` functions described below MUST be implemented to burn tokens.
Token contracts MAY implement other functions to burn tokens.
@ -475,7 +485,7 @@ Token contracts MAY implement other functions to burn tokens.
**`burn` function**
``` solidity
function burn(uint256 amount, bytes data) public;
function burn(uint256 amount) public;
```
Burn the `amount` of tokens from the address `msg.sender`.
@ -483,22 +493,26 @@ Burn the `amount` of tokens from the address `msg.sender`.
The *operator* and the *token holder* MUST both be the `msg.sender`.
> <small>**parameters**</small>
> <small>`amount`: number of tokens to burn</small>
> <small>`data`: information attached to the burn by the token holder</small>
> <small>`amount`: Number of tokens to burn.</small>
**`operatorBurn` function**
``` solidity
function operatorBurn(address from, uint256 amount, bytes data, bytes operatorData) public;
function operatorBurn(address from, uint256 amount, bytes operatorData) public;
```
Burn the `amount` of tokens on behalf of the address `from`.
The *operator* MUST be `msg.sender`.
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 the burn MUST throw.
*Reminder*: If the *operator* address is not an authorized operator of the `from` address, then the burn process MUST `revert`.
*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.
> <small>**parameters**</small>
> <small>`from`: Token holder whose tokens will be burned (or `0x0` to set `from` to `msg.sender`).</small>
> <small>`amount`: Number of tokens to burn.</small>
> <small>`operatorData`: Information attached to the burn by the *operator*.</small>
*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).
@ -512,7 +526,7 @@ interface ERC777TokensSender {
address operator,
address from,
address to,
uint value,
uint256 amount,
bytes data,
bytes operatorData
) public;
@ -524,7 +538,7 @@ interface ERC777TokensSender {
**`tokensToSend`**
``` solidity
function tokensToSend(address operator, address from, address to, uint value, bytes data, bytes operatorData) public
function tokensToSend(address operator, address from, address to, uint256 amount, 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.
@ -532,14 +546,14 @@ Notify a send or burn (if `to` is `0x0`) of `amount` tokens from the `from` addr
*NOTE*: This function MUST NOT be called outside of a burn, send or [ERC20] transfer process.
> <small>**parameters**</small>
> <small>`operator`: address which triggered the balance decrease (through sending or burning)</small>
> <small>`from`: *token holder*</small>
> <small>`to`: *token recipient* for a send and `0x` for a burn</small>
> <small>`amount`: number of tokens the *token holder* balance is decreased by</small>
> <small>`data`: extra information provided by the *token holder*</small>
> <small>`operatorData`: extra information provided by the address which triggered the balance decrease</small>
> <small>`operator`: Address which triggered the balance decrease (through sending or burning).</small>
> <small>`from`: *token holder*.</small>
> <small>`to`: *token recipient* for a send and `0x` for a burn.</small>
> <small>`amount`: Number of tokens the *token holder* balance is decreased by.</small>
> <small>`data`: Extra information provided by the *token holder*.</small>
> <small>`operatorData`: Extra information provided by the address which triggered the balance decrease.</small>
The following rules apply to the `tokensToSend` hook:
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&mdash;i.e. *before* the balance is decremented.
@ -547,9 +561,11 @@ The following rules apply to the `tokensToSend` hook:
- `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.
- `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 throwing. (I.e., reject the withdrawal of tokens from its account.)
- 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`.
@ -560,7 +576,7 @@ This hook takes precedence over [ERC20] and MUST be called (if registered) when
#### **`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].
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].
``` solidity
interface ERC777TokensRecipient {
@ -568,7 +584,7 @@ interface ERC777TokensRecipient {
address operator,
address from,
address to,
uint amount,
uint256 amount,
bytes data,
bytes operatorData
) public;
@ -577,7 +593,7 @@ interface ERC777TokensRecipient {
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.
- 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&mdash;the address of a contract&mdash;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.
@ -585,7 +601,7 @@ If the *recipient* is a contract, which has not registered an `ERC777TokensRecip
**`tokensReceived`**
``` solidity
function tokensReceived(address operator, address from, address to, uint amount, bytes data, bytes operatorData) public
function tokensReceived(address operator, address from, address to, uint256 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.
@ -593,25 +609,25 @@ Notify a send or mint (if `from` is `0x0`) of `amount` tokens from the `from` ad
*NOTE*: This function MUST NOT be called outside of a mint, send or [ERC20] transfer process.
> <small>**parameters**</small>
> <small>`operator`: address which triggered the balance increase (through sending or minting)</small>
> <small>`from`: *token holder* for a send and `0x` for a mint</small>
> <small>`to`: *token recipient*</small>
> <small>`amount`: number of tokens the *recipient* balance is increased by</small>
> <small>`data`: extra information provided by the *token holder* for a send and nothing (empty bytes) for a mint,</small>
> <small>`operatorData`: extra information provided by the address which triggered the balance increase</small>
> <small>`operator`: Address which triggered the balance increase (through sending or minting).</small>
> <small>`from`: *token holder* for a send and `0x` for a mint.</small>
> <small>`to`: *token recipient*.</small>
> <small>`amount`: Number of tokens the *recipient* balance is increased by.</small>
> <small>`data`: Extra information provided by the *token holder* for a send and nothing (empty bytes) for a mint,.</small>
> <small>`operatorData`: Extra information provided by the address which triggered the balance increase.</small>
The following rules apply to the `tokensToSend` hook:
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 update&mdash;i.e. *after* the balance is incremented.
- The `tokensReceived` hook MUST be called *after* the state is updated&mdash;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.
- `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 throwing. (I.e., reject the reception of tokens.)
- 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`.
@ -640,7 +656,7 @@ The logo for the standard can be found in the [`/assets/eip-777/logo`][logos] fo
## 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].
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.
@ -660,8 +676,7 @@ The state-modifying functions from both standards are decoupled and can operate
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 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]:
@ -695,13 +710,13 @@ The table below summarizes the different actions the token contract MUST take wh
</tr>
<tr>
<td>contract</td>
<td align="center">MUST throw</td>
<td align="center">MUST <code>revert</code></td>
</tr>
</table>
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).
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 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.
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.
@ -711,7 +726,8 @@ The [repository with the reference implementation][jacquesd/ERC777] contains all
## Implementation
The GitHub repository [jacquesd/ERC777] contains the [reference implementation].
The GitHub repository [jacquesd/ERC777] contains the [reference implementation]. The reference implementation is also available via [npm][npm/erc777] and can be installed with `npm install erc777`.
## Copyright
@ -724,11 +740,14 @@ Copyright and related rights waived via [CC0].
[ERC777]: https://eips.ethereum.org/EIPS/eip-777
[ERC820]: https://eips.ethereum.org/EIPS/eip-820
[jacquesd/ERC777]: https://github.com/jacquesd/ERC777
[npm/erc777]: https://www.npmjs.com/package/erc777
[ref tests]: https://github.com/jacquesd/ERC777/blob/master/test/ReferenceToken.test.js
[reference implementation]: https://github.com/jacquesd/ERC777/blob/master/contracts/examples/ReferenceToken.sol
[EIP223]: https://github.com/ethereum/EIPs/issues/223
[eth_estimateGas]: https://github.com/ethereum/wiki/wiki/JSON-RPC#eth_estimategas
[authorizedoperator]: #authorizedoperator
[revokedoperator]: #revokedoperator
[isOperatorFor]: #isOperatorFor
[defaultOperators]: #defaultOperators
[sent]: #sent