0
0
Blockchain / Solidityprogramming~20 mins

Checks-Effects-Interactions pattern in Blockchain / Solidity - Practice Problems & Coding Challenges

Choose your learning style9 modes available
Challenge - 5 Problems
🎖️
Checks-Effects-Interactions Master
Get all challenges correct to earn this badge!
Test your skills under time pressure!
Predict Output
intermediate
2:00remaining
Output of a simple withdrawal function using Checks-Effects-Interactions
What is the output when calling withdraw(50) on this contract if the initial balance is 100?
Blockchain / Solidity
contract SimpleBank {
    mapping(address => uint) public balances;

    constructor() {
        balances[msg.sender] = 100;
    }

    function withdraw(uint amount) public returns (string memory) {
        require(balances[msg.sender] >= amount, "Insufficient balance"); // Check
        balances[msg.sender] -= amount; // Effect
        (bool success, ) = msg.sender.call{value: amount}(""); // Interaction
        require(success, "Transfer failed");
        return "Withdrawal successful";
    }

    receive() external payable {}
}
ARevert with "Transfer failed"
BRevert with "Insufficient balance"
C"Withdrawal successful"
DNo output, transaction runs out of gas
Attempts:
2 left
💡 Hint
Think about the order: check balance, update balance, then send funds.
Predict Output
intermediate
2:00remaining
Effect of changing order in Checks-Effects-Interactions
What happens if the withdraw function sends funds before updating the balance?
Blockchain / Solidity
function withdraw(uint amount) public returns (string memory) {
    require(balances[msg.sender] >= amount, "Insufficient balance"); // Check
    (bool success, ) = msg.sender.call{value: amount}(""); // Interaction
    require(success, "Transfer failed");
    balances[msg.sender] -= amount; // Effect
    return "Withdrawal successful";
}
ASafe because balance is checked first
BVulnerable to reentrancy attack, balance not updated before sending funds
CTransaction always reverts due to order
DNo difference from original function
Attempts:
2 left
💡 Hint
Consider what happens if the receiver calls back into withdraw before balance changes.
🔧 Debug
advanced
2:30remaining
Identify the bug in this contract violating Checks-Effects-Interactions
Which line causes a security risk violating the Checks-Effects-Interactions pattern?
Blockchain / Solidity
contract Vulnerable {
    mapping(address => uint) public balances;

    function deposit() public payable {
        balances[msg.sender] += msg.value;
    }

    function withdraw(uint amount) public {
        require(balances[msg.sender] >= amount, "Insufficient balance");
        (bool sent, ) = msg.sender.call{value: amount}("");
        require(sent, "Failed to send Ether");
        balances[msg.sender] -= amount;
    }
}
ALine with call to msg.sender.call{value: amount}("")
BLine with require(balances[msg.sender] >= amount, "Insufficient balance")
CLine with balances[msg.sender] -= amount
DLine with balances[msg.sender] += msg.value
Attempts:
2 left
💡 Hint
Look for interaction before effect.
🧠 Conceptual
advanced
1:30remaining
Why is Checks-Effects-Interactions pattern important?
Which statement best explains the main reason for using the Checks-Effects-Interactions pattern in smart contracts?
ATo prevent reentrancy attacks by updating state before external calls
BTo reduce gas costs by minimizing external calls
CTo allow multiple external calls before state changes
DTo make contracts compatible with all Ethereum clients
Attempts:
2 left
💡 Hint
Think about what kind of attack this pattern helps avoid.
🚀 Application
expert
3:00remaining
How many times can a reentrant call withdraw funds in this contract?
Given this contract, how many times can a malicious contract reenter and withdraw funds before balance updates?
Blockchain / Solidity
contract ReentrancyExample {
    mapping(address => uint) public balances;

    function deposit() public payable {
        balances[msg.sender] += msg.value;
    }

    function withdraw(uint amount) public {
        require(balances[msg.sender] >= amount, "Insufficient balance");
        (bool sent, ) = msg.sender.call{value: amount}("");
        require(sent, "Failed to send Ether");
        balances[msg.sender] -= amount;
    }
}
AExactly twice, due to call stack limits
BOnly once, because require prevents multiple calls
CZero times, function reverts immediately
DMultiple times until contract runs out of gas or balance
Attempts:
2 left
💡 Hint
Consider that balance is updated after sending funds.