Checks-Effects-Interactions Explained in Detail
Checks-Effects-Interactions is a control-flow pattern for functions that touch state and then call external code. The safe order is: validate the request, update internal state, then perform the external interaction.
The pattern reduces the chance that a callback can observe stale balances, shares, debt, or permissions.
Smart contract example
The unsafe version sends ETH before clearing the balance:
function withdraw() external {
uint256 amount = balances[msg.sender];
require(amount > 0, "empty");
(bool ok,) = msg.sender.call{value: amount}("");
require(ok, "send failed");
balances[msg.sender] = 0;
}
The safer CEI order clears the balance before the external call:
function withdraw() external {
uint256 amount = balances[msg.sender];
require(amount > 0, "empty");
balances[msg.sender] = 0;
(bool ok,) = msg.sender.call{value: amount}("");
require(ok, "send failed");
}
Checks-Effects-Interactions in Auditing
CEI is a baseline review technique. It gives auditors a fast way to spot callback windows before deeper invariant testing.
CEI is not a complete defense. Cross-function reentrancy, read-only reentrancy, token hooks, and multi-contract state machines can still break a protocol that appears to follow CEI in one function.
Red flags in code
-
ETH transfer before balance, debt, share, or collateral updates.
-
Token transfer before accounting is finalized.
-
call,delegatecall, hooks, or callbacks inside sensitive functions. -
Partial state updates before an external interaction.
-
CEI applied to one function while sibling functions touch the same state.
-
Complex protocols relying on CEI without invariant tests.
How to test or review it
-
Mark every external interaction in the function.
-
List the state that is updated before and after each interaction.
-
Try a malicious receiver that calls back during the interaction.
-
Test same-function and cross-function callback paths.
-
Check whether protocol invariants hold during the callback window.
-
Pair CEI with a reentrancy guard when shared state remains exposed.