Smart Contract Security

Reentrancy Attacks Explained: How DeFi Lost Billions and How to Prevent It

Kennedy OwiroFebruary 18, 202612 min read

In June 2016, an attacker drained 3.6 million ETH (~$60M at the time) from The DAO — one of the largest crowdfunding experiments in history — using a single vulnerability: reentrancy. Nearly a decade later, the same class of bug continues to cost DeFi protocols hundreds of millions. Curve Finance lost $70M in July 2023. Rari Capital lost $80M in 2022. The pattern repeats because developers keep making the same mistakes.

In this article, we'll break down exactly how reentrancy works, walk through real exploit code, examine the modern variants, and show you how to make your contracts immune.

What Is a Reentrancy Attack?

A reentrancy attack exploits the fact that when a smart contract sends ETH or calls an external contract, the receiving contract gets to execute code before the original function finishes. If the original contract hasn't updated its state yet, the attacker can "re-enter" the same function and repeat the action — typically draining funds.

Think of it like a bank teller who gives you cash before marking your withdrawal in the ledger. If you can walk back to the counter before they update the book, you can withdraw again — and again, and again.

The Vulnerable Pattern

// VULNERABLE - DO NOT USE
function withdraw() external {
    uint256 balance = balances[msg.sender];
    require(balance > 0, "No balance");

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

    // 2. Update state AFTER sending — Too late!
    balances[msg.sender] = 0;
}

The problem: msg.sender.call{value: balance}("") transfers ETH and triggers the recipient's receive() or fallback() function. If the recipient is a malicious contract, it can call withdraw() again before balances[msg.sender] = 0 executes.

The Attacker's Contract

contract Attacker {
    VulnerableContract target;

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

    receive() external payable {
        if (address(target).balance >= 1 ether) {
            target.withdraw();  // Re-enter!
        }
    }
}

Real-World Exploits

ProtocolYearLossVariant
The DAO2016$60MClassic reentrancy
Uniswap/Lendf.me2020$25MERC-777 reentrancy
Cream Finance2021$130MCross-contract reentrancy
Rari Capital / Fei2022$80MCross-function reentrancy
Curve Finance2023$70MRead-only reentrancy

Combined, reentrancy has caused over $500M+ in losses across DeFi.

Modern Variants

1. Cross-Function Reentrancy

The attacker re-enters a different function that shares state. During withdraw(), the attacker calls transfer() to move "phantom" balance before it's zeroed out.

2. Cross-Contract Reentrancy

The attacker re-enters a different contract that reads stale state from the first. Especially dangerous in DeFi protocols with multiple interacting contracts.

3. Read-Only Reentrancy

The attacker exploits a view function that returns incorrect values during a callback. The Curve hack exploited a Vyper compiler bug where the reentrancy lock didn't protect get_virtual_price().

4. ERC-777 Token Reentrancy

ERC-777 tokens have built-in hooks (tokensReceived) that execute on the recipient during transfers — exactly what hit Uniswap V1 and Lendf.me.

Prevention

Checks-Effects-Interactions (CEI)

function withdraw() external {
    uint256 balance = balances[msg.sender];
    require(balance > 0);
    balances[msg.sender] = 0;           // Effect FIRST
    (bool success, ) = msg.sender.call{value: balance}("");  // Interaction LAST
    require(success);
}

Reentrancy Guards

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

contract SecureVault is ReentrancyGuard {
    function withdraw() external nonReentrant {
        // Safe: mutex lock prevents re-entry
    }
}

How Vultbase Detects Reentrancy

  1. Slither — Detects external calls before state updates and unprotected callbacks
  2. Pattern DB — 53 reentrancy-specific patterns from real exploits
  3. Cross-Challenge Correlation — Checks for chained vulnerabilities
  4. Engineer Validation — Human review to eliminate false positives

Conclusion

Reentrancy has been DeFi's most consistent killer for nearly a decade. The CEI pattern and reentrancy guards make it entirely preventable. Don't be the next headline. Submit your contracts for a security audit — we try to break them so attackers can't.

reentrancysmart contract securitysolidityDeFivulnerabilityThe DAOweb3 security
Share

Written by

Kennedy Owiro

Founder & CTO, Vultbase

14+ years building security and QA systems at scale. Background in fintech security and Web3 smart contract testing. Built Vultbase's Intelligence Engine with 1,200+ exploit patterns from $40B+ in historical DeFi losses.

Protect your protocol before launch.

Submit your smart contracts for automated security analysis powered by 1,200+ real exploit patterns.

Start Your Audit →