Unexpected Ether Transfers (Force Feeding)¶
Forcing a smart contract to hold an Ether balance can influence its internal accounting and security assumptions. There are multiple ways a smart contract can receive Ether. The hierarchy is as follows:
- Check whether a payable external
receive
function is defined. - If not, check whether a payable external
fallback
function is defined. - Revert.
The precedence of each function is explained in this great graphic from the Solidity by Example article:
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 |
|
The contract's logic seemingly disallows direct payments and prevents "something bad" from happening. However, calling revert
in both fallback
and receive
cannot prevent the contract from receiving Ether. The following techniques can be used to 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 is immediately halted. Since this opcode works on the EVM-level, Solidity-level functions that might block the receipt of Ether will not be executed.
Pre-calculated Deployments¶
Additionally, the target address of newly deployed smart contracts is generated deterministically. The address generation can be looked up 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 has happened. This is also illustrated by this 2017 Underhanded Solidity Contest submission.
Mitigation¶
The above effects illustrate that relying on exact comparisons to the contract's Ether balance is unreliable. The smart contract's business logic must consider that the actual balance associated with it can be higher than the internal accounting's value.
Generally, using the contract's balance as a guard is not advisable.