forked from DecentralizedClimateFoundation/DCIPs
1279 lines
55 KiB
Markdown
1279 lines
55 KiB
Markdown
|
---
|
||
|
eip: 777
|
||
|
title: Token Standard
|
||
|
author: Jacques Dafflon <mail@0xjac.com>, Jordi Baylina <jordi@baylina.cat>, Thomas Shababi <tom@truelevel.io>
|
||
|
discussions-to: https://github.com/ethereum/EIPs/issues/777
|
||
|
status: Final
|
||
|
type: Standards Track
|
||
|
category: ERC
|
||
|
created: 2017-11-20
|
||
|
requires: 1820
|
||
|
---
|
||
|
|
||
|
## Simple Summary
|
||
|
|
||
|
This EIP defines standard interfaces and behaviors for token contracts.
|
||
|
|
||
|
## Abstract
|
||
|
|
||
|
This standard defines a new way to interact with a token contract while remaining backward compatible with [ERC-20].
|
||
|
|
||
|
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 [ERC-1820] 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 upon the widely used [ERC-20] 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 `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 [ERC-20] which requires a double call (`approve`/`transferFrom`) to achieve this.
|
||
|
|
||
|
5. The 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 `data` and `operatorData` bytes fields
|
||
|
to be used freely to pass data from the holder and the operator, respectively.
|
||
|
|
||
|
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)
|
||
|
|
||
|
``` solidity
|
||
|
interface ERC777Token {
|
||
|
function name() external view returns (string memory);
|
||
|
function symbol() external view returns (string memory);
|
||
|
function totalSupply() external view returns (uint256);
|
||
|
function balanceOf(address holder) external view returns (uint256);
|
||
|
function granularity() external view returns (uint256);
|
||
|
|
||
|
function defaultOperators() external view returns (address[] memory);
|
||
|
function isOperatorFor(
|
||
|
address operator,
|
||
|
address holder
|
||
|
) external view returns (bool);
|
||
|
function authorizeOperator(address operator) external;
|
||
|
function revokeOperator(address operator) external;
|
||
|
|
||
|
function send(address to, uint256 amount, bytes calldata data) external;
|
||
|
function operatorSend(
|
||
|
address from,
|
||
|
address to,
|
||
|
uint256 amount,
|
||
|
bytes calldata data,
|
||
|
bytes calldata operatorData
|
||
|
) external;
|
||
|
|
||
|
function burn(uint256 amount, bytes calldata data) external;
|
||
|
function operatorBurn(
|
||
|
address from,
|
||
|
uint256 amount,
|
||
|
bytes calldata data,
|
||
|
bytes calldata 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 holder
|
||
|
);
|
||
|
event RevokedOperator(address indexed operator, address indexed holder);
|
||
|
}
|
||
|
```
|
||
|
|
||
|
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 [ERC-1820].
|
||
|
|
||
|
> This is done by calling the `setInterfaceImplementer` function on the [ERC-1820] registry
|
||
|
> with the token contract address as both the address and the implementer
|
||
|
> and the `keccak256` hash of `ERC777Token` (`0xac7fbab5f54a3ca8194167523c6753bfeb96a445279294b6125b68cce2177054`)
|
||
|
> as the interface hash.
|
||
|
|
||
|
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 ERC1820.
|
||
|
Unregistering implies calling the `setInterfaceImplementer` with the token contract address as the address,
|
||
|
the `keccak256` hash of `ERC777Token` as the interface hash and `0x0` as the implementer.
|
||
|
(See [Set An Interface For An Address][erc1820-set] in [ERC-1820] for more details.)
|
||
|
|
||
|
When interacting with the token contract, all amounts and balances MUST be unsigned integers.
|
||
|
I.e. internally, all values are stored as a denomination of 1E-18 of a token.
|
||
|
The display denomination—to display any amount to the end user—MUST
|
||
|
be 10<sup>18</sup> of the internal denomination.
|
||
|
|
||
|
In other words, the internal denomination is similar to a wei
|
||
|
and the display denomination is similar to an ether.
|
||
|
It is equivalent to an [ERC-20]'s `decimals` function returning `18`.
|
||
|
E.g. if a token contract returns a balance of `500,000,000,000,000,000` (0.5×10<sup>18</sup>) for a user,
|
||
|
the user interface MUST 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×10<sup>18</sup>).
|
||
|
|
||
|
User Interfaces which are generated programmatically from the ABI of the token contract
|
||
|
MAY use and display the internal denomination.
|
||
|
But this MUST be made clear, for example by displaying the `uint256` type.
|
||
|
|
||
|
#### **View Functions**
|
||
|
|
||
|
The `view` functions detailed below MUST be implemented.
|
||
|
|
||
|
**`name` function**
|
||
|
|
||
|
``` solidity
|
||
|
function name() external view returns (string memory)
|
||
|
```
|
||
|
|
||
|
Get the name of the token, e.g., `"MyToken"`.
|
||
|
|
||
|
> <small>**identifier:** `06fdde03`</small>
|
||
|
> <small>**returns:** Name of the token.</small>
|
||
|
|
||
|
**`symbol` function**
|
||
|
|
||
|
``` solidity
|
||
|
function symbol() external view returns (string memory)
|
||
|
```
|
||
|
|
||
|
Get the symbol of the token, e.g., `"MYT"`.
|
||
|
|
||
|
> <small>**identifier:** `95d89b41`</small>
|
||
|
> <small>**returns:** Symbol of the token.</small>
|
||
|
|
||
|
**`totalSupply` function**
|
||
|
|
||
|
``` solidity
|
||
|
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.
|
||
|
|
||
|
> <small>**identifier:** `18160ddd`</small>
|
||
|
> <small>**returns:** Total supply of tokens currently in circulation.</small>
|
||
|
|
||
|
**`balanceOf` function**
|
||
|
|
||
|
``` solidity
|
||
|
function balanceOf(address holder) external view returns (uint256)
|
||
|
```
|
||
|
|
||
|
Get the balance of the account with address `holder`.
|
||
|
|
||
|
The balance MUST be zero (`0`) or higher.
|
||
|
|
||
|
> <small>**identifier:** `70a08231`</small>
|
||
|
> <small>**parameters**</small>
|
||
|
> <small>`holder`: Address for which the balance is returned.</small>
|
||
|
>
|
||
|
> <small>**returns:** Amount of tokens held by `holder` in the token contract.</small>
|
||
|
|
||
|
**`granularity` function**
|
||
|
|
||
|
``` solidity
|
||
|
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 amount of tokens (in the internal denomination)
|
||
|
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 than or equal to `1`.
|
||
|
|
||
|
- All balances MUST be a multiple of the granularity.
|
||
|
|
||
|
- Any amount of tokens (in the internal denomination) minted, sent or burned
|
||
|
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 tokens SHOULD be fully partition-able.
|
||
|
I.e., this function SHOULD return `1` unless there is a good reason for not allowing any fraction of the token.
|
||
|
|
||
|
> <small>**identifier:** `556f0dc7`</small>
|
||
|
> <small>**returns:** The smallest non-divisible part of the token.</small>
|
||
|
|
||
|
*NOTE*: [`defaultOperators`][defaultOperators] and [`isOperatorFor`][isOperatorFor] are also `view` functions,
|
||
|
defined under the [operators] for consistency.
|
||
|
|
||
|
*[ERC-20] compatibility requirement*:
|
||
|
The decimals of the token MUST always be `18`.
|
||
|
For a *pure* ERC777 token the [ERC-20] `decimals` 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 [ERC-20] compatible token, the `decimals` function is REQUIRED and MUST return `18`.
|
||
|
(In [ERC-20], 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 [ERC-20] compatible tokens.)
|
||
|
|
||
|
#### **Operators**
|
||
|
|
||
|
An `operator` is an address which is allowed to send and burn tokens on behalf of some *holder*.
|
||
|
|
||
|
When an address becomes an *operator* for a *holder*, an `AuthorizedOperator` event MUST be emitted.
|
||
|
The `AuthorizedOperator`'s `operator` (topic 1) and `holder` (topic 2)
|
||
|
MUST be the addresses of the *operator* and the *holder* respectively.
|
||
|
|
||
|
When a *holder* revokes an *operator*, a `RevokedOperator` event MUST be emitted.
|
||
|
The `RevokedOperator`'s `operator` (topic 1) and `holder` (topic 2)
|
||
|
MUST be the addresses of the *operator* and the *holder* respectively.
|
||
|
|
||
|
*NOTE*: A *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 *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 *holder* MUST be allowed to revoke a *default operator*
|
||
|
(unless the *holder* is the *default operator* in question).
|
||
|
|
||
|
- A *holder* MUST be allowed to re-authorize a previously revoked *default operator*.
|
||
|
|
||
|
- When a *default operator* is explicitly authorized or revoked for a specific *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 *holder*, `isOperatorFor` MUST return `true`.
|
||
|
|
||
|
- If an address is not an *operator* for a *holder*, `isOperatorFor` MUST return `false`.
|
||
|
|
||
|
- The token contract MUST emit an `AuthorizedOperator` event with the correct values
|
||
|
when a *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 *holder* revokes an address as its *operator* as defined in the
|
||
|
[`RevokedOperator` Event][revokedoperator].
|
||
|
|
||
|
*NOTE*: A *holder* MAY authorize an already authorized *operator*.
|
||
|
An `AuthorizedOperator` MUST be emitted each time.
|
||
|
|
||
|
*NOTE*: A *holder* MAY revoke an already revoked *operator*.
|
||
|
A `RevokedOperator` MUST be emitted each time.
|
||
|
|
||
|
**`AuthorizedOperator` event** <a id="authorizedoperator"></a>
|
||
|
|
||
|
``` solidity
|
||
|
event AuthorizedOperator(address indexed operator, address indexed holder)
|
||
|
```
|
||
|
|
||
|
Indicates the authorization of `operator` as an *operator* for `holder`.
|
||
|
|
||
|
*NOTE*: This event MUST NOT be emitted outside of an *operator* authorization process.
|
||
|
|
||
|
> <small>**parameters**</small>
|
||
|
> <small>`operator`: Address which became an *operator* of `holder`.</small>
|
||
|
> <small>`holder`: Address of a *holder* which authorized the `operator` address as an *operator*.</small>
|
||
|
|
||
|
**`RevokedOperator` event** <a id="revokedoperator"></a>
|
||
|
|
||
|
``` solidity
|
||
|
event RevokedOperator(address indexed operator, address indexed holder)
|
||
|
```
|
||
|
|
||
|
Indicates the revocation of `operator` as an *operator* for `holder`.
|
||
|
|
||
|
*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 `holder`.</small>
|
||
|
> <small>`holder`: Address of a *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*.
|
||
|
|
||
|
**`defaultOperators` function** <a id="defaultOperators"></a>
|
||
|
|
||
|
``` solidity
|
||
|
function defaultOperators() external view returns (address[] memory)
|
||
|
```
|
||
|
|
||
|
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>**identifier:** `06e48538`</small>
|
||
|
> <small>**returns:** List of addresses of all the *default operators*.
|
||
|
|
||
|
**`authorizeOperator` function**
|
||
|
|
||
|
``` solidity
|
||
|
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 *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 holder (`msg.sender`)
|
||
|
as an *operator* for itself (i.e. if `operator` is equal to `msg.sender`).
|
||
|
|
||
|
> <small>**identifier:** `959b8c3f`</small>
|
||
|
> <small>**parameters**</small>
|
||
|
> <small>`operator`: Address to set as an *operator* for `msg.sender`.</small>
|
||
|
|
||
|
**`revokeOperator` function**
|
||
|
|
||
|
``` solidity
|
||
|
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 *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 holder (`msg.sender`)
|
||
|
as an *operator* for itself (i.e., if `operator` is equal to `msg.sender`).
|
||
|
|
||
|
> <small>**identifier:** `fad8b32a`</small>
|
||
|
> <small>**parameters**</small>
|
||
|
> <small>`operator`: Address to rescind as an *operator* for `msg.sender`.</small>
|
||
|
|
||
|
**`isOperatorFor` function** <a id="isOperatorFor"></a>
|
||
|
|
||
|
``` solidity
|
||
|
function isOperatorFor(
|
||
|
address operator,
|
||
|
address holder
|
||
|
) external view returns (bool)
|
||
|
```
|
||
|
|
||
|
Indicate whether the `operator` address is an *operator* of the `holder` address.
|
||
|
|
||
|
> <small>**identifier:** `d95b6371`</small>
|
||
|
> <small>**parameters**</small>
|
||
|
> <small>`operator`: Address which may be an *operator* of `holder`.</small>
|
||
|
> <small>`holder`: Address of a *holder* which may have the `operator` address as an *operator*.</small>
|
||
|
>
|
||
|
> <small>**returns:** `true` if `operator` is an *operator* of `holder` and `false` otherwise.
|
||
|
|
||
|
*NOTE*: To know which addresses are *operators* for a given *holder*,
|
||
|
one MUST call `isOperatorFor` with the *holder* for each *default operator*
|
||
|
and parse the `AuthorizedOperator`, and `RevokedOperator` events for the *holder* in question.
|
||
|
|
||
|
#### **Sending Tokens**
|
||
|
|
||
|
When an *operator* sends an `amount` of tokens from a *holder* to a *recipient*
|
||
|
with the associated `data` and `operatorData`, the token contract MUST apply the following rules:
|
||
|
|
||
|
- Any authorized *operator* MAY send tokens to any *recipient* (except to `0x0`).
|
||
|
|
||
|
- The balance of the *holder* MUST be decreased by the `amount`.
|
||
|
|
||
|
- The balance of the *recipient* MUST be increased by the `amount`.
|
||
|
|
||
|
- The balance of the *holder* MUST be greater or equal to the `amount`—such
|
||
|
that its resulting balance is greater or equal to zero (`0`) after the send.
|
||
|
|
||
|
- The token contract MUST emit a `Sent` event with the correct values as defined in the [`Sent` Event][sent].
|
||
|
|
||
|
- The *operator* MAY include information in the `operatorData`.
|
||
|
|
||
|
- The token contract MUST call the `tokensToSend` hook of the *holder*
|
||
|
if the *holder* registers an `ERC777TokensSender` implementation via [ERC-1820].
|
||
|
|
||
|
- The token contract MUST call the `tokensReceived` hook of the *recipient*
|
||
|
if the *recipient* registers an `ERC777TokensRecipient` implementation via [ERC-1820].
|
||
|
|
||
|
- The `data` and `operatorData` MUST be immutable during the entire send process—hence
|
||
|
the same `data` and `operatorData` MUST be used to call both hooks and emit the `Sent` event.
|
||
|
|
||
|
The token contract MUST `revert` when sending in any of the following cases:
|
||
|
|
||
|
- The *operator* address is not an authorized operator for the *holder*.
|
||
|
|
||
|
- The resulting *holder* balance or *recipient* balance after the send
|
||
|
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 [ERC-1820].
|
||
|
|
||
|
- The address of the *holder* or the *recipient* is `0x0`.
|
||
|
|
||
|
- Any of the resulting balances becomes negative, i.e. becomes less than zero (`0`).
|
||
|
|
||
|
- The `tokensToSend` hook of the *holder* `revert`s.
|
||
|
|
||
|
- The `tokensReceived` hook of the *recipient* `revert`s.
|
||
|
|
||
|
The token contract MAY send tokens from many *holders*, to many *recipients*, or both. In this case:
|
||
|
|
||
|
- The previous send rules MUST apply to all the *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 *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*: Movements of tokens MAY be chained.
|
||
|
For example, if a contract upon receiving tokens sends them further to another address.
|
||
|
In this case, the previous send rules apply to each send, in order.
|
||
|
|
||
|
*NOTE*: Sending an amount of zero (`0`) tokens is valid and MUST be treated as a regular send.
|
||
|
|
||
|
*Implementation Requirement*:
|
||
|
- The token contract MUST call the `tokensToSend` hook *before* updating the state.
|
||
|
- The token contract MUST call the `tokensReceived` hook *after* updating the state.
|
||
|
I.e., `tokensToSend` MUST be called first,
|
||
|
then the balances MUST be updated to reflect the send,
|
||
|
and finally `tokensReceived` MUST be called *afterward*.
|
||
|
Thus a `balanceOf` call within `tokensToSend` returns the balance of the address *before* the send
|
||
|
and a `balanceOf` call within `tokensReceived` returns the balance of the address *after* the send.
|
||
|
|
||
|
*NOTE*: The `data` field contains information provided by the *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*.
|
||
|
|
||
|
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>
|
||
|
|
||
|
``` solidity
|
||
|
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 [ERC-20] transfer process.
|
||
|
|
||
|
> <small>**parameters**</small>
|
||
|
> <small>`operator`: Address which triggered the send.</small>
|
||
|
> <small>`from`: *Holder* whose tokens were sent.</small>
|
||
|
> <small>`to`: Recipient of the tokens.</small>
|
||
|
> <small>`amount`: Number of tokens sent.</small>
|
||
|
> <small>`data`: Information provided by the *holder*.</small>
|
||
|
> <small>`operatorData`: Information provided 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.
|
||
|
|
||
|
**`send` function**
|
||
|
|
||
|
``` solidity
|
||
|
function send(address to, uint256 amount, bytes calldata data) external
|
||
|
```
|
||
|
|
||
|
Send the `amount` of tokens from the address `msg.sender` to the address `to`.
|
||
|
|
||
|
The *operator* and the *holder* MUST both be the `msg.sender`.
|
||
|
|
||
|
> <small>**identifier:** `9bd9bbc6`</small>
|
||
|
> <small>**parameters**</small>
|
||
|
> <small>`to`: Recipient of the tokens.</small>
|
||
|
> <small>`amount`: Number of tokens to send.</small>
|
||
|
> <small>`data`: Information provided by the *holder*.</small>
|
||
|
|
||
|
**`operatorSend` function**
|
||
|
|
||
|
``` solidity
|
||
|
function operatorSend(
|
||
|
address from,
|
||
|
address to,
|
||
|
uint256 amount,
|
||
|
bytes calldata data,
|
||
|
bytes calldata operatorData
|
||
|
) external
|
||
|
```
|
||
|
|
||
|
Send the `amount` of tokens on behalf of the address `from` to the address `to`.
|
||
|
|
||
|
*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>**identifier:** `62ad1b83`</small>
|
||
|
> <small>**parameters**</small>
|
||
|
> <small>`from`: *Holder* whose tokens are being sent.</small>
|
||
|
> <small>`to`: Recipient of the tokens.</small>
|
||
|
> <small>`amount`: Number of tokens to send.</small>
|
||
|
> <small>`data`: Information provided by the *holder*.</small>
|
||
|
> <small>`operatorData`: Information provided by the *operator*.</small>
|
||
|
|
||
|
#### **Minting Tokens**
|
||
|
|
||
|
Minting tokens is the act of producing new tokens.
|
||
|
[ERC-777] intentionally does not define specific functions to mint tokens.
|
||
|
This intent comes from the wish not to limit the use of the [ERC-777] 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 (except `0x0`).
|
||
|
|
||
|
- The total supply MUST be increased by the amount of tokens minted.
|
||
|
|
||
|
- The balance of `0x0` MUST NOT be decreased.
|
||
|
|
||
|
- The balance of the *recipient* MUST be increased by the amount of tokens minted.
|
||
|
|
||
|
- The token contract MUST emit a `Minted` event with the correct values as defined in the [`Minted` Event][minted].
|
||
|
|
||
|
- The token contract MUST call the `tokensReceived` hook of the *recipient*
|
||
|
if the *recipient* registers an `ERC777TokensRecipient` implementation via [ERC-1820].
|
||
|
|
||
|
- The `data` and `operatorData` MUST be immutable during the entire mint process—hence
|
||
|
the same `data` and `operatorData` MUST be used to call the `tokensReceived` hook and emit the `Minted` event.
|
||
|
|
||
|
The token contract MUST `revert` when minting in any of the following cases:
|
||
|
|
||
|
- The resulting *recipient* balance after the mint is not a multiple of the *granularity* defined by the token contract.
|
||
|
- The *recipient* is a contract, and it does not implement the `ERC777TokensRecipient` interface via [ERC-1820].
|
||
|
- The address of the *recipient* is `0x0`.
|
||
|
- The `tokensReceived` hook of the *recipient* `revert`s.
|
||
|
|
||
|
*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.
|
||
|
|
||
|
*[ERC-20] compatibility requirement*:
|
||
|
While a `Sent` event MUST NOT be emitted when minting,
|
||
|
if the token contract is [ERC-20] backward compatible,
|
||
|
a `Transfer` event with the `from` parameter set to `0x0` SHOULD be emitted as defined in the [ERC-20] 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`.
|
||
|
|
||
|
*NOTE*: Minting an amount of zero (`0`) tokens is valid and MUST be treated as a regular mint.
|
||
|
|
||
|
*NOTE*: While during a send or a burn, the data is provided by the *holder*, it is inapplicable for a mint.
|
||
|
In this case the data MAY be provided by the token contract or the *operator*,
|
||
|
for example to ensure a successful minting to a *holder* expecting specific data.
|
||
|
|
||
|
*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.
|
||
|
|
||
|
**`Minted` event** <a id="minted"></a>
|
||
|
|
||
|
``` solidity
|
||
|
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.
|
||
|
|
||
|
> <small>**parameters**</small>
|
||
|
> <small>`operator`: Address which triggered the mint.</small>
|
||
|
> <small>`to`: Recipient of the tokens.</small>
|
||
|
> <small>`amount`: Number of tokens minted.</small>
|
||
|
> <small>`data`: Information provided for the *recipient*.</small>
|
||
|
> <small>`operatorData`: Information provided by the *operator*.</small>
|
||
|
|
||
|
#### **Burning Tokens**
|
||
|
|
||
|
Burning tokens is the act of destroying existing tokens.
|
||
|
[ERC-777] 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 *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 *holder*:
|
||
|
|
||
|
- Tokens MAY be burned from any *holder* address (except `0x0`).
|
||
|
|
||
|
- The total supply MUST be decreased by the amount of tokens burned.
|
||
|
|
||
|
- The balance of `0x0` MUST NOT be increased.
|
||
|
|
||
|
- The balance of the *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 *holder*
|
||
|
if the *holder* registers an `ERC777TokensSender` implementation via [ERC-1820].
|
||
|
|
||
|
- The `operatorData` MUST be immutable during the entire burn process—hence
|
||
|
the same `operatorData` MUST be used to call the `tokensToSend` hook and emit the `Burned` event.
|
||
|
|
||
|
The token contract MUST `revert` when burning in any of the following cases:
|
||
|
|
||
|
- The *operator* address is not an authorized operator for the *holder*.
|
||
|
|
||
|
- The resulting *holder* balance after the burn is not a multiple of the *granularity*
|
||
|
defined by the token contract.
|
||
|
|
||
|
- The balance of *holder* is inferior to the amount of tokens to burn
|
||
|
(i.e., resulting in a negative balance for the *holder*).
|
||
|
|
||
|
- The address of the *holder* is `0x0`.
|
||
|
|
||
|
- The `tokensToSend` hook of the *holder* `revert`s.
|
||
|
|
||
|
*[ERC-20] compatibility requirement*:
|
||
|
While a `Sent` event MUST NOT be emitted when burning;
|
||
|
if the token contract is [ERC-20] enabled, a `Transfer` event with the `to` parameter set to `0x0` SHOULD be emitted.
|
||
|
The [ERC-20] standard does not define the concept of burning tokens, but this is a commonly accepted practice.
|
||
|
|
||
|
The token contract MAY burn tokens for multiple *holders* at once. In this case:
|
||
|
|
||
|
- The previous burn rules MUST apply to each *holders*.
|
||
|
- The sum of all the balances decremented MUST be equal to the total burned amount.
|
||
|
- A `Burned` event MUST be emitted for every *holder* with the corresponding amount for each *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.
|
||
|
|
||
|
*NOTE*: The `data` field contains information provided by the 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*.
|
||
|
|
||
|
**`Burned` event** <a id="burned"></a>
|
||
|
|
||
|
``` solidity
|
||
|
event Burned(
|
||
|
ddress 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.
|
||
|
|
||
|
> <small>**parameters**</small>
|
||
|
> <small>`operator`: Address which triggered the burn.</small>
|
||
|
> <small>`from`: *Holder* whose tokens were burned.</small>
|
||
|
> <small>`amount`: Number of tokens burned.</small>
|
||
|
> <small>`data`: Information provided by the *holder*.</small>
|
||
|
> <small>`operatorData`: Information provided 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.
|
||
|
|
||
|
**`burn` function**
|
||
|
|
||
|
``` solidity
|
||
|
function burn(uint256 amount, bytes calldata data) external
|
||
|
```
|
||
|
|
||
|
Burn the `amount` of tokens from the address `msg.sender`.
|
||
|
|
||
|
The *operator* and the *holder* MUST both be the `msg.sender`.
|
||
|
|
||
|
> <small>**identifier:** `fe9d9303`</small>
|
||
|
> <small>**parameters**</small>
|
||
|
> <small>`amount`: Number of tokens to burn.</small>
|
||
|
> <small>`data`: Information provided by the *holder*.</small>
|
||
|
|
||
|
**`operatorBurn` function**
|
||
|
|
||
|
``` solidity
|
||
|
function operatorBurn(
|
||
|
address from,
|
||
|
uint256 amount,
|
||
|
bytes calldata data,
|
||
|
bytes calldata operatorData
|
||
|
) external
|
||
|
```
|
||
|
|
||
|
Burn the `amount` of tokens on behalf of the address `from`.
|
||
|
|
||
|
*Reminder*: If the *operator* address is not an authorized operator of the `from` address,
|
||
|
then the burn process MUST `revert`.
|
||
|
|
||
|
> <small>**identifier:** `fc673c4f`</small>
|
||
|
> <small>**parameters**</small>
|
||
|
> <small>`from`: *Holder* whose tokens will be burned.</small>
|
||
|
> <small>`amount`: Number of tokens to burn.</small>
|
||
|
> <small>`data`: Information provided by the *holder*.</small>
|
||
|
> <small>`operatorData`: Information provided 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).
|
||
|
|
||
|
#### **`ERC777TokensSender` And The `tokensToSend` Hook**
|
||
|
|
||
|
The `tokensToSend` hook notifies of any request to decrement the balance (send and burn) for a given *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 [ERC-1820].
|
||
|
|
||
|
> This is done by calling the `setInterfaceImplementer` function on the [ERC-1820] registry
|
||
|
> with the *holder* address as the address,
|
||
|
> the `keccak256` hash of `ERC777TokensSender`
|
||
|
> (`0x29ddb589b1fb5fc7cf394961c1adf5f8c6454761adf795e67fe149f658abe895`) as the interface hash,
|
||
|
> and the address of the contract implementing the `ERC777TokensSender` as the implementer.
|
||
|
|
||
|
``` solidity
|
||
|
interface ERC777TokensSender {
|
||
|
function tokensToSend(
|
||
|
address operator,
|
||
|
address from,
|
||
|
address to,
|
||
|
uint256 amount,
|
||
|
bytes calldata userData,
|
||
|
bytes calldata 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`**
|
||
|
|
||
|
``` solidity
|
||
|
function tokensToSend(
|
||
|
address operator,
|
||
|
address from,
|
||
|
address to,
|
||
|
uint256 amount,
|
||
|
bytes calldata userData,
|
||
|
bytes calldata operatorData
|
||
|
) external
|
||
|
```
|
||
|
|
||
|
Notify a request to send or burn (if `to` is `0x0`) an `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 [ERC-20] transfer process.
|
||
|
|
||
|
> <small>**identifier:** `75ab9782`</small>
|
||
|
> <small>**parameters**</small>
|
||
|
> <small>`operator`: Address which triggered the balance decrease (through sending or burning).</small>
|
||
|
> <small>`from`: *Holder* whose tokens were sent.</small>
|
||
|
> <small>`to`: Recipient of the tokens for a send (or `0x0` for a burn).</small>
|
||
|
> <small>`amount`: Number of tokens the *holder* balance is decreased by.</small>
|
||
|
> <small>`data`: Information provided by the *holder*.</small>
|
||
|
> <small>`operatorData`: Information provided by the *operator*.</small>
|
||
|
|
||
|
The following rules apply when calling the `tokensToSend` hook:
|
||
|
|
||
|
- The `tokensToSend` hook MUST be called for every send and burn processes.
|
||
|
|
||
|
- 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 send or burn process.
|
||
|
|
||
|
- `from` MUST be the address of the *holder* whose tokens are sent or burned.
|
||
|
|
||
|
- `to` MUST be the address of the *recipient* which receives the tokens for a send.
|
||
|
|
||
|
- `to` MUST be `0x0` for a burn.
|
||
|
|
||
|
- `amount` MUST be the number of tokens the *holder* sent or burned.
|
||
|
|
||
|
- `data` MUST contain the extra information (if any) provided to the send or the burn process.
|
||
|
|
||
|
- `operatorData` MUST contain the extra information provided by the address
|
||
|
which triggered the decrease of the balance (if any).
|
||
|
|
||
|
- The *holder* MAY block a send or burn process by `revert`ing.
|
||
|
(I.e., reject the withdrawal of tokens from its account.)
|
||
|
|
||
|
*NOTE*: Multiple *holders* MAY use the same implementation of `ERC777TokensSender`.
|
||
|
|
||
|
*NOTE*: An address can register at most one implementation at any given time for all [ERC-777] 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.
|
||
|
|
||
|
*[ERC-20] compatibility requirement*:
|
||
|
This hook takes precedence over [ERC-20] and MUST be called (if registered)
|
||
|
when calling [ERC-20]'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 credits to their address
|
||
|
MAY register the address of a contract implementing the `ERC777TokensRecipient` interface described below via [ERC-1820].
|
||
|
|
||
|
> This is done by calling the `setInterfaceImplementer` function on the [ERC-1820] registry
|
||
|
> with the *recipient* address as the address,
|
||
|
> the `keccak256` hash of `ERC777TokensRecipient`
|
||
|
> (`0xb281fc8c12954d22544db45de3159a39272895b169a852b314f9cc762e44c53b`) as the interface hash,
|
||
|
> and the address of the contract implementing the `ERC777TokensRecipient` as the implementer.
|
||
|
|
||
|
``` solidity
|
||
|
interface ERC777TokensRecipient {
|
||
|
function tokensReceived(
|
||
|
address operator,
|
||
|
address from,
|
||
|
address to,
|
||
|
uint256 amount,
|
||
|
bytes calldata data,
|
||
|
bytes calldata operatorData
|
||
|
) external;
|
||
|
}
|
||
|
```
|
||
|
|
||
|
If the *recipient* is a contract, which has not registered an `ERC777TokensRecipient` implementation;
|
||
|
then the token contract:
|
||
|
|
||
|
- MUST `revert` if the `tokensReceived` hook is called from a mint or send call.
|
||
|
|
||
|
- SHOULD continue processing the transaction
|
||
|
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`**
|
||
|
|
||
|
``` solidity
|
||
|
function tokensReceived(
|
||
|
address operator,
|
||
|
address from,
|
||
|
address to,
|
||
|
uint256 amount,
|
||
|
bytes calldata data,
|
||
|
bytes calldata 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 [ERC-20] transfer process.
|
||
|
|
||
|
> <small>**identifier:** `0023de29`</small>
|
||
|
> <small>**parameters**</small>
|
||
|
> <small>`operator`: Address which triggered the balance increase (through sending or minting).</small>
|
||
|
> <small>`from`: *Holder* whose tokens were sent (or `0x0` for a mint).</small>
|
||
|
> <small>`to`: Recipient of the tokens.</small>
|
||
|
> <small>`amount`: Number of tokens the *recipient* balance is increased by.</small>
|
||
|
> <small>`data`: Information provided by the *holder*.</small>
|
||
|
> <small>`operatorData`: Information provided by the *operator*.</small>
|
||
|
|
||
|
The following rules apply when calling the `tokensReceived` hook:
|
||
|
|
||
|
- The `tokensReceived` hook MUST be called for every send and mint processes.
|
||
|
|
||
|
- 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 send or mint process.
|
||
|
|
||
|
- `from` MUST be the address of the *holder* whose tokens are sent for a send.
|
||
|
|
||
|
- `from` MUST be `0x0` for a mint.
|
||
|
|
||
|
- `to` MUST be the address of the *recipient* which receives the tokens.
|
||
|
|
||
|
- `amount` MUST be the number of tokens the *recipient* sent or minted.
|
||
|
|
||
|
- `data` MUST contain the extra information (if any) provided to the send or the mint process.
|
||
|
|
||
|
- `operatorData` MUST contain the extra information provided by the address
|
||
|
which triggered the increase of the balance (if any).
|
||
|
|
||
|
- The *holder* MAY block a send or mint process by `revert`ing.
|
||
|
(I.e., reject the reception of tokens.)
|
||
|
|
||
|
*NOTE*: Multiple *holders* MAY use the same implementation of `ERC777TokensRecipient`.
|
||
|
|
||
|
*NOTE*: An address can register at most one implementation at any given time for all [ERC-777] 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.
|
||
|
|
||
|
*[ERC-20] compatibility requirement*:
|
||
|
This hook takes precedence over [ERC-20] and MUST be called (if registered)
|
||
|
when calling [ERC-20]'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`][eth_estimateGas]—to avoid running out of gas during the transaction.
|
||
|
|
||
|
### Logo
|
||
|
|
||
|
| **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 [ERC-777] token implementations
|
||
|
and [ERC-777] compliant technologies such as wallets and dapps.
|
||
|
|
||
|
[ERC-777] 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 [ERC-777] compliant.
|
||
|
|
||
|
The logo for the standard can be found in the [`/assets/eip-777/logo`][logos] 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
|
||
|
|
||
|
The principal intent for this standard is
|
||
|
to solve some of the shortcomings of [ERC-20] while maintaining backward compatibility with [ERC-20],
|
||
|
and avoiding the problems and vulnerabilities of [EIP-223].
|
||
|
|
||
|
Below are the rationales for the decisions regarding the main aspects of the standards.
|
||
|
|
||
|
*NOTE*: Jacques Dafflon ([0xjac]), one of the authors of the standard,
|
||
|
conjointly wrote his [master thesis] on the standard,
|
||
|
which goes in more details than could reasonably fit directly within the standard,
|
||
|
and can provide further clarifications regarding certain aspects or decisions.
|
||
|
|
||
|
### Lifecycle
|
||
|
|
||
|
More than just sending tokens, [ERC-777] defines the entire lifecycle of a token,
|
||
|
starting with the minting process, followed by the sending process and terminating with the burn process.
|
||
|
|
||
|
Having a lifecycle clearly defined is important for consistency and accuracy,
|
||
|
especially when value is derived from scarcity.
|
||
|
In contrast when looking at some [ERC-20] tokens, a discrepancy can be observed
|
||
|
between the value returned by the `totalSupply` and the actual circulating supply,
|
||
|
as the standard does not clearly define a process to create and destroy tokens.
|
||
|
|
||
|
### Data
|
||
|
|
||
|
The mint, send and burn processes can all make use of a `data` and `operatorData` fields
|
||
|
which are passed to any movement (mint, send or burn).
|
||
|
Those fields may be empty for simple use cases,
|
||
|
or they may contain valuable information related to the movement of tokens,
|
||
|
similar to information attached to a bank transfer by the sender or the bank itself.
|
||
|
|
||
|
The use of a `data` field is equally present in other standard proposals such as [EIP-223],
|
||
|
and was requested by multiple members of the community who reviewed this standard.
|
||
|
|
||
|
### Hooks
|
||
|
|
||
|
In most cases, [ERC-20] requires two calls to safely transfer tokens to a contract without locking them.
|
||
|
A call from the sender, using the `approve` function
|
||
|
and a call from the recipient using `transferFrom`.
|
||
|
Furthermore, this requires extra communication between the parties which is not clearly defined.
|
||
|
Finally, holders can get confused between `transfer` and `approve`/`transferFrom`.
|
||
|
Using the former to transfer tokens to a contract will most likely result in locked tokens.
|
||
|
|
||
|
Hooks allow streamlining of the sending process and offer a single way to send tokens to any recipient.
|
||
|
Thanks to the `tokensReceived` hook, contracts are able to react and prevent locking tokens upon reception.
|
||
|
|
||
|
#### **Greater Control For Holders**
|
||
|
|
||
|
The `tokensReceived` hook also allows holders to reject the reception of some tokens.
|
||
|
This gives greater control to holders who can accept or reject incoming tokens based on some parameters,
|
||
|
for example located in the `data` or `operatorData` fields.
|
||
|
|
||
|
Following the same intentions and based on suggestions from the community,
|
||
|
the `tokensToSend` hook was added to give control over and prevent the movement of outgoing tokens.
|
||
|
|
||
|
#### **[ERC-1820] Registry**
|
||
|
|
||
|
The [ERC-1820] Registry allows holders to register their hooks.
|
||
|
Other alternatives were examined beforehand to link hooks and holders.
|
||
|
|
||
|
The first was for hooks to be defined at the sender's or recipient's address.
|
||
|
This approach is similar to [EIP-223] which proposes a `tokenFallback` function on recipient contracts
|
||
|
to be called when receiving tokens,
|
||
|
but improves on it by relying on [ERC-165] for interface detection.
|
||
|
While straightforward to implement, this approach imposes several limitations.
|
||
|
In particular, the sender and recipient must be contracts in order to provide their implementation of the hooks.
|
||
|
Preventing externally owned addresses to benefit from hooks.
|
||
|
Existing contracts have a strong probability not to be compatible,
|
||
|
as they undoubtedly were unaware and do not define the new hooks.
|
||
|
Consequently existing smart contract infrastructure such as multisig wallets
|
||
|
which potentially hold large amounts of ether and tokens would need to be migrated to new updated contracts.
|
||
|
|
||
|
The second approach considered was to use [ERC-672] which offered pseudo-introspection for addresses using reverse-ENS.
|
||
|
However, this approach relied heavily on ENS, on top of which reverse lookup would need to be implemented.
|
||
|
Analysis of this approach promptly revealed a certain degree of complexity and security concerns
|
||
|
which would transcend the benefits of approach.
|
||
|
|
||
|
The third solution—used in this standard—is to rely on a unique registry
|
||
|
where any address can register the addresses of contracts implementing the hooks on its behalf.
|
||
|
This approach has the advantage that externally owned accounts and contracts can benefit from hooks,
|
||
|
including existing contracts which can rely on hooks deployed on proxy contracts.
|
||
|
|
||
|
The decision was made to keep this registry in a separate EIP,
|
||
|
as to not over complicate this standard.
|
||
|
More importantly, the registry is designed in a flexible fashion,
|
||
|
such that other EIPs and smart contract infrastructures can benefit from it
|
||
|
for their own use cases, outside the realm of [ERC-777] and tokens.
|
||
|
The first proposal for this registry was [ERC-820].
|
||
|
Unfortunately, issues emanating from upgrades in the Solidity language to versions 0.5 and above
|
||
|
resulted in a bug in a separated part of the registry, which required changes.
|
||
|
This was discovered right after the last call period.
|
||
|
Attempts made to avoid creating a separate EIP, such as [ERC820a], were rejected.
|
||
|
Hence the standard for the registry used for [ERC-777] became [ERC-1820].
|
||
|
[ERC-1820] and [ERC-820] are functionally equivalent. [ERC-1820] simply contains the fix for newer versions of Solidity.
|
||
|
|
||
|
### Operators
|
||
|
|
||
|
The standard defines the concept of operators as any address which moves tokens.
|
||
|
While intuitively every address moves its own tokens,
|
||
|
separating the concepts of holder and operator allows for greater flexibility.
|
||
|
Primarily, this originates from the fact that the standard defines a mechanism for holders
|
||
|
to let other addresses become their operators.
|
||
|
Moreover, unlike the approve calls in [ERC-20] where the role of an approved address is not clearly defined,
|
||
|
[ERC-777] details the intent of and interactions with operators,
|
||
|
including an obligation for operators to be approved,
|
||
|
and an irrevocable right for any holder to revoke operators.
|
||
|
|
||
|
#### **Default Operators**
|
||
|
|
||
|
Default operators were added based on community demand for pre-approved operators.
|
||
|
That is operators which are approved for all holders by default.
|
||
|
For obvious security reasons, the list of default operators is defined at the token contract creation time,
|
||
|
and cannot be changed.
|
||
|
Any holder still has the right to revoke default operators.
|
||
|
One of the obvious advantages of default operators is to allow ether-less movements of tokens.
|
||
|
Default operators offer other usability advantages,
|
||
|
such as allowing token providers to offer functionality in a modular way,
|
||
|
and to reduce the complexity for holders to use features provided through operators.
|
||
|
|
||
|
## Backward Compatibility
|
||
|
|
||
|
This EIP does not introduce backward incompatibilities and is backward compatible with the older [ERC-20] 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 [ERC-20] functions `transfer`, `transferFrom`, `approve` and `allowance`
|
||
|
alongside to make a token fully compatible with [ERC-20].
|
||
|
|
||
|
The token MAY implement `decimals()` for backward compatibility with [ERC-20].
|
||
|
If implemented, it MUST always return `18`.
|
||
|
|
||
|
Therefore a token contract MAY implement both [ERC-20] and [ERC-777] 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 [ERC-777] and MUST be implemented:
|
||
|
`name`, `symbol` `balanceOf` and `totalSupply`
|
||
|
(`decimals` is not part of the [ERC-777] standard).
|
||
|
|
||
|
The state-modifying functions from both standards are decoupled and can operate independently from each other.
|
||
|
Note that [ERC-20] functions SHOULD be limited to only being called from old contracts.
|
||
|
|
||
|
If the token implements [ERC-20],
|
||
|
it MUST register the `ERC20Token` interface with its own address via [ERC-1820].
|
||
|
This is done by calling the `setInterfaceImplementer` function on the ERC1820 registry
|
||
|
with the token contract address as both the address and the implementer
|
||
|
and the `keccak256` hash of `ERC20Token` (`0xaea199e31a596269b42cdafd93407f14436db6e4cad65417994c2eb37381e05a`)
|
||
|
as the interface hash.
|
||
|
|
||
|
If the contract has a switch to enable or disable ERC20 functions, every time the switch is triggered,
|
||
|
the token MUST register or unregister the `ERC20Token` interface for its own address accordingly via ERC1820.
|
||
|
Unregistering implies calling the `setInterfaceImplementer` with the token contract address as the address,
|
||
|
the `keccak256` hash of `ERC20Token` as the interface hash and `0x0` as the implementer.
|
||
|
(See [Set An Interface For An Address][erc1820-set] in [ERC-1820] for more details.)
|
||
|
|
||
|
The difference for new contracts implementing [ERC-20] is that
|
||
|
`tokensToSend` and `tokensReceived` hooks take precedence over [ERC-20].
|
||
|
Even with an [ERC-20] `transfer` and `transferFrom` call, the token contract MUST check via [ERC-1820]
|
||
|
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 [ERC-20] `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 [ERC-777] and [ERC-20]:
|
||
|
|
||
|
<table>
|
||
|
<tr>
|
||
|
<th align="right">ERC1820</th>
|
||
|
<th><code>to</code> address</th>
|
||
|
<th align="center">ERC777 Sending And Minting</th>
|
||
|
<th align="center">ERC20 <code>transfer</code>/<code>transferFrom</code></th>
|
||
|
</tr>
|
||
|
<tr>
|
||
|
<td rowspan="2" align="right">
|
||
|
<code>ERC777TokensRecipient</code><br/>registered
|
||
|
</td>
|
||
|
<td>regular address</td>
|
||
|
<td colspan="2" rowspan="2" align="center">
|
||
|
MUST call <code>tokensReceived</code>
|
||
|
</td>
|
||
|
</tr>
|
||
|
<tr>
|
||
|
<td>contract</td>
|
||
|
</tr>
|
||
|
<tr>
|
||
|
<td rowspan="2" align="right">
|
||
|
<code>ERC777TokensRecipient</code><br/>not registered
|
||
|
</td>
|
||
|
<td>regular address</td>
|
||
|
<td colspan="2" align="center">continue</td>
|
||
|
</tr>
|
||
|
<tr>
|
||
|
<td>contract</td>
|
||
|
<td align="center">MUST <code>revert</code></td>
|
||
|
<td align="center">SHOULD continue<sup><a id="continue-footnote-backlink" href="#continue-footnote">1</a></sup></td>
|
||
|
</tr>
|
||
|
</table>
|
||
|
|
||
|
> <a href="#continue-footnote-backlink"><small id="continue-footnote">1.</small></a>
|
||
|
> <small>The transaction SHOULD continue for clarity as ERC20 is not aware of hooks.</small>
|
||
|
> <small>However, this can result in accidentally locked tokens.</small>
|
||
|
> <small>If avoiding accidentally locked tokens is paramount, the transaction MAY <code>revert</code>.</small>
|
||
|
|
||
|
|
||
|
There is no particular action to take if `tokensToSend` is not implemented.
|
||
|
The movement 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 [ERC-1820],
|
||
|
the token contract SHOULD emit a `Transfer` event for minting and burning
|
||
|
and MUST emit a `Transfer` event for sending (as specified in the [ERC-20] standard).
|
||
|
During an [ERC-20]'s `transfer` or `transferFrom` functions, a valid `Sent` event MUST be emitted.
|
||
|
|
||
|
Hence for any movement of tokens, two events MAY be emitted:
|
||
|
an [ERC-20] `Transfer` and an [ERC-777] `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][0xjac/ERC777] contains all the [tests][ref tests].
|
||
|
|
||
|
## Implementation
|
||
|
|
||
|
The GitHub repository [0xjac/ERC777] contains the [reference implementation].
|
||
|
The reference implementation is also available via [npm][npm/erc777] and can be installed with `npm install erc777`.
|
||
|
|
||
|
## Copyright
|
||
|
|
||
|
Copyright and related rights waived via [CC0](../LICENSE.md).
|
||
|
|
||
|
[operators]: #operators
|
||
|
|
||
|
[ERC-20]: ./eip-20.md
|
||
|
[ERC-165]: ./eip-165.md
|
||
|
[ERC-672]: https://github.com/ethereum/EIPs/issues/672
|
||
|
[ERC-777]: ./eip-777.md
|
||
|
[ERC-820]: ./eip-820.md
|
||
|
[ERC820a]: https://github.com/ethereum/EIPs/pull/1758
|
||
|
[ERC-1820]: ./eip-1820.md
|
||
|
[erc1820-set]: ./eip-1820.md#set-an-interface-for-an-address
|
||
|
[0xjac]: https://github.com/0xjac
|
||
|
[0xjac/ERC777]: https://github.com/0xjac/ERC777
|
||
|
[master thesis]: https://github.com/0xjac/master-thesis
|
||
|
[npm/erc777]: https://www.npmjs.com/package/erc777
|
||
|
[ref tests]: https://github.com/0xjac/ERC777/blob/master/test/ReferenceToken.test.js
|
||
|
[reference implementation]: https://github.com/0xjac/ERC777/blob/master/contracts/examples/ReferenceToken.sol
|
||
|
[EIP-223]: 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
|
||
|
[minted]: #minted
|
||
|
[burned]: #burned
|
||
|
|
||
|
[logos]: https://github.com/ethereum/EIPs/tree/master/assets/eip-777/logo
|
||
|
[beige logo]: ../assets/eip-777/logo/png/ERC-777-logo-beige-48px.png
|
||
|
[white logo]: ../assets/eip-777/logo/png/ERC-777-logo-white-48px.png
|
||
|
[light grey logo]: ../assets/eip-777/logo/png/ERC-777-logo-light_grey-48px.png
|
||
|
[dark grey logo]: ../assets/eip-777/logo/png/ERC-777-logo-dark_grey-48px.png
|
||
|
[black logo]: ../assets/eip-777/logo/png/ERC-777-logo-black-48px.png
|