Skip to content
Last updated: February 06, 2026

SCWE-102: Missing Checks-Effects-Interactions Pattern

Stable Version v1.0

This content is in the version-(v1.0) and still under active development, so it is subject to change any time (e.g. structure, IDs, content, URLs, etc.).

Send Feedback

Relationships

Description

Calling external contracts before updating local state breaks the checks-effects-interactions (CEI) pattern. Attackers can reenter or observe stale state to perform double-withdrawals, bypass limits, or corrupt accounting.

Remediation

  • Perform input validation and state updates before external calls.
  • Use reentrancy guards on functions that transfer value or call untrusted contracts.
  • Prefer pull-based withdrawals to avoid pushing funds inside complex flows.

Examples

Vulnerable

pragma solidity ^0.8.0;

contract Payout {
    mapping(address => uint256) public balances;

    function withdraw() external {
        (bool ok, ) = msg.sender.call{value: balances[msg.sender]}("");
        require(ok, "send failed");       // external call first
        balances[msg.sender] = 0;         // state updated after
    }
}

Fixed

pragma solidity ^0.8.0;
import "@openzeppelin/contracts/security/ReentrancyGuard.sol"; // OZ 5.x: utils/ReentrancyGuard.sol

contract Payout is ReentrancyGuard {
    mapping(address => uint256) public balances;

    function withdraw() external nonReentrant {
        uint256 amount = balances[msg.sender];
        balances[msg.sender] = 0;         // effects first
        (bool ok, ) = msg.sender.call{value: amount}("");
        require(ok, "send failed");       // interaction last
    }
}