Bird
Raised Fist0
Blockchain / Solidityprogramming~10 mins

Reentrancy guard pattern in Blockchain / Solidity - Step-by-Step Execution

Choose your learning style10 modes available
Concept Flow - Reentrancy guard pattern
Function call starts
↓
Check if locked?
Yes→Reject call
No↓
Set locked = true
↓
Execute function body
↓
Set locked = false
↓
Function call ends
This flow shows how the reentrancy guard prevents a function from being called again before the first call finishes by using a lock flag.
Execution Sample
Blockchain / Solidity
bool locked = false;

function withdraw() public {
  require(!locked, "Reentrant call");
  locked = true;
  // send funds
  locked = false;
}
This code prevents reentrant calls by locking the function during execution.
Execution Table
SteplockedCondition (!locked)ActionResult
1falsetrueEnter function, set locked = truelocked = true, function proceeds
2truefalseReentrant call detected, revertCall rejected with error
3truefalseFunction body executesFunds sent
4truefalseSet locked = falselocked = false
5falsetrueFunction call endsReady for next call
💡 Reentrant call rejected at step 2 because locked is true
Variable Tracker
VariableStartAfter Step 1After Step 3After Step 4Final
lockedfalsetruetruefalsefalse
Key Moments - 3 Insights
Why does the function reject the second call when locked is true?
Because the reentrancy guard checks the locked flag before running. If locked is true (step 2 in execution_table), it means the function is already running, so it rejects to prevent reentrancy.
Why do we set locked = true before executing the function body?
Setting locked = true (step 1) marks the function as busy. This prevents any nested or repeated calls from entering until the first call finishes and resets locked.
What happens if we forget to set locked = false at the end?
If locked stays true, all future calls will be rejected forever, blocking the function. Step 4 resets locked to false to allow new calls.
Visual Quiz - 3 Questions
Test your understanding
Look at the execution_table, what is the value of locked after step 1?
Atrue
Bfalse
Cundefined
Dnull
💡 Hint
Check the 'locked' column in row for step 1 in execution_table
At which step does the function reject a reentrant call?
AStep 1
BStep 2
CStep 3
DStep 5
💡 Hint
Look for the row where Condition (!locked) is false and Action is 'Reentrant call detected'
If we remove 'locked = false' at step 4, what happens to future calls?
AThey succeed normally
BThey cause an error only once
CThey are rejected forever
DThey skip the lock check
💡 Hint
Refer to key_moments about resetting locked to false after function execution
Concept Snapshot
Reentrancy guard pattern:
- Use a boolean 'locked' flag
- Check 'locked' at function start
- If false, set 'locked = true' and proceed
- After function body, set 'locked = false'
- Prevents nested calls during execution
Full Transcript
The reentrancy guard pattern uses a boolean flag called 'locked' to prevent a function from being called again before the first call finishes. When the function starts, it checks if 'locked' is false. If so, it sets 'locked' to true and runs the function body. If 'locked' is true, it rejects the call to prevent reentrancy. After the function finishes, it resets 'locked' to false to allow future calls. This pattern protects against attacks where a function is called repeatedly before finishing, which can cause unexpected behavior or theft of funds.

Practice

(1/5)
1.

What is the main purpose of the reentrancy guard pattern in blockchain smart contracts?

easy
A. To prevent a function from being called again before it finishes execution
B. To speed up the execution of smart contracts
C. To allow multiple users to access a function simultaneously
D. To reduce the gas cost of transactions

Solution

  1. Step 1: Understand the problem reentrancy causes

    Reentrancy happens when a function is called again before the first call finishes, causing unexpected behavior or exploits.
  2. Step 2: Identify the role of the reentrancy guard

    The reentrancy guard uses a lock to block repeated calls until the first call completes, preventing reentrancy attacks.
  3. Final Answer:

    To prevent a function from being called again before it finishes execution -> Option A
  4. Quick Check:

    Reentrancy guard = prevent repeated calls [OK]
Hint: Reentrancy guard stops repeated calls during execution [OK]
Common Mistakes:
  • Thinking it speeds up contract execution
  • Believing it allows simultaneous access
  • Confusing it with gas optimization
2.

Which of the following is the correct way to declare a reentrancy guard variable in Solidity?

contract Example {
    // Fill in the blank
    _____ private locked = false;
}
easy
A. string
B. bool
C. int
D. address

Solution

  1. Step 1: Identify the variable type for a lock

    The lock variable is used as a flag to indicate if the function is running, so it should be a boolean (true/false).
  2. Step 2: Match the correct Solidity type

    In Solidity, bool is the type for true/false values, suitable for a lock variable.
  3. Final Answer:

    bool -> Option B
  4. Quick Check:

    Lock variable type = bool [OK]
Hint: Lock variables are boolean flags in Solidity [OK]
Common Mistakes:
  • Using int instead of bool for lock
  • Using string or address types incorrectly
  • Forgetting to initialize the variable
3.

Consider this Solidity function using a reentrancy guard:

bool private locked = false;

function withdraw(uint amount) public {
    require(!locked, "Reentrant call detected");
    locked = true;
    (bool success, ) = msg.sender.call{value: amount}("");
    require(success, "Transfer failed");
    locked = false;
}

What happens if msg.sender.call triggers a fallback that calls withdraw again?

medium
A. The second call is blocked by the lock and reverts
B. The second call succeeds and causes reentrancy
C. The contract runs out of gas and fails
D. The lock variable resets before the second call

Solution

  1. Step 1: Analyze the lock status during the first call

    When withdraw is called, locked is set to true before sending funds.
  2. Step 2: Understand the effect on reentrant call

    If msg.sender.call triggers a fallback that calls withdraw again, the require(!locked) check fails because locked is true, blocking the call.
  3. Final Answer:

    The second call is blocked by the lock and reverts -> Option A
  4. Quick Check:

    Lock true blocks reentrant call [OK]
Hint: Lock true means no reentrant calls allowed [OK]
Common Mistakes:
  • Assuming reentrancy still happens
  • Thinking lock resets before second call
  • Confusing gas failure with reentrancy
4.

Identify the bug in this reentrancy guard implementation:

bool private locked = false;

function withdraw(uint amount) public {
    require(!locked, "Reentrant call detected");
    (bool success, ) = msg.sender.call{value: amount}("");
    require(success, "Transfer failed");
    locked = true;
    locked = false;
}
medium
A. The require statement is missing
B. The lock variable is never set to true
C. The lock is set after the external call, allowing reentrancy
D. The call to msg.sender is incorrect syntax

Solution

  1. Step 1: Check when the lock is set

    The lock is set to true only after the external call, which means the function is not protected during the call.
  2. Step 2: Understand reentrancy risk

    Because the lock is set too late, a reentrant call can happen during the external call, causing vulnerability.
  3. Final Answer:

    The lock is set after the external call, allowing reentrancy -> Option C
  4. Quick Check:

    Lock must be set before external calls [OK]
Hint: Set lock before external calls to prevent reentrancy [OK]
Common Mistakes:
  • Setting lock after external call
  • Forgetting to set lock at all
  • Misplacing require statements
5.

You want to protect a function that updates user balances and sends Ether. Which is the best way to apply the reentrancy guard pattern?

function safeWithdraw(uint amount) public {
    // Your code here
    balances[msg.sender] -= amount;
    (bool success, ) = msg.sender.call{value: amount}("");
    require(success, "Transfer failed");
}
hard
A. Update balances after sending Ether without any lock
B. Do not use any lock, just rely on require statements
C. Use a lock variable but set it after sending Ether
D. Set a lock variable before updating balances and sending Ether, then release it after

Solution

  1. Step 1: Understand the order of operations for safety

    To prevent reentrancy, set a lock before any state changes and external calls, then release it after.
  2. Step 2: Apply the reentrancy guard pattern correctly

    Set the lock variable to true before updating balances and sending Ether, then set it back to false after the transfer completes.
  3. Final Answer:

    Set a lock variable before updating balances and sending Ether, then release it after -> Option D
  4. Quick Check:

    Lock before state change and external call [OK]
Hint: Lock before balance update and transfer, unlock after [OK]
Common Mistakes:
  • Setting lock after sending Ether
  • Updating balances after external call
  • Not using any lock at all
Recommended

Ready to prove it?

Pass the test and this pattern moves unknown strong while earning +1 Grip toward your certificate.

10 questions~5 mineasy hard