Skip to main content
Sigvex

Payable Fallback Remediation

How to keep a payable fallback from stranding funds by accounting for, restricting, or rejecting stray ETH.

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.

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 receive but not in fallback (or vice versa), so one path silently loses funds.
  • Emitting no event, leaving off-chain systems unable to detect or reconcile incoming transfers.

References