Unexpected Ether Transfers (Force Feeding)¶
Forcing a smart contract to hold an Ether balance influences its internal accounting and security assumptions. A smart contract receives Ether through the following hierarchy:
- Check whether a payable external
receivefunction is defined. - If not, check whether a payable external
fallbackfunction is defined. - Revert.
The Solidity by Example article illustrates the precedence:
1 2 3 4 5 6 7 8 9 10 11 12 13 | |
Consider the following example:
1 2 3 4 5 6 7 8 9 10 11 12 13 | |
The logic seemingly disallows direct payments and prevents "something bad" from happening. Calling revert in both fallback and receive nonetheless cannot prevent the contract from receiving Ether. Two techniques force-feed Ether to a smart contract.
Selfdestruct¶
When the SELFDESTRUCT opcode is called, funds of the calling address are sent to the address on the stack, and execution halts immediately. The opcode works at the EVM level, so Solidity-level functions that would block the receipt of Ether are not executed.
Pre-calculated Deployments¶
The target address of a newly deployed smart contract is generated deterministically. The address generation is visible in any EVM implementation, such as the py-evm reference implementation by the Ethereum Foundation:
1 2 | |
An attacker can send funds to this address before the deployment happens. A 2017 Underhanded Solidity Contest submission demonstrates the technique.
Mitigation¶
Exact comparisons against the contract's Ether balance are unreliable. Business logic has to account for an actual balance higher than the value tracked by internal accounting. The contract balance is not a trustworthy guard.