Solia saysSolidity dispatches calls by selector; receive() handles plain ETH, fallback() handles everything unknown, and low-level call/staticcall/delegatecall reach across the type system.
A contract receiving a call decides what to do via dispatch. If msg.data is empty and ETH is attached, the receive() function runs (or revert if it does not exist). If the selector matches a function, that function runs. If nothing matches, the fallback() function runs (or revert). Low-level calls — call, staticcall, delegatecall — bypass type checks: you build the calldata yourself and get back raw bytes plus a success flag.
Use receive() for plain ETH deposits, fallback() for proxy patterns (forward unknown selectors to a logic contract), and low-level calls when interacting with unknown or precompiled contracts. The demo dispatches three different incoming calls.
Power-ups you unlock
receive(): runs when calldata is empty and value > 0
fallback(): runs when no function selector matches
call/staticcall/delegatecall: low-level, raw bytes, manual
Proxies use fallback() to forward to a logic contract