Mastering the Checks-Effects-Interactions Pattern for Solidity Smart Contracts


When writing smart contracts in Solidity, security is very important. One common problem developers face is called a reentrancy attack, where a bad actor tricks a contract into doing something it shouldn’t by calling it repeatedly before it finishes its work. To stop this, Solidity developers use a simple but powerful rule called the Checks-Effects-Interactions (CEI) pattern.
This pattern helps keep your contract safe by organizing your code into three clear steps:
Checks – Make sure everything is valid before doing anything.
Effects – Update your contract’s data or state.
Interactions – Call other contracts or send money last.
By following these steps, you reduce the chance that someone can attack your contract and steal funds or mess up your data.
What Is a Reentrancy Attack?
A reentrancy attack happens when a contract calls another contract (or an external address), and that external contract calls back into the original contract before the first call finishes. This can cause the original contract to run code multiple times in unexpected ways, often stealing money or causing errors.
For example, if your contract sends money to someone before updating their balance, the attacker can call your withdraw function again before their balance is reduced. This lets them withdraw more money than they should.
The Checks-Effects-Interactions Pattern Explained
Let’s break down the CEI pattern in simple terms:
1. Checks
First, check that everything is okay. For example, check if the user has enough balance to withdraw:
textrequire(balances[msg.sender] >= amount, "Not enough balance");
2. Effects
Next, update your contract’s state. For example, reduce the user’s balance:
textbalances[msg.sender] -= amount;
3. Interactions
Finally, send the money or call another contract:
text(bool success, ) = msg.sender.call{value: amount}("");
require(success, "Transfer failed");
By doing the external call last, you make sure the contract’s state is already updated and safe from attacks.
Why Does Order Matter?
If you send money or call another contract before updating your state, an attacker can call your function again before the state changes. This is how many hacks happen.
Unsafe example:
textfunction withdraw(uint amount) public {
require(balances[msg.sender] >= amount);
(bool sent, ) = msg.sender.call{value: amount}("");
require(sent, "Failed to send Ether");
balances[msg.sender] -= amount; // State updated after sending money - dangerous!
}
Safe example using CEI:
textfunction withdraw(uint amount) public {
require(balances[msg.sender] >= amount); // Check
balances[msg.sender] -= amount; // Effect
payable(msg.sender).transfer(amount); // Interaction
}
Extra Tips for Security
Use reentrancy guards (like OpenZeppelin’s
ReentrancyGuard
) to block multiple calls at once.Use the pull payment pattern where users withdraw funds themselves instead of pushing money automatically.
Test your contracts with tools like Slither or MythX to find vulnerabilities.
Keep your contract logic simple and easy to understand.
Real-World Importance of CEI
Many big hacks in DeFi happened because contracts didn’t follow CEI. For example, the famous DAO hack in 2016 lost millions because it updated balances after sending Ether. Projects that use CEI and other security measures are much safer and trusted by users.
Conclusion
The Checks-Effects-Interactions pattern is one of the most important rules for writing safe Solidity contracts. It helps protect your contract from reentrancy attacks by making sure you check conditions first, update your contract’s state second, and only interact with other contracts or send money last. Using CEI, along with other security tools, will make your smart contracts stronger and more reliable. As blockchain apps handle more valuable assets, following CEI is a simple but powerful way to keep your users’ funds safe.
Follow me on LinkedIn for additional Web3 guides and smart contract tips. The learning journey continues with more exciting challenges ahead.
Subscribe to my newsletter
Read articles from azeez okhamena directly inside your inbox. Subscribe to the newsletter, and don't miss out.
Written by

azeez okhamena
azeez okhamena
I'm a skilled fullstack developer proficient in Python, Typescript, JavaScript, and Solidity. Experienced with frameworks like Django, Flask, and ExpressJS