Back to Blog8 min read
Smart Contracts

Reentrancy Attack in Solidity: What It Is, Real Examples & How to Prevent It (2026)

Learn what reentrancy attacks are, how the $60M DAO hack happened, and how to prevent reentrancy in Solidity with CEI pattern, ReentrancyGuard, and pull payment examples. Free vulnerability checker included.

August 6, 2026
8 min read
3 views
✓ Written by blockchain developers·✓ Reviewed for technical accuracy·✓ Updated August 2026
Reentrancy Attack in Solidity:  What It Is, Real Examples & How to Prevent It (2026)

Reentrancy Attack in Solidity: What It Is, Real Examples, and How to Prevent It (2026)

A reentrancy attack occurs when a malicious smart contract repeatedly calls a vulnerable contract's withdraw function before the balance updates — draining all funds in a loop. It is the most common smart contract vulnerability and caused the $60 million DAO hack in 2016. Use the free Blockhertz AI Smart Contract Auditor to check your contracts for reentrancy vulnerabilities in under 60 seconds.


What is a Reentrancy Attack?

A reentrancy attack exploits the order of operations in a smart contract. When a contract sends ETH to an external address before updating its own state, an attacker can use a malicious contract to call back into the vulnerable contract before the state update happens.

The result: the contract's balance check passes every time, and the attacker withdraws funds repeatedly until the contract is empty.


The $60 Million DAO Hack — Real World Example

In June 2016, an attacker exploited a reentrancy vulnerability in The DAO — a decentralized autonomous organization holding $150 million in ETH. The attacker drained $60 million before the team could react.

The attack was so significant it led to the Ethereum hard fork that created Ethereum (ETH) and Ethereum Classic (ETC).

The vulnerability was simple:

  1. Attacker calls withdraw()
  2. Contract checks balance — passes
  3. Contract sends ETH to attacker
  4. Attacker's fallback function calls withdraw() again
  5. Contract checks balance again — still not updated
  6. Contract sends ETH again
  7. Loop repeats until contract is empty

Vulnerable Code Example

// VULNERABLE — do not use
contract VulnerableBank {
    mapping(address => uint256) public balances;

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

    function withdraw() external {
        uint256 amount = balances[msg.sender];
        require(amount > 0, "No balance");

        // ❌ Sends ETH BEFORE updating balance
        (bool success, ) = msg.sender.call{value: amount}("");
        require(success, "Transfer failed");

        // ❌ Balance updated AFTER sending — too late
        balances[msg.sender] = 0;
    }
}

The attacker exploits this with a malicious contract:

// ATTACKER CONTRACT
contract Attacker {
    VulnerableBank public target;

    constructor(address _target) {
        target = VulnerableBank(_target);
    }

    function attack() external payable {
        target.deposit{value: msg.value}();
        target.withdraw();
    }

    // Fallback called every time ETH is received
    receive() external payable {
        if (address(target).balance >= msg.value) {
            target.withdraw(); // calls withdraw again!
        }
    }
}

How to Prevent Reentrancy Attacks

Fix 1 — Checks-Effects-Interactions Pattern (CEI)

The most important rule in Solidity: always update state BEFORE making external calls.

// SECURE — CEI pattern applied
contract SecureBank {
    mapping(address => uint256) public balances;

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

    function withdraw() external {
        uint256 amount = balances[msg.sender];
        
        // ✅ CHECK — verify condition
        require(amount > 0, "No balance");

        // ✅ EFFECT — update state first
        balances[msg.sender] = 0;

        // ✅ INTERACTION — send ETH last
        (bool success, ) = msg.sender.call{value: amount}("");
        require(success, "Transfer failed");
    }
}

Fix 2 — ReentrancyGuard (OpenZeppelin)

OpenZeppelin's ReentrancyGuard adds a mutex lock that prevents reentrant calls:

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

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

    function withdraw() external nonReentrant {
        uint256 amount = balances[msg.sender];
        require(amount > 0, "No balance");
        
        balances[msg.sender] = 0;
        
        (bool success, ) = msg.sender.call{value: amount}("");
        require(success, "Transfer failed");
    }
}

The nonReentrant modifier sets a lock before execution and releases it after — making reentrant calls revert automatically.

Fix 3 — Pull Payment Pattern

Instead of pushing ETH to users, let them pull it themselves:

contract PullPayment {
    mapping(address => uint256) private pendingWithdrawals;

    function allowWithdrawal(
        address recipient, 
        uint256 amount
    ) internal {
        pendingWithdrawals[recipient] += amount;
    }

    function withdraw() external {
        uint256 amount = pendingWithdrawals[msg.sender];
        require(amount > 0, "Nothing to withdraw");
        
        pendingWithdrawals[msg.sender] = 0;
        
        (bool success, ) = msg.sender.call{value: amount}("");
        require(success, "Transfer failed");
    }
}

Reentrancy Attack Checklist

Check Status
State updated before external calls ✅ Required
ReentrancyGuard on withdraw functions ✅ Recommended
No external calls in loops ✅ Required
Return values from call() checked ✅ Required
Pull payment pattern for ETH distribution ✅ Recommended

Cross-Function Reentrancy

Reentrancy can also happen across multiple functions in the same contract. If two functions share state and one makes an external call, an attacker can exploit both:

// VULNERABLE — cross-function reentrancy
contract CrossFunctionVuln {
    mapping(address => uint256) public balances;
    
    function withdraw() external {
        uint256 amount = balances[msg.sender];
        // External call before state update
        (bool success, ) = msg.sender.call{value: amount}("");
        require(success);
        balances[msg.sender] = 0;
    }
    
    function transfer(address to, uint256 amount) external {
        // Attacker calls this during reentrance
        // balance still shows old value!
        require(balances[msg.sender] >= amount);
        balances[msg.sender] -= amount;
        balances[to] += amount;
    }
}

Fix: Apply ReentrancyGuard to all functions that share state, not just withdraw.


Read-Only Reentrancy

A newer attack vector (2022+) where read-only functions are exploited during a reentrant call when state is temporarily inconsistent:

  • Price oracle reads stale state during reentrancy
  • Flash loan attacks combine with read-only reentrancy
  • Multiple DeFi protocols exploited this way in 2022-2023

Fix: Add ReentrancyGuard to view functions that read critical state if they are used as price oracles by other protocols.


Check Your Contracts for Reentrancy

Detecting reentrancy manually requires reviewing every function that makes external calls and verifying the CEI pattern is followed throughout. In complex contracts with multiple inheritance and libraries, this is easy to miss.

The Blockhertz AI Smart Contract Auditor automatically detects reentrancy vulnerabilities in your Solidity, Rust, Move, or Vyper contracts in under 60 seconds.

  1. Go to blockhertz.com/tools/ai-auditor
  2. Paste your smart contract
  3. Click Audit Contract
  4. Get a full security report with risk score and fix recommendations

Free to start — no signup required: blockhertz.com/tools/ai-auditor


Summary

  • Reentrancy = attacker calls back into your contract before state updates
  • The DAO hack = $60M stolen via reentrancy in 2016
  • CEI pattern = always update state before external calls
  • ReentrancyGuard = add nonReentrant modifier to all sensitive functions
  • Cross-function and read-only reentrancy = newer variants to watch for

Automatically detect reentrancy and 10+ other vulnerabilities in your smart contracts at blockhertz.com/tools/ai-auditor — free, no signup required.

Views

3

Read Time

8 min read

Likes

0

Published

Aug 6, 2026

smart contract securitysolidityblockchainweb3AI Toolsreentrancysecurity
smart contract securitysolidityblockchainweb3AI Toolsreentrancysecurity

SIGNAL THREAD

00 SIGNALS

NO SIGNALS YET — BE FIRST TO TRANSMIT

Technical Writer Team Blockhertz

Blockchain & Web3 Innovator

Blockhertz is a collective of blockchain developers, architects, and innovators dedicated to building next-gen Web3 solutions. Our team specialises in DeFi, tokenomics, smart contracts, and distributed systems.