Free 30-min Web3 Consultation
Book Now
Smart Contract Security Audits
Learn More
Custom DeFi Protocol Development
Explore
Full-Stack Web3 dApp Development
View Services
Free 30-min Web3 Consultation
Book Now
Smart Contract Security Audits
Learn More
Custom DeFi Protocol Development
Explore
Full-Stack Web3 dApp Development
View Services
Free 30-min Web3 Consultation
Book Now
Smart Contract Security Audits
Learn More
Custom DeFi Protocol Development
Explore
Full-Stack Web3 dApp Development
View Services
Free 30-min Web3 Consultation
Book Now
Smart Contract Security Audits
Learn More
Custom DeFi Protocol Development
Explore
Full-Stack Web3 dApp Development
View Services
LABS
Glossary

Reentrant Call

A reentrant call is a programming pattern where a smart contract function is interrupted by an external call, allowing the caller to recursively re-enter the function before its state is finalized.
Chainscore © 2026
definition
BLOCKCHAIN SECURITY

What is a Reentrant Call?

A reentrant call is a programming vulnerability where an external contract's callback into the calling contract interrupts and re-executes its logic before the initial execution is complete, potentially draining funds.

A reentrant call occurs when a smart contract function makes an external call to another contract, which then calls back into the original function before its first invocation has finished. This creates a recursive loop where the contract's state—such as an account balance—is not updated before the second execution begins. The most famous exploit of this pattern was the 2016 DAO hack on Ethereum, where an attacker's malicious contract repeatedly withdrew funds because the balance was only subtracted after the external transfer.

The vulnerability stems from violating the checks-effects-interactions pattern. Secure code should first perform all checks (e.g., validating sufficient balance), then apply all effects (updating the internal state), and only finally make external interactions. A reentrant function that interacts before updating its state allows the attacker's fallback function to re-enter and pass the same checks again, as the original balance remains unchanged. This is a specific type of race condition within a single transaction's execution.

Preventing reentrancy involves using reentrancy guards, such as OpenZeppelin's ReentrancyGuard modifier, which employs a boolean lock (nonReentrant) to block recursive calls. Alternatively, developers can adhere strictly to the checks-effects-interactions pattern. Some languages, like Vyper, have built-in protections against state reentrancy. It's crucial to understand that reentrancy can also be cross-function, where a callback enters a different function that shares sensitive state, requiring careful design of state variable access.

how-it-works
BLOCKCHAIN SECURITY

How a Reentrant Call Works

A reentrant call is a critical smart contract vulnerability where an external contract maliciously calls back into the original function before its initial execution has completed, often to drain funds.

A reentrant call occurs when a smart contract function makes an external call to another untrusted contract, which then recursively calls back into the original function before its first invocation has finished. This is possible because the Ethereum Virtual Machine (EVM) does not automatically lock a contract's state during an external call. The most famous exploitation of this was the DAO attack in 2016, where a malicious contract repeatedly called the vulnerable withdraw function, tricking the balance accounting logic and siphoning millions in Ether.

The vulnerability stems from a violation of the Checks-Effects-Interactions (CEI) pattern. A secure pattern dictates that a function should: 1) perform all checks (e.g., validating balances), 2) apply all effects (e.g., updating internal state like deducting a balance), and 3) only then make external interactions (e.g., sending funds). A reentrancy bug inverts this order, typically sending funds (interaction) before updating the internal state (effect), allowing the attacker's fallback function to re-enter and pass the unchanged checks repeatedly.

Developers prevent reentrancy using two primary methods. The first is strict adherence to the CEI pattern. The second, and more robust for complex logic, is using a reentrancy guard. This is a function modifier, like OpenZeppelin's ReentrancyGuard, that sets a boolean lock (nonReentrant) for the function's duration, throwing an error if a reentrant call is attempted. For ultimate safety, the pull-over-push pattern is recommended, where the contract designates owed funds to users who must call a separate function to withdraw them, eliminating untrusted external calls from critical state-changing functions altogether.

key-features
REENTRANT CALL

Key Features & Characteristics

A reentrant call is a programming vulnerability where an external contract's callback function maliciously re-enters the calling contract before its initial execution is complete, often to drain funds. These are the core mechanisms and properties that define this security flaw.

01

The Core Vulnerability

A reentrant call exploits the sequential nature of state updates in smart contracts. The vulnerability occurs when a contract:

  • Sends funds (e.g., via call.value()) to an untrusted address.
  • Updates its internal state (e.g., reducing the user's balance) after the external call. An attacker's contract can execute a fallback or receive function that calls back into the vulnerable function before the balance is deducted, allowing repeated withdrawals from the same initial balance.
02

The Checks-Effects-Interactions Pattern

The primary defense against reentrancy is the Checks-Effects-Interactions (CEI) pattern. This coding standard mandates a strict order of operations:

  1. Checks: Validate all conditions and inputs (e.g., require(balance >= amount)).
  2. Effects: Update all internal state variables before any external calls (e.g., balances[msg.sender] -= amount).
  3. Interactions: Perform external calls to other contracts or addresses last. By updating state first, any reentrant call will see the already-modified balance, preventing double-spending.
03

Reentrancy Guard Modifiers

A reentrancy guard is a code-level lock that prevents a function from being called recursively. It uses a boolean state variable (e.g., locked) that is set to true on entry and false on exit. The OpenZeppelin ReentrancyGuard contract provides a standard nonReentrant modifier. When applied to a function, any reentrant call will fail the require(!locked) check, providing a robust, single-line defense. This is considered a best practice for any function performing external calls.

04

Historical Impact: The DAO Hack

The most famous reentrancy attack was the 2016 DAO hack, which resulted in the theft of 3.6 million ETH (worth ~$50M at the time). The vulnerable splitDAO function in The DAO's contract sent ETH to an attacker before updating the attacker's internal token balance. The attacker's fallback function repeatedly re-entered splitDAO, draining funds in a loop. This event directly led to the Ethereum hard fork that created Ethereum (ETH) and Ethereum Classic (ETC).

05

Cross-Function & Cross-Contract Reentrancy

Reentrancy is not limited to a single function. Cross-function reentrancy occurs when an attacker's callback re-enters a different function in the same contract that shares state. Cross-contract reentrancy involves re-entering a separate contract that shares a common state variable (e.g., a pair of pools using the same token contract). These are subtler and harder to detect than single-function reentrancy, as the malicious path doesn't directly loop within one function.

06

Interaction with ERC-777 and ERC-1155

Modern token standards like ERC-777 and ERC-1155 introduce built-in hooks (tokensToSend, tokensReceived, onERC1155Received). These are callback functions that execute during a token transfer. If a contract's logic is sensitive to token balances and performs actions after a transfer, these hooks can be used to launch a reentrant attack before the contract's state is finalized. Developers must apply the CEI pattern and reentrancy guards even when using these standards.

code-example
REENTRANCY ATTACK

Code Example: Vulnerable Pattern

A practical demonstration of a smart contract vulnerability where an external call allows an attacker to re-enter the function before its state is finalized.

A reentrant call vulnerability occurs when a smart contract makes an external call to an untrusted contract before it has updated its own internal state. The canonical example is a simple bank contract with a withdraw function. If the contract sends Ether via call.value() to the user before updating their balance to zero, a malicious contract can implement a receive or fallback function that calls withdraw again. This creates a recursive loop, draining the contract's funds in a single transaction.

The core issue is a violation of the Checks-Effects-Interactions (CEI) pattern. Secure code should first check all conditions (e.g., sufficient balance), then effect all state changes (e.g., set balance to zero), and only finally perform interactions with external addresses. The vulnerable pattern inverts this order, performing the interaction (the Ether transfer) before the crucial state effect. This leaves the contract in an inconsistent state where the attacker's balance is still non-zero during the recursive call.

To mitigate this, developers must adhere to the CEI pattern rigorously. For Ether transfers, using transfer or send (which forward a limited 2300 gas) can prevent reentrancy, though this is not a complete solution. The recommended modern practice is to use a reentrancy guard, a boolean modifier that locks a function during execution. Furthermore, for complex logic, consider using pull payment patterns, where users withdraw funds themselves, separating the transaction of value from the core business logic and eliminating the dangerous external call context.

security-considerations
GLOSSARY TERM

Security Considerations & Attack Vectors

A reentrant call is a type of smart contract vulnerability where an external contract maliciously calls back into the original function before its initial execution is complete, often to drain funds.

01

Core Mechanism

The attack exploits the order of operations in a function. A typical pattern is:

  • 1. Contract A sends funds to Contract B.
  • 2. Contract B's fallback/receive function is triggered.
  • 3. Before Contract A's state (like balance) is updated, Contract B calls back into Contract A's original function.
  • 4. Contract A sees its old, unchanged balance, allowing the same withdrawal to repeat. This creates a recursive loop that drains assets.
02

The DAO Hack (2016)

The most famous reentrancy attack resulted in the theft of 3.6 million ETH (worth ~$50M at the time) from The DAO, leading to the Ethereum hard fork. The vulnerable splitDAO function sent ETH before updating the user's internal token balance. The attacker's contract recursively called splitDAO, draining funds in a loop before the balance was set to zero.

03

Checks-Effects-Interactions Pattern

The primary defense is a strict coding pattern:

  1. Checks: Validate all conditions and inputs (e.g., require(balance > 0)).
  2. Effects: Update all internal state variables (e.g., balances[msg.sender] = 0).
  3. Interactions: Perform external calls to other contracts or addresses last (e.g., msg.sender.call{value: amount}("")). By updating state before interacting externally, you eliminate the reentrancy condition.
04

ReentrancyGuard Modifier

A common implementation uses a mutex lock. The nonReentrant modifier sets a boolean flag (_locked) when a function executes and reverts if re-entry is attempted.

solidity
modifier nonReentrant() {
    require(!_locked, "Reentrant call");
    _locked = true;
    _;
    _locked = false;
}

Libraries like OpenZeppelin's ReentrancyGuard provide a standardized, audited version of this protection.

05

Cross-Function & Cross-Contract Reentrancy

Reentrancy isn't limited to a single function.

  • Cross-Function: An attacker re-enters a different function in the same contract that shares state (e.g., a withdraw function and a transfer function both use the same balance map).
  • Cross-Contract: An attack involves multiple contracts that share state. Defending requires careful state isolation and applying the checks-effects-interactions pattern across the entire system.
06

Related Vulnerabilities

Reentrancy often interacts with other flaws:

  • Race Conditions: Similar to reentrancy, where outcome depends on sequence of events.
  • Unchecked Call Return Values: Using low-level call without handling failure can compound reentrancy damage.
  • ERC-777 Hooks: The tokensToSend/tokensReceived hooks can be used for reentrancy if state isn't secured. Audits must consider these composite risks.
examples
REENTRANCY ATTACKS

Historical Examples & Real-World Impact

Reentrant calls are most famously associated with critical security vulnerabilities that have led to some of the most significant financial losses in blockchain history. These real-world incidents serve as definitive case studies for developers.

04

The Checks-Effects-Interactions Pattern

The primary defensive programming pattern developed in direct response to reentrancy attacks like The DAO. It mandates a strict function structure:

  • Checks: Validate all conditions (e.g., balances, permissions).
  • Effects: Update all internal state variables (e.g., deduct balance).
  • Interactions: Perform external calls to other contracts or users. This pattern prevents state corruption by ensuring all state changes are finalized before any external interaction occurs.
05

ReentrancyGuard & Modern Mitigations

A standard modifier-based lock (nonReentrant) that uses a boolean state variable to prevent recursive function entry. Widely adopted in libraries like OpenZeppelin Contracts. Modern Solidity compilers also emit warnings for patterns that pose reentrancy risks. This represents the evolution from ad-hoc fixes to standardized, audited security primitives in smart contract development.

06

Impact on Auditing & Formal Verification

Reentrancy attacks fundamentally changed the smart contract security landscape, making static analysis for reentrancy a cornerstone of security audits. Tools like Slither and MythX include dedicated detectors. The severity of historical losses also accelerated research into formal verification methods to mathematically prove the absence of such vulnerabilities in critical contract code.

prevention-mitigation
REENTRANT CALL

Prevention and Mitigation Strategies

A reentrant call is a critical vulnerability in smart contracts where an external contract maliciously calls back into the original function before its initial execution is complete, potentially draining funds or corrupting state.

The canonical example is the 2016 DAO hack, where a malicious contract exploited a withdraw function. The attacker's fallback function was designed to be called upon receiving Ether, creating a recursive loop that repeatedly withdrew funds before the contract's internal balance was updated. This attack led to a historic hard fork of the Ethereum network. The core issue is a violation of the checks-effects-interactions pattern, where state changes (effects) should occur before any external calls (interactions) to prevent the contract's state from being inconsistent during a callback.

The primary defense is the reentrancy guard, a simple mutex lock implemented via a boolean state variable (e.g., nonReentrant). Functions protected with this modifier set the lock upon entry and release it only after all logic completes, blocking any nested calls to the same function. The OpenZeppelin ReentrancyGuard library provides a standardized, audited implementation. For broader protection, the pull-over-push pattern is recommended: instead of contracts actively sending funds (pushing), they allow users to withdraw funds themselves (pulling), which moves the reentrancy risk to the user's end.

Beyond guards, adhering to the checks-effects-interactions pattern is fundamental. First, validate all conditions (checks). Second, update all internal state variables (effects). Finally, perform external calls or transfers (interactions). This ensures the contract's state is finalized and immutable before any potentially dangerous external interaction occurs. For Ether transfers, using transfer or send (which forward a limited 2300 gas) can mitigate simple reentrancy, but this is not a complete solution as gas costs can change and it does not protect against calls to other functions.

Advanced mitigation involves formal verification and static analysis tools like Slither or MythX, which can automatically detect potential reentrancy paths in contract code. Furthermore, developers should be wary of cross-function reentrancy, where a call back into a different function of the same contract exploits shared state. Comprehensive testing, including fuzzing and invariant testing with tools like Foundry, is essential to simulate complex attack vectors and ensure all state transitions are secure before and after any external call.

REENTRANCY

Common Misconceptions

Reentrancy is a critical vulnerability pattern in smart contracts, often misunderstood as a simple 'function calling itself.' This section clarifies its precise mechanism, common pitfalls, and the difference between single-function and cross-function attacks.

A reentrant call is an exploit where a malicious contract's fallback or receive function recursively calls back into the vulnerable function of the original contract before its initial execution and state changes are finalized. The attack works by exploiting the order of operations in a vulnerable function, typically one that performs an external call (e.g., sending Ether via .transfer(), .send(), or .call()) before updating its internal state (like reducing a balance). The malicious contract intercepts the funds transfer, and its fallback function contains code to call the vulnerable function again, which may see the old, unchanged state and allow funds to be withdrawn multiple times.

solidity
// VULNERABLE PATTERN
function withdraw() public {
    uint amount = balances[msg.sender];
    // External call BEFORE state update
    (bool success, ) = msg.sender.call{value: amount}("");
    require(success, "Transfer failed");
    // State update happens too late
    balances[msg.sender] = 0;
}
SECURITY PATTERNS

Comparison: Reentrancy Guard vs. CEI Pattern

A comparison of two primary smart contract security patterns used to prevent reentrancy attacks.

Feature / CharacteristicReentrancy GuardChecks-Effects-Interactions (CEI) Pattern

Core Mechanism

State variable mutex lock

Strict function execution order

Implementation Complexity

Low (library/ modifier)

Medium (requires manual discipline)

Gas Overhead

~5k-10k gas per protected call

Minimal (structural only)

Protection Scope

Entire function

Specific external calls

Common Use Case

Functions with multiple external calls

All state-changing functions

Audit Friendliness

Explicit and easily verified

Requires careful line-by-line review

Risk if Misapplied

Deadlock (if misconfigured)

Remaining vulnerability

REENTRANT CALLS

Frequently Asked Questions (FAQ)

Reentrancy is a critical security vulnerability in smart contracts. These questions address its mechanics, famous exploits, and best practices for prevention.

A reentrant call occurs when an external contract is called during the execution of a function, and that external call is able to recursively call back into the original function before its initial execution is complete. This can lead to state inconsistencies and is the root cause of the reentrancy attack, where an attacker's contract repeatedly withdraws funds because the victim contract's balance is not updated until after the external call. The classic pattern involves a function that performs a call (e.g., sending Ether via .transfer(), .send(), or .call()) before updating its internal state variables to reflect that the funds have been disbursed.

ENQUIRY

Get In Touch
today.

Our experts will offer a free quote and a 30min call to discuss your project.

NDA Protected
24h Response
Directly to Engineering Team
10+
Protocols Shipped
$20M+
TVL Overall
NDA Protected direct pipeline
Reentrant Call: Definition & Security Risks in Smart Contracts | ChainScore Glossary