What happens when you call the function that does not exist, or send ether to a contract via send()
or transfer()
?
fallback() external [payable] {...}
executes when:
- function does not exist in contract
- ether transfer with non-empty call data
- ether transfer when there is no
receive()
function
receive() external payable {...}
executes when:
- ether transfer with empty calldata
Code example
contract TestPayable {
uint x;
uint y;
fallback() external payable { x = 1; y = msg.value; }
receive() external payable { x = 2; y = msg.value; }
}
address(contract).transfer(AMOUNT)
=> x=2
address(contract).call{value:1}(abi.encodePacked("SOME"))
=> x=1
Real-world usage: Proxy
Let's look at the code of Proxy.sol
proxy.foo(...)
-
foo(...)
does not exist in proxy's source code - fallback is triggered
- make
delegatecall(...)
on implementation - return result
Thanks to fallback function arbitrary function can be called on a proxy. Without it we would have to create more complicated mechanism.
Why receive()
was introduced?
- To make explicit check if
msg.data
is empty. Without it, it was up to the programmer to check whether calldata is empty. - Better UX for plain ETH transfers, so the user who didn't put calldata could be handled independetly.
receive()
function enforced on programmers thinking about such cases.
Ethernaut 01: Fallback
There was a fallback method which granted you ownership if you contributed before and supplied some ether. This way your contribution could be less than owner but still you can gain ownership.
Interact with contract using web3.js
-
Send ether when interacting with an ABI
contract.contribute({value: AMOUNT_IN_WEI})
-
Send ether outside of the ABI docs
contract.sendTransaction({value: AMOUNT_IN_WEI})
sources:
forum answer
Top comments (0)