revert and exceptions
A revert is the EVM's way of saying 'stop, and pretend none of this happened'. When a contract hits a condition it refuses to proceed under — an insufficient balance, a failed authorization check, a math error — it can revert, which immediately halts execution and rolls back every state change made in the current call frame, as if the call were never run. This all-or-nothing behavior is what makes a transaction atomic: it either fully succeeds or leaves the world exactly as it found it.
The clean way to do this is the REVERT opcode (0xFD, introduced in EIP-140). REVERT stops execution, undoes state changes in the current frame, returns any remaining gas to the caller, and hands back a region of memory as error data. Solidity's require, revert, and custom errors all compile down to REVERT, and they encode that error data in standard shapes: Error(string) for a require with a message, Panic(uint256) for internal failures like division by zero or array out-of-bounds, and ABI-encoded custom errors (gas-cheaper than strings) for application-specific failures.
Not every failure is a graceful revert. An invalid opcode, a stack underflow, or — most commonly — running out of gas triggers an exceptional halt that also reverts state but consumes all remaining gas rather than refunding it. This asymmetry is deliberate: a gas-exhaustion failure has already made the network do real work, so it is not refunded, whereas an explicit REVERT signals a clean, anticipated rejection and returns the unused gas.
Revert semantics are scoped to the call frame, which is essential to understand for cross-contract calls. A low-level CALL to a contract that reverts does not automatically bubble up — it merely returns false, and the caller chooses whether to revert in turn. Solidity's high-level external calls do re-throw by default, but try/catch lets a contract deliberately catch a callee's revert and continue, which is how patterns like 'attempt this, and fall back if it fails' are built.
Reverting still costs gas — you pay for the work done up to the revert point, only the unused remainder is returned. 'Free if it fails' is a myth; a failed transaction that ran a lot of logic before reverting can still be expensive.