# checks-effects-interactions pattern prevents reentrancy by updating state before external calls
The checks-effects-interactions (CEI) pattern structures function execution in three phases: first check all preconditions and validate inputs, then update all state variables, and only then make external calls. Since [[reentrancy is possible whenever external calls precede state updates]], performing state updates before external calls ensures that any reentrant call sees the already-updated state, neutralizing the attack.
Combined with reentrancy guard modifiers (mutex locks), CEI has driven reentrancy from #1 to #5 in the OWASP 2025 ranking. The pattern is the primary defense against single-function, cross-function, and cross-contract reentrancy. For [[read-only reentrancy exploits view functions to cause other protocols to read inconsistent state|read-only reentrancy]], CEI alone is insufficient, protocols must also protect state-reading functions across contracts, not just state-writing ones, since `nonReentrant` guards do not trigger on view function calls.
CEI analysis must trace through internal function calls to identify all external call sites, not only direct top-level `call()` invocations. Internal helpers that hide `.call.value()` interactions make violations non-obvious when reading the calling function alone. This is the same cognitive failure that makes [[hidden ERC standard callbacks create reentrancy surface developers do not recognize as hookable|ERC standard callbacks invisible as reentrancy entry points]]: in both cases the external call is wrapped inside a function the developer does not mentally model as "making an external call."
---
Relevant Notes:
- [[reentrancy is possible whenever external calls precede state updates]]: the vulnerability this defends against
- [[read-only reentrancy exploits view functions to cause other protocols to read inconsistent state]]: a variant that requires additional measures beyond CEI
- [[vyper compiler reentrancy lock storage slot bug broke cross-function reentrancy protection in versions 0.2.15 through 0.3.0]]: demonstrates why CEI is essential as defense-in-depth: the compiler-level lock failure would not have mattered if CEI had been followed
- [[vyper nonreentrant decorator uses a single global storage lock eliminating cross-function reentrancy by design in versions 0.4.0 and later]]: Vyper's language-level complement to CEI, providing structural reentrancy defense
- [[the Fei Protocol Rari Capital exploit demonstrated that incomplete reentrancy guard coverage enables cross-function reentrancy through unprotected state-sharing functions]]: $80M lost because the team chose mutex locks over CEI and applied them incompletely
- [[the Curve Finance Vyper compiler exploit proved that source-level reentrancy guards can be silently broken by compiler bugs invisible to auditors]]: CEI at the source level would have prevented this exploit regardless of the compiler bug that broke mutex guards
- [[the Penpie exploit demonstrated reentrancy in reward-harvesting via permissionless Pendle market creation enabling flash-loan-amplified extraction of $27 million]]: $27M lost because _harvestBatchMarketRewards() made external calls to unvalidated market contracts before state updates; CEI compliance would have neutralized the re-entry
- [[hidden ERC standard callbacks create reentrancy surface developers do not recognize as hookable]]: extends: CEI requires recognizing ERC-1155/ERC-721/ERC-777 library functions as external calls, not just explicit .call() invocations
- [[EIP-2929 warm and cold storage access costs create gas-limit dependent reentrancy guard failures in contracts using hardcoded gas amounts]]: grounds the preference for CEI over gas-stipend-based reentrancy prevention: the 2300 gas stipend was broken by EIP-2929's cold SLOAD cost increase, while CEI remains valid regardless of gas schedule changes
- [[the Fuse reentrancy attack used flash-loan-funded liquidity supply to the target pool enabling the exploit without requiring attacker-owned capital]]: exemplifies: the CEI violation in borrow() is the entry point the flash-loan capital structure was constructed to exploit
Topics:
- [[security-patterns]]
- [[vulnerability-patterns]]