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

Reentrancy Attack

A reentrancy attack is a smart contract exploit where an external contract makes recursive calls back into the vulnerable function before its initial execution finishes, often to drain assets.
Chainscore © 2026
definition
BLOCKCHAIN SECURITY

What is a Reentrancy Attack?

A reentrancy attack is a critical vulnerability in smart contracts where an external malicious contract exploits the state-changing logic of a target contract to drain its funds.

A reentrancy attack is a security exploit in which a malicious contract recursively calls back into a vulnerable function before its initial execution completes. This is possible because the Ethereum Virtual Machine (EVM) allows external calls to interact with other contracts, temporarily pausing the execution of the calling function. The classic pattern involves a function that performs an external call (e.g., sending Ether) before updating its internal state (e.g., deducting a balance). An attacker's fallback() or receive() function can be designed to repeatedly re-enter the vulnerable function, each time exploiting the outdated state to withdraw more funds than entitled.

The most infamous example is the 2016 DAO attack, which resulted in the loss of 3.6 million ETH and led to the Ethereum hard fork. The vulnerable code pattern followed the checks-effects-interactions anti-pattern. Modern prevention involves adhering to the checks-effects-interactions pattern, where internal state updates are performed before any external calls. Key defensive techniques include using Reentrancy Guards (like OpenZeppelin's ReentrancyGuard modifier), which employ a mutex lock (nonReentrant) to block recursive calls, and implementing pull-over-push payment patterns where users withdraw funds themselves.

Beyond simple single-function reentrancy, developers must guard against more sophisticated variants. Cross-function reentrancy occurs when an attacker uses a call from one function to re-enter a different, state-sharing function within the same contract. Read-only reentrancy exploits view functions or price oracles that read a contract's intermediate, inconsistent state during an external call, leading to incorrect calculations. Auditing tools and formal verification are essential for detection, but understanding the underlying execution model—where state is king and external calls are a suspension point—is the foundation of writing secure, resilient smart contracts.

key-features
VULNERABILITY MECHANICS

Key Characteristics of Reentrancy

Reentrancy is a critical vulnerability where an external contract can recursively call back into a vulnerable function before its initial execution completes, leading to state inconsistencies and fund theft.

01

The Core Mechanism

A reentrancy attack exploits the sequence of operations in a smart contract. The vulnerability occurs when a function makes an external call (e.g., sending Ether) to an untrusted contract before updating its own internal state (e.g., deducting a balance). The malicious contract's receive() or fallback() function can recursively call back into the vulnerable function, which sees the unupdated state, allowing repeated withdrawals.

02

Single-Function vs. Cross-Function

Reentrancy manifests in two primary forms:

  • Single-Function Reentrancy: The recursive call targets the same function that initiated the external call. This is the classic pattern seen in the DAO hack.
  • Cross-Function Reentrancy: The recursive call targets a different function in the same vulnerable contract that shares state (e.g., a balance variable) with the initially called function. This can be harder to detect during code review.
03

The State Update Pattern

The fundamental defense is the Checks-Effects-Interactions pattern. A secure function must:

  1. Checks: Validate all conditions and inputs.
  2. Effects: Update all internal state variables (e.g., deduct balances).
  3. Interactions: Perform external calls to other contracts or addresses. Reentrancy vulnerabilities occur when this order is violated, typically by performing Interactions before Effects.
05

Historical Impact: The DAO Hack

The 2016 attack on The DAO is the canonical example, resulting in the theft of 3.6 million ETH (worth ~$50M at the time). The vulnerability was a single-function reentrancy in the splitDAO function, which sent ETH before zeroing the attacker's token balance. This event directly led to the Ethereum hard fork that created Ethereum (ETH) and Ethereum Classic (ETC).

06

Related Vulnerability: Read-Only Reentrancy

A more subtle form where a malicious contract re-enters a view or pure function of a different, dependent protocol (like a DEX or lending market) that reads the inconsistent state of the vulnerable contract. This can manipulate oracle prices or collateral calculations without directly stealing from the primary contract, poisoning the ecosystem's shared state.

how-it-works
SECURITY VULNERABILITY

How a Reentrancy Attack Works: Step-by-Step

A reentrancy attack is a critical smart contract vulnerability where an external contract maliciously calls back into a vulnerable function before its initial execution is complete, allowing the attacker to drain funds or manipulate state.

The classic attack exploits a flawed withdrawal pattern. A vulnerable contract, such as a simple bank, typically follows these steps: 1) Check the user's balance, 2) Send funds via a low-level call, and 3) Update the internal balance to zero. The vulnerability lies in performing the state update after the external call. An attacker's malicious contract, acting as the recipient, has a receive() or fallback() function that recursively calls back into the vulnerable withdrawal function before step 3 executes.

Because the contract's internal balance for the attacker has not yet been set to zero, the initial balance check in step 1 passes again on the recursive call. This allows the attacker to withdraw the same funds multiple times in a single transaction, draining the contract's entire balance. This pattern is known as a single-function reentrancy attack. More complex variants, like cross-function reentrancy, exploit interactions between different functions that share state.

The infamous DAO hack of 2016, which resulted in the loss of 3.6 million ETH and led to the Ethereum hard fork, was a direct consequence of a reentrancy vulnerability. The attack demonstrated that even meticulously audited code could contain this subtle flaw, fundamentally changing smart contract security practices. The standard defense is the Checks-Effects-Interactions pattern, which mandates that code should: first perform all checks (e.g., balance validation), then update all internal state effects, and only finally make external interactions (like transfer or call).

Modern developers use additional safeguards like reentrancy guards, which are mutex locks that prevent a function from being called recursively. Solidity's nonReentrant modifier from OpenZeppelin's libraries is a common implementation. However, understanding the underlying execution flow—how the Ethereum Virtual Machine (EVM) handles message calls and state changes—is essential for writing robust code that is secure against this and other related control flow vulnerabilities.

code-example
REENTRANCY ATTACK

Code Example: Vulnerable vs. Secure Pattern

This section dissects a classic smart contract vulnerability by contrasting a flawed implementation with its corrected, secure counterpart, illustrating the critical importance of state management.

A vulnerable pattern for a withdrawal function, often found in early Ethereum contracts, fails to update the user's internal balance before making an external call, creating a reentrancy window. The flawed logic typically follows a checks-effects-interactions anti-pattern: it checks the caller's balance, sends the Ether via call.value(), and then updates the state to zero. This allows a malicious contract's fallback or receive function to call back into the vulnerable function repeatedly before its balance is deducted, draining the contract's funds in a single transaction.

The secure pattern enforces the checks-effects-interactions principle as a defensive programming rule. First, it validates all conditions (checks). Crucially, it then updates all state variables, such as setting the user's balance to zero, to reflect the transaction's outcome (effects). Only after the contract's internal state is finalized and immutable does it perform the external transfer (interactions). This order eliminates the reentrancy window, as any subsequent call into the function will find the caller's balance already zero, causing the initial check to fail.

Implementing this secure pattern often involves using specific, safer methods for transfers. Instead of the low-level addr.call{value: x}(""), which forwards all remaining gas and is prone to reentrancy, developers should use addr.transfer(x) (which limits gas to 2300, enough for logging but not for complex callbacks) or addr.send(x). For more complex interactions, employing a reentrancy guard—a boolean lock that prevents recursive function entry—provides an additional layer of protection, making the function effectively atomic during execution.

attack-variants
EXPLOIT CLASSIFICATION

Types of Reentrancy Attacks

Reentrancy attacks are categorized by their execution method and target. Understanding these variants is critical for implementing precise defensive strategies.

01

Single-Function Reentrancy

The classic form of the attack, made infamous by The DAO hack. An external contract's fallback or receive function is used to re-enter the same vulnerable function before its state updates are finalized.

  • Mechanism: The attacker calls withdraw(). The victim contract sends Ether, triggering the attacker's fallback function, which calls withdraw() again while the victim's balance is still non-zero.
  • Prerequisite: Relies on the checks-effects-interactions pattern violation, where an external call (interaction) is made before internal state (effects) is updated.
02

Cross-Function Reentrancy

An attacker re-enters a different function in the same contract that shares state with the initially called function. This bypasses simple mutex locks on a single function.

  • Mechanism: Function A updates a shared state variable after making an external call. The attacker's callback re-enters Function B, which reads the same not-yet-updated state, leading to invalid logic.
  • Defense: Requires a contract-wide reentrancy guard or ensuring all state changes for related functions happen before any external calls.
03

Cross-Contract Reentrancy

The exploit propagates across multiple contracts that share state, often via a shared token contract or registry. The state inconsistency is in a separate contract from the one making the external call.

  • Mechanism: Contract X calls attacker, who then calls Contract Y. Contract Y reads a shared state variable from Contract Z that X has not yet updated, allowing double-spends or unauthorized actions.
  • Example: The 2021 CREAM Finance hack exploited a shared interestRateModel state between market contracts via a reentrant callback.
04

Read-Only Reentrancy

A sophisticated attack where the reentrant call doesn't modify state in the victim contract but manipulates a price oracle or view function that the victim relies on. The state corruption occurs in a third-party dependency.

  • Mechanism: The attacker re-enters a lending protocol's getPrice() oracle function. The oracle reads its price from a vulnerable DEX pool where the attacker's reentrant call has artificially skewed the reserves, resulting in a faulty, exploitable price.
  • Defense: Oracles must use pull-based price updates or be resistant to reentrancy themselves.
05

Deferred Reentrancy

An attack that exploits the deferred execution of callbacks in systems like ERC-777 tokens or smart contract wallets. The malicious callback is not immediate but is scheduled, bypassing standard reentrancy guards.

  • Mechanism: An ERC-777 tokensToSend or tokensReceived hook is called after the guard is released. The attacker's hook then re-enters the protocol, which now considers the transaction complete and its guard inactive.
  • Key Insight: Defenses must account for hooks in token standards and consider the entire transaction lifecycle, not just a single function call.
06

Defensive Patterns

Standard mitigations to prevent reentrancy attacks, each with trade-offs.

  • Checks-Effects-Interactions: The fundamental pattern. Always perform state changes (effects) before external calls (interactions).
  • Reentrancy Guard: A boolean lock (e.g., OpenZeppelin's ReentrancyGuard) that prevents re-entry into annotated functions. Effective but can be bypassed in cross-function attacks.
  • Pull Payments: Shift to a withdrawal pattern. Instead of sending funds, mark the recipient's balance and let them withdraw later, removing the reentrant callback opportunity.
  • Gas Limitations: Some early attacks were mitigated by limiting forwarded gas (transfer), but this is not a reliable modern solution.
historical-examples
REENTRANCY ATTACK

Historical Examples & Impact

These pivotal incidents demonstrate the evolution of reentrancy attacks from theoretical vulnerability to a critical, costly exploit, fundamentally shaping smart contract security practices.

02

The Checks-Effects-Interactions Pattern

A direct, critical security pattern developed in response to reentrancy attacks. It mandates a strict function structure:

  • Checks: Validate all conditions (e.g., require(balance > 0)).
  • Effects: Update the contract's internal state before any external calls (e.g., balances[msg.sender] = 0).
  • Interactions: Perform the external call last (e.g., msg.sender.call{value: amount}("")). This pattern is now a fundamental best practice.
03

Reentrancy Guard Modifiers

A common technical mitigation using a mutex lock (nonReentrant modifier) to prevent recursive calls. When a function with this modifier is executed, it sets a boolean flag (e.g., locked = true) at entry and clears it upon exit. Any reentrant call will fail the initial require(!locked, "Reentrant call") check. This is implemented in libraries like OpenZeppelin's ReentrancyGuard.

04

Uniswap/Lendf.Me Hack (2020)

A cross-contract reentrancy attack that exploited an ERC-777 token's tokensToSend hook. The attacker used a malicious contract to reenter the lending protocol's flashLoan function during the callback, manipulating internal accounting. This resulted in a loss of $25 million and highlighted that reentrancy isn't limited to simple Ether transfers but can involve complex callback mechanisms in token standards.

05

Impact on Auditing & Tooling

Reentrancy attacks catalyzed the development of the smart contract security industry.

  • Static Analysis Tools: Slither and MythX automatically detect reentrancy vulnerabilities.
  • Formal Verification: Tools like Certora prove the absence of such flaws.
  • Security Audits: Reentrancy is now the primary check in every major smart contract audit, with dedicated sections in reports.
06

The Evolution to Read-Only Reentrancy

A sophisticated modern variant where an attack doesn't modify the calling contract's state but exploits external view functions that rely on the victim's state. By reentering a view function (which doesn't have a guard) from within a state-changing call, the attacker can read outdated, inconsistent data to manipulate prices or logic in DeFi oracles and lending protocols. This bypasses traditional mutex guards.

prevention-mitigation
REENTRANCY ATTACK

Prevention and Mitigation Strategies

Reentrancy attacks are prevented by modifying the order of state changes and external calls, or by using built-in security mechanisms. These strategies form the foundation of secure smart contract design.

01

Checks-Effects-Interactions Pattern

The Checks-Effects-Interactions (CEI) pattern is the fundamental defense against reentrancy. It mandates a strict execution order:

  • Checks: Validate all conditions and inputs (e.g., require(balance >= amount)).
  • Effects: Update all internal contract state before any external calls (e.g., balances[msg.sender] -= amount).
  • Interactions: Perform the external call to other contracts or EOAs last (e.g., msg.sender.call{value: amount}("")). This ensures the contract's state is finalized and immune to recursive callbacks before funds are transferred.
02

Reentrancy Guard Modifiers

A reentrancy guard is a mutex lock that prevents a function from being called recursively. It uses a boolean state variable (e.g., locked) that is set upon entry and cleared on exit. The OpenZeppelin ReentrancyGuard contract provides a standardized, audited implementation. Applying the nonReentrant modifier to a function is a robust, one-line solution, though it can introduce gas overhead and does not protect against cross-function reentrancy without careful application.

03

Pull Over Push Payments

The pull payment strategy mitigates reentrancy by separating the logic for entitlement from the actual transfer. Instead of a contract "pushing" funds to users (a risky external call), users "pull" their owed funds by calling a separate withdrawal function. This pattern, exemplified by OpenZeppelin's PullPayment library, localizes external calls to a dedicated function with its own reentrancy protections, drastically reducing the attack surface of the core business logic.

04

State Variable Locks & Gas Limits

Direct manipulation of state variables can create implicit locks. Using low-level transfer or send (which forward a strict 2300 gas stipend) for Ether transfers can prevent a receiving contract from having enough gas to execute a recursive callback. However, this is not reliable with modern gas costs. More explicitly, updating a user's balance to zero before transferring (a "zero-balance" check) or using a withdrawal pattern where the user initiates the transfer are effective state-based mitigations.

05

Formal Verification & Static Analysis

Formal verification uses mathematical proofs to demonstrate a contract's logic is free of specific vulnerabilities, including reentrancy. Static analysis tools (e.g., Slither, MythX) automatically scan source code for patterns that violate the CEI pattern or exhibit other dangerous control flows. Integrating these tools into the development lifecycle is a proactive mitigation strategy to catch vulnerabilities before deployment.

06

Comprehensive Auditing & Testing

Manual security audits by expert reviewers are critical for identifying complex reentrancy paths, including cross-function and cross-contract variants. Supplementing audits with rigorous testing is essential:

  • Unit Tests: Verify CEI pattern adherence.
  • Fuzz Tests: Use random inputs to explore unexpected states (e.g., Echidna).
  • Invariant Tests: Assert that certain state properties (e.g., total supply) always hold, even during reentrant calls. This multi-layered approach is the industry standard for high-value contracts.
DEBUNKED

Common Misconceptions About Reentrancy

Reentrancy attacks are a critical smart contract vulnerability, but several persistent myths can lead to inadequate defenses. This section clarifies common misunderstandings about their mechanics, prevention, and scope.

No, a reentrancy attack is a specific, malicious form of recursion enabled by external calls before state updates. While both involve a function calling itself, benign recursion is a controlled, internal programming pattern. A reentrancy attack exploits the Ethereum Virtual Machine's ability for an external contract (the attacker's) to call back into the original function before its initial execution and, crucially, its state changes are finalized. This creates a race condition where the contract's balance or other state variables are read as unchanged, allowing the same withdrawal or minting logic to be executed multiple times.

Key differentiator: The malicious callback originates from an external contract address, not the function's own internal logic.

REENTRANCY ATTACK

Frequently Asked Questions (FAQ)

A reentrancy attack is a critical smart contract vulnerability where an external contract maliciously calls back into the original function before its initial execution is complete, leading to state inconsistencies and fund theft. These questions address its mechanics, history, and prevention.

A reentrancy attack is a smart contract vulnerability where an external contract's fallback or receive function makes a recursive callback into the calling function before its initial execution finishes, exploiting the order of operations to drain funds. The classic pattern involves a contract that sends Ether before updating its internal balance state. An attacker's contract receives the Ether and, in its fallback function, immediately calls the vulnerable function again. Because the victim's balance hasn't been decremented, the check for sufficient funds passes again, allowing the same withdrawal to be repeated multiple times in a single transaction until gas runs out or funds are exhausted.

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 Directly to Engineering Team
Reentrancy Attack: Definition & Prevention in Smart Contracts | ChainScore Glossary