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.).
Relationships¶
- CWE-841: Improper Enforcement of Behavioral Workflow
https://cwe.mitre.org/data/definitions/841.html
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
}
}