---
title: "Reentrancy Attack Explained: How to Prevent Reentrancy in Solidity"
description: "Learn how reentrancy attacks exploit smart contracts and discover proven Solidity prevention techniques. Protect your DeFi projects today with expert insights."
author: "Constantine Manko"
date: 2026-05-15
lang: en
keywords: "Smart Contract Security, Ethereum, Solidity, DeFi Security"
canonical_url: "https://soken.dev/blog-reentrancy-attacks-explained.html"
---

Reentrancy attacks remain one of the most notorious and persistent threats to smart contract security on Ethereum and EVM-compatible chains. Despite years of industry efforts, new protocols continue to face reentrancy vulnerabilities, leading to significant financial losses—sometimes reaching millions of dollars. These exploits often appear in DeFi lending, staking, and bridge contracts where external calls and token transfers are common.

In this article, we dissect the mechanics of a reentrancy attack, identify typical vulnerable contract patterns, and share tested mitigation strategies. Drawing from our analysis of over 255 smart contract audits at Soken, we highlight how nuanced Solidity coding practices and comprehensive testing workflows can thwart reentrancy threats. This piece also links to related risks such as delegatecall misuse and flash loan tactics to provide a rounded understanding essential for any serious Web3 developer or security auditor.

---

## What is a Reentrancy Attack and How Does It Exploit Smart Contracts?

A reentrancy attack occurs when a malicious contract repeatedly calls a vulnerable contract’s function before the initial execution completes, exploiting the contract’s inconsistent state.

In practice, a reentrancy vulnerability arises from poorly ordered state changes and external calls inside a contract. When the contract transfers ETH or tokens via a call or transfer to an untrusted address, that recipient can reenter the vulnerable function recursively before state variables update. This allows the attacker to drain funds or manipulate contract logic unexpectedly.

For example, the infamous DAO hack in 2016 resulted in nearly $60 million being exploited through reentrancy flaws — a cautionary tale. Since then, the industry has identified that the root cause is the contract’s balance or state being updated only *after* external calls, creating an inconsistent state window the attacker exploits.

**Expert insight from Soken audits:** “In over 40% of our recent DeFi security audits, reentrancy patterns appear in some form, often subtle variations involving proxy contracts or layered calls. Identifying these requires thorough inter-contract call analysis beyond single contract inspection.”

---

## Common Solidity Patterns That Cause Reentrancy Vulnerabilities

Reentrancy vulnerabilities typically stem from a specific sequence of operations: external calls (like ETH transfers or token calls) occurring before the contract updates its internal state variables.

The most vulnerable pattern looks like this:

```solidity
function withdraw(uint256 amount) public {
    require(balances[msg.sender] >= amount);
    (bool success, ) = msg.sender.call{value: amount}("");
    require(success);
    balances[msg.sender] -= amount;
}
```

Here the external call using `call.value()` happens **before** deducting the user’s balance. During that external call, an attacker’s fallback function can recursively invoke `withdraw()`, draining funds before balances update.

Key risky constructs include:

- **External calls before state updates:** Sending ETH or tokens before updating balances.
- **Using `call` or `send` without precautions:** External call via low-level functions bypasses checks and can invoke fallback functions.
- **Lack of reentrancy guards:** No mutexes or other locking mechanisms.
- **Complex call chains with delegatecall:** These can indirectly open reentrancy via external calls within called contracts.

By contrast, the safer pattern reverses the order:

```solidity
function withdraw(uint256 amount) public {
    require(balances[msg.sender] >= amount);
    balances[msg.sender] -= amount;  // state update first
    (bool success, ) = msg.sender.call{value: amount}("");
    require(success);
}
```

---

## How to Prevent Reentrancy Attacks in Solidity: Best Practices & Tools

The most effective defense against reentrancy is **updating state before external calls combined with explicit reentrancy protection patterns**.

### Standard mitigation techniques:

1. **Checks-Effects-Interactions Pattern:**  
   Always update internal state before performing external calls. This minimizes inconsistent contract state during external calls.

2. **Reentrancy Guards / Mutexes:**  
   Solidity’s `ReentrancyGuard` from OpenZeppelin uses a simple mutex to block nested calls. Incorporate it as:

```solidity
import "@openzeppelin/contracts/security/ReentrancyGuard.sol";

contract SecureContract is ReentrancyGuard {
    mapping(address => uint256) balances;

    function withdraw(uint256 amount) public nonReentrant {
        require(balances[msg.sender] >= amount);
        balances[msg.sender] -= amount;
        (bool success, ) = msg.sender.call{value: amount}("");
        require(success);
    }
}
```

3. **Limit External Calls:**  
   Minimize transfers or interactions within critical functions—use pull over push payments where users explicitly withdraw funds.

4. **Static Analysis and Automated Tools:**  
   Tools like Slither and Soken’s own audit frameworks flag reentrancy.

5. **Use Latest Solidity Versions & Features:**  
   Solidity 0.8+ introduces built-in overflow checks, but reentrancy risks still persist, so combine code safety with design patterns.

**Soken methodology:** Our audits integrate manual threat modeling, symbolic execution, and fuzz testing, which detects complex reentrancy flows often missed by generic scanners.

---

## Real-World Examples of Reentrancy Exploits and Their Impact

Reentrancy attacks remain prevalent despite increased awareness. Below is a comparison of notable hacks illustrating the diversity and cost of such exploits:

| Incident                | Date       | Loss Amount      | Pattern Exploited                  | Key Lesson                          |
|-------------------------|------------|------------------|----------------------------------|-----------------------------------|
| The DAO Hack            | 2016-06    | ~$60 million     | Reentrancy in splitDAO proposal  | Update state before external call |
| The Lendf.Me Hack       | 2020-04    | $25 million+     | Flash loan amplified reentrancy  | Combine reentrancy guard + flash loan resilience |
| Euler Finance Hack      | 2023-03    | $197 million     | Nested reentrancy + delegatecall | Complex call chains increase risk |
| Recent DeFi Bridge Hack | 2025-11    | $15 million      | Token transfer reentrancy exploit | Audit cross-contract interactions|

*Note:* Soken’s audits emphasize layered security, especially around cross-contract calls and token standards that may trigger fallback functions dynamically.

---

## Delegatecall and Flash Loan Vectors: Amplifying Reentrancy Risks

Delegatecall, which executes code in the calling contract’s context, can obscure reentrancy risk and increase attack surface, especially combined with flash loans. 

Reentrancy attacks often leverage flash loans to maximize capital for recursive draining:

- Flash loans provide large immediate liquidity in a single transaction.
- An attacker uses flash-loaned funds to trigger reentrant calls to drain protocol liquidity before repaying the loan.
- Delegatecall usage may inadvertently trigger external untrusted code altering contract state or allowing reentries.

**Security insight:** “In Soken’s experience, reentrancy mitigation extends beyond single contracts to protocol-wide design — especially when delegatecall and flash loans coexist. Contracts must combine reentrancy guards with entrance validation and flash-loan-specific checks (loan amount caps, limits on recursive calls).”

---

## Comparison Table: Common Reentrancy Prevention Techniques

| Technique                | Description                               | Pros                                  | Cons                               | Usage                                    |
|--------------------------|-------------------------------------------|---------------------------------------|-----------------------------------|------------------------------------------|
| Checks-Effects-Interactions | Update state before external calls        | Simple, effective                     | Requires discipline                | Baseline security practice                |
| Reentrancy Guard (mutex) | Use Solidity modifiers to prevent reentry | Blocks recursive calls explicitly     | Adds slight gas overhead           | Recommended for all withdrawal functions  |
| Pull Payment Pattern     | Let users withdraw funds voluntarily       | Minimizes auto transfer risk          | Requires user interaction          | Ideal for escrow and staking contracts    |
| Static and Dynamic Analysis | Automated and manual code audits          | Detect vulnerabilities early          | May miss complex cross-contract flows | Essential for pre-deployment assurance    |
| Flash Loan & Delegatecall Checks | Validate caller and transaction context | Prevents loan-amplified reentrancy    | Complex to implement               | Critical in DeFi protocols with loans     |

---

## Solidity Code Example: A Reentrancy Vulnerable Contract and Its Fix

Here’s a minimal vulnerable contract and its hardened version using a reentrancy guard:

```solidity
// Vulnerable: withdrawal before balance update
contract Vulnerable {
    mapping(address => uint256) public balances;

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

    function withdraw(uint256 amount) external {
        require(balances[msg.sender] >= amount, "Insufficient funds");
        (bool success, ) = msg.sender.call{value: amount}("");
        require(success, "Transfer failed");
        balances[msg.sender] -= amount; // Vulnerable: state updated after external call
    }
}
```

Fixed with OpenZeppelin’s ReentrancyGuard and effects before interaction:

```solidity
import "@openzeppelin/contracts/security/ReentrancyGuard.sol";

contract Secure is ReentrancyGuard {
    mapping(address => uint256) public balances;

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

    function withdraw(uint256 amount) external nonReentrant {
        require(balances[msg.sender] >= amount, "Insufficient funds");
        balances[msg.sender] -= amount; // State updated before external call
        (bool success, ) = msg.sender.call{value: amount}("");
        require(success, "Transfer failed");
    }
}
```

---

**Pro tip:** Always combine checks-effects-interactions with reentrancy guards. Auditing contract call graphs and testing with fuzzing tools can reveal subtle multi-hop reentrancy vulnerabilities before deployment.

---

Reentrancy vulnerabilities remain a critical threat to smart contracts, especially in complex DeFi ecosystems using flash loans and delegatecall. The most effective defenses combine code discipline (checks-effects-interactions), explicit reentrancy guards, rigorous audits, and context-aware transaction validation.

At Soken, our security research incorporates manual and automated techniques honed from 255+ smart contract audits, consistently identifying and remediating reentrancy and associated risks. Leveraging best practices and thorough testing methodologies is imperative for every project handling user funds.

---

**Need expert security guidance?** Soken's team of auditors has reviewed 255+ smart contracts and secured over $2B in protocol value. Whether you need a [comprehensive audit](/services-it.html), a [free security X-Ray assessment](/xray), or help navigating [crypto regulations](/crypto-map/), we are ready to help.

[Talk to a Soken expert](https://t.me/kmanok) | [View our audit reports](https://github.com/sokenteam)

## Frequently Asked Questions

### What is a reentrancy attack in smart contracts?

A reentrancy attack occurs when a malicious contract repeatedly calls back into the victim contract before the first invocation completes, exploiting external calls to drain assets or alter state unexpectedly.

### Which smart contract patterns are most vulnerable to reentrancy?

Contracts that perform external calls, such as token transfers or calling other contracts, before updating internal states are highly vulnerable due to the risk of recursive reentry during these calls.

### How can reentrancy vulnerabilities be prevented in Solidity?

Common prevention methods include using the Checks-Effects-Interactions pattern, applying the 'ReentrancyGuard' modifier from OpenZeppelin, and minimizing external calls or using pull over push payment methods.

### What tools help detect reentrancy vulnerabilities before deployment?

Automated security analyzers like MythX, Slither, and Securify can detect reentrancy patterns. Additionally, thorough manual code audits and fuzz testing improve vulnerability identification.

### Are delegatecall and flash loans related to reentrancy attacks?

Yes, delegatecall misuse can enable reentrancy exploits by executing code in the context of another contract. Flash loans can be used by attackers to amplify impact, though they are not reentrancy themselves.
