Remediating Token Hook Reentrancy
How to prevent reentrancy through ERC-777, ERC-1155, and ERC-4626 callback hooks using the CEI pattern and reentrancy guards.
Remediating Token Hook Reentrancy
Overview
Related Detector: Token Hook Reentrancy
Token standards with mandatory callbacks (ERC-777, ERC-1155, ERC-4626 with ERC-777 underlying) require the same Checks-Effects-Interactions discipline as ETH transfers. Apply ReentrancyGuard to all functions that interact with hook-enabled tokens.
Recommended Fix
Before (Vulnerable)
function withdraw(uint256 amount) external {
require(balances[msg.sender] >= amount);
erc777Token.send(msg.sender, amount, ""); // Callback fires here
balances[msg.sender] -= amount; // Too late
}
After (Fixed)
function withdraw(uint256 amount) external nonReentrant {
require(balances[msg.sender] >= amount);
balances[msg.sender] -= amount; // State update first
erc777Token.send(msg.sender, amount, ""); // Callback fires after
}
Alternative Mitigations
- Avoid hook-enabled tokens: Use standard ERC-20
transferinstead of ERC-777sendwhen hooks are not needed. - Pull pattern: Let users withdraw (pull) rather than pushing tokens to them.
Common Mistakes
- Only guarding the obvious function: Token hooks can trigger reentrancy into any public function, not just the one performing the transfer. Guard all state-modifying functions.