ERC777 is an advanced token standard designed to improve on ERC20 by adding hooks that notify sender and recipient contracts on every transfer. These hooks — tokensToSend and tokensReceived — make ERC777 tokens inherently dangerous to integrate without reentrancy guards, and have been exploited in real attacks.
Every ERC777 transfer calls an external function on the sender's registered hook (tokensToSend) and the recipient's registered hook (tokensReceived). These are arbitrary external calls that execute during the transfer — before any state updates in your contract complete.
This turns every token transfer into a potential reentrancy vector, even if you're following the checks-effects-interactions pattern for Ether transfers. The attack flow:
tokensReceived hooktoken.transfer(attacker, amount)attacker.tokensReceived() before your state is updatedIn April 2020, an attacker drained approximately $300,000 from the imBTC/ETH pool on Uniswap V1. imBTC is an ERC777 token — its tokensReceived hook was called during the swap, re-entering Uniswap V1's single-function design before the balance was updated. See the imBTC token risk profile and AMP token risk profile for the affected tokens.
ERC777 tokens in widespread use include AMP and imBTC. ERC777 is also backward-compatible with ERC20 — meaning a contract can be both ERC20 and ERC777 simultaneously, and many tools won't warn you.
nonReentrant modifier to any function that calls transfer or transferFrom on external tokens// ERC1820 registry — detect if token has hooks
IERC1820Registry constant ERC1820 = IERC1820Registry(0x1820a4B7618BdE71Dce8cdc73aAB6C95905faD24);
bytes32 constant TOKENS_RECIPIENT_INTERFACE_HASH = keccak256("ERC777TokensRecipient");
bool hasHook = ERC1820.getInterfaceImplementer(tokenAddress, TOKENS_RECIPIENT_INTERFACE_HASH) != address(0);
See reentrancy for the full vulnerability explanation and prevention patterns.