Payable Fallback Remediation
Overview
Related Detector: Payable Fallback
A payable fallback/receive that accepts ETH without updating accounting, emitting an event, or restricting the sender lets ether pile up with no way to attribute or withdraw it — permanently locked. The remediation is to decide deliberately whether the contract should hold ETH: if yes, account for it; if no, reject it.
Recommended Fix
Before (Vulnerable)
// Silently accepts ETH from anyone, with no accounting and no withdrawal path.
receive() external payable {}
fallback() external payable {}
After (Fixed — either account for it, or reject it)
// Option A: the contract is meant to hold ETH — record and expose a withdrawal.
event Received(address indexed from, uint256 amount);
receive() external payable {
balances[msg.sender] += msg.value;
emit Received(msg.sender, msg.value);
}
// Option B: the contract is NOT meant to hold ETH — refuse it.
receive() external payable {
revert("No direct ETH");
}
Pick the option that matches the contract’s purpose. If ETH is expected only through specific functions, make those payable and leave receive/fallback non-payable (or reverting).
Common Mistakes
- Making the fallback payable “just in case” and never adding a withdrawal path, so any ETH sent is trapped.
- Accounting for ETH in
receivebut not infallback(or vice versa), so one path silently loses funds. - Emitting no event, leaving off-chain systems unable to detect or reconcile incoming transfers.