OpenZeppelin recently published their implementation of the surging fungible-token standard ERC777. The purpose of ERC777 is to improve upon ERC20 while maintaining backward compatibility. The contract comes with two hooks,
tokensReceived, that addresses may implement to control and revert token operations. Accounts can now receive funds and a notification within a single transaction, supplanting the two-step process (
transferFrom) in ERC20. Let’s jump right in.
A glaring difference between 777 and ERC20 is the addition of operators. Token holders can authorize and revoke trusted entities to act on their behalf. Contract deployers may define default operators who can move tokens for all addresses. Notice that
send is used in place of
transferFrom, mirroring the transfer of Ether.
We begin with the contract definition and variable declarations. ERC777 inherits from the interface defined in the EIP as well as from the ERC20 interface. An introspection registry (ERC1820) where contracts and regular addresses publish the functionality they implement, is required. The two hardcoded hashes will see use later when we call our send/receive hooks.
The constructor intakes three arguments: the
name of the token, the
symbol of the token (DAI, BAT…etc), and an array
defaultOperators to hold a list of addresses. Private variables are assigned. The contract then proclaims its ERC777/ERC20 interfaces with the registry.
view functions look as expected. ERC20 compliance requires the implementation of
We’ve arrived at
send, the quintessential method which moves tokens between accounts. The
_send call nested inside will also be called by
operatorSend (which we will get to later).
_send requires that from and to cannot be the zero address. Notice that
_move is the one who moves the needle (and emit two
events, one for each token standard).
_callTokensReceived is the duo responsible for calling the previously mentioned hook functions.
The Send Hook
_callTokensToSend first checks with the introspection registry that the
from address in our transaction implements the send-hook. This enables us to call
IERC777Sender(implementer).tokensToSend. Upon firing, the
from address should receive a prompt (This will depend on the wallet implementation of the interface) allowing the sender to revert the transaction.
The Receive Hook
_callTokensReceived should feel familiar. The
else if at the end will revert if
to is a contract.
false only when an ERC20 function (
_callTokensReceived. Recall that ERC777 inherits from ERC20. Upon invoking
tokensReceived, the receiver will get a notification that someone is sending them some tokens, and allowing the receiver to revert.
A 777-derived contract is meant to call
_mint since the authors chose to remain agnostic in token creation methodologies. State variables are updated to reflect the minting. The receiver of the newly minted coins gets notified so long as the receive-hook is at the ready. Finally, the method emits the
Minted (ERC777) and
Transfer (ERC20) events.
operatorBurn both invoke the underlying
_burn method. The send-hook allows the burner to revert.
_balances[from] are updated appropriately.
Operator send / burn
As expected, these functions call the
_burn methods on behalf of the token holder.
revokeOperator both require
msg.sender to not be the argument supplied. Both functions check to see if the
operator is part of the list of default operators so that it may modify the corresponding array.
isOperatorFor checks if a given user authorizes a given operator.
All of the ERC20 functions are implemented.