Skip to content

Commit

Permalink
ERC777: last call feedback
Browse files Browse the repository at this point in the history
Changes based on the feedback from @fulldecent
  • Loading branch information
0xjac committed May 6, 2019
1 parent 598d77d commit 7f30359
Showing 1 changed file with 140 additions and 23 deletions.
163 changes: 140 additions & 23 deletions EIPS/eip-777.md
Original file line number Diff line number Diff line change
Expand Up @@ -239,7 +239,7 @@ The following rules MUST be applied regarding the *granularity*:
- 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 partition-able.
*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>
Expand Down Expand Up @@ -470,7 +470,7 @@ The token contract MAY send tokens from many *holders*, to many *recipients*, or
*NOTE*: Mechanisms such as applying a fee on a send is considered as a send to multiple *recipients*:
the intended *recipient* and the fee *recipient*.

*NOTE*: Transfer of tokens MAY be chained.
*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.

Expand Down Expand Up @@ -847,26 +847,26 @@ by the `operator` address.
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 for every send and burn processes.

- The `tokensToSend` hook MUST be called *before* the state is updated&mdash;i.e. *before* the balance is decremented.

- `operator` MUST be the address which triggered the decrease of the balance.
- `operator` MUST be the address which triggered the send or burn process.

- `from` MUST be the address of the *holder* whose balance is decreased.
- `from` MUST be the address of the *holder* whose tokens are sent or burned.

- `to` MUST be the address of the *recipient* whose balance is increased for a send.
- `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* balance is decreased by.
- `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 decrease of its balance by `revert`ing.
- 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`.
Expand Down Expand Up @@ -948,25 +948,27 @@ by the `operator` address.
The following rules apply when calling the `tokensReceived` hook:

- The `tokensReceived` hook MUST be called every time the balance is incremented.
- The `tokensReceived` hook MUST be called for every send and mint processes.

- 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.
- `operator` MUST be the address which triggered the send or mint process.

- `from` MUST be the address of the *holder* whose balance is decreased for a send.
- `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* whose balance is increased.
- `to` MUST be the address of the *recipient* which receives the tokens.

- `amount` MUST be the number of tokens the *recipient* balance is increased by.
- `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 an increase of its balance by `revert`ing. (I.e., reject the reception of tokens.)

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

Expand Down Expand Up @@ -1006,14 +1008,123 @@ If needed, other sizes MAY be created by converting from `SVG` into `PNG`.

## Rationale

This standard solves some of the shortcomings of [ERC20] while maintaining backward compatibility with [ERC20].
It avoids the problems and vulnerabilities of [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 *holders* over their tokens.
Note that, the usage of [ERC1820] provides backward compatibility with wallets and existing contracts
without having to be redeployed thanks proxy contracts implementing the hooks.
The principal intent for this standard is
to solve some of the shortcomings of [ERC20] while maintaining backward compatibility with [ERC20],
and avoiding the problems and vulnerabilities of [EIP223].

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, [ERC777] 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 [ERC20] 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 [EIP223],
and was requested by multiple members of the community who reviewed this standard.

### Hooks

In most cases, [ERC20] 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.

#### **[ERC1820] Registry**

The [ERC1820] 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 [EIP223] which proposes a `tokenFallback` function on recipient contracts
to be called when receiving tokens,
but improves on it by relying on [ERC165] 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 [ERC672] 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&mdash;used in this standard&mdash;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 [ERC777] and tokens.
The first proposal for this registry was [ERC820].
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 [ERC777] became [ERC1820].
[ERC1820] and [ERC820] are functionally equivalent. [ERC1820] 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 [ERC20] where the role of an approved address is not clearly defined,
[ERC777] 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

Expand Down Expand Up @@ -1102,7 +1213,7 @@ when sending, minting and transferring token via [ERC777] and [ERC20]:

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
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.
Expand Down Expand Up @@ -1135,10 +1246,16 @@ Copyright and related rights waived via [CC0].
[operators]: #operators

[ERC20]: https://eips.ethereum.org/EIPS/eip-20
[ERC165]: https://eips.ethereum.org/EIPS/eip-165
[ERC672]: https://github.com/ethereum/EIPs/issues/672
[ERC777]: https://eips.ethereum.org/EIPS/eip-777
[ERC820]: https://eips.ethereum.org/EIPS/eip-820
[ERC820a]: https://github.com/ethereum/EIPs/pull/1758
[ERC1820]: https://eips.ethereum.org/EIPS/eip-1820
[erc1820-set]: https://eips.ethereum.org/EIPS/eip-1820#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
Expand Down

0 comments on commit 7f30359

Please sign in to comment.