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
| Protocol | Year | Loss | Variant |
|---|---|---|---|
| The DAO | 2016 | $60M | Classic reentrancy |
| Uniswap/Lendf.me | 2020 | $25M | ERC-777 reentrancy |
| Cream Finance | 2021 | $130M | Cross-contract reentrancy |
| Rari Capital / Fei | 2022 | $80M | Cross-function reentrancy |
| Curve Finance | 2023 | $70M | Read-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
- Slither — Detects external calls before state updates and unprotected callbacks
- Pattern DB — 53 reentrancy-specific patterns from real exploits
- Cross-Challenge Correlation — Checks for chained vulnerabilities
- 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.