Solidity

Checks-Effects-Interactions

Checks-Effects-Interactions is a Solidity pattern that validates inputs first, updates contract state second, and performs external calls last to reduce reentrancy risk.

Check that the action is allowed, record the state change, then talk to another contract.

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.

Sources