This article explains reentrancy attacks โ the single most costly category of smart contract exploit in crypto. You'll understand the exact mechanism, see how real attacks unfolded, and learn what defenses exist. If you've bounced off other explanations, the problem was probably that they jumped to code before explaining the logic. We'll fix that here.
What a Reentrancy Attack Actually Is
A smart contract is a program that lives on a blockchain and automatically executes when someone interacts with it. A reentrancy attack exploits a specific flaw: a contract sends funds to an outside address before it updates its own records. The attacker uses that gap to call back into the contract and withdraw again โ and again โ before the contract ever realizes the first withdrawal happened.
- The name comes from the attacker "re-entering" the vulnerable contract's function while that function is still running
- The contract's balance sheet (its state) still shows the old, pre-withdrawal amount during the attack
- Each reentry triggers another withdrawal based on stale data
- The loop continues until the contract is drained or the transaction runs out of gas (the fee that fuels computation on Ethereum)
- This isn't a hack of the blockchain itself โ it's a logic error in one contract's code
What this means practically: The contract is like a bank teller who hands you cash, then checks your balance. If you can keep asking before they check, you keep getting paid.
The Attack Step by Step
The mechanics follow a precise sequence. Every reentrancy attack is a variation on these steps, so understanding the order is the key to understanding everything else.
1. Attacker deploys a malicious contract. This contract is purpose-built to interact with the vulnerable one. It contains a fallback function โ a piece of code that automatically executes whenever the contract receives ETH. This matters because the fallback function is where the reentry happens.
2. Attacker deposits funds into the vulnerable contract. This creates a legitimate balance that entitles the attacker to withdraw. The deposit is real โ the attack exploits the withdrawal process, not the deposit.
3. Attacker calls the withdraw function. The vulnerable contract checks the attacker's balance, confirms it's valid, and sends ETH to the attacker's contract.
4. The ETH transfer triggers the attacker's fallback function. Before the vulnerable contract has updated the attacker's balance to zero, the fallback function calls withdraw again.
5. The vulnerable contract checks the balance โ which hasn't changed yet. It still reads the original deposit amount. So it sends ETH again.
6. Steps 4 and 5 repeat until the contract is empty or gas runs out.
7. The vulnerable contract finally updates the balance โ but there's nothing left to protect.
What this means practically: The entire exploit hinges on one design mistake: sending money before updating the ledger.
The DAO Hack โ The Attack That Split Ethereum
The most consequential reentrancy attack happened in June 2016. The DAO (Decentralized Autonomous Organization) was an investment fund run by a smart contract on Ethereum. It held roughly $150 million in ETH at the time โ about 14% of all ETH in existence.
- An attacker used exactly the reentrancy pattern described above to drain approximately 3.6 million ETH (~$60 million at the time)
- The DAO's withdraw function sent ETH before zeroing out the caller's balance
- The Ethereum community responded by executing a hard fork โ a permanent split of the blockchain that reversed the theft, creating two chains: Ethereum (ETH) and Ethereum Classic (ETC)
- This remains one of the defining events in crypto history, raising fundamental questions about immutability and governance
What this means practically: Reentrancy isn't a theoretical risk. It literally split the second-largest blockchain in two.
Three Variants of Reentrancy
This is where most explanations go wrong โ they describe reentrancy as one thing. It has at least three distinct forms, and newer exploits tend to use the less obvious ones.
- Single-function reentrancy: The attacker reenters the same function that's still executing. This is the classic DAO-style attack and the easiest to spot.
- Cross-function reentrancy: The attacker reenters a different function on the same contract that shares the same state variable. If function A sends ETH and function B reads the same balance, the attacker can call B during A's execution and exploit the stale data.
- Cross-contract reentrancy: The attacker targets two separate contracts that share state or depend on each other. This variant appeared in several DeFi exploits from 2020 onward and is harder to catch in audits because the vulnerability spans multiple codebases.
What this means practically: Defending only against single-function reentrancy leaves two other attack surfaces open.
How Contracts Defend Against Reentrancy
Three defenses are standard in modern Solidity development. Each addresses the problem from a different angle, and serious contracts typically use more than one.
- Checks-Effects-Interactions pattern: The contract performs all checks first (is the balance sufficient?), then updates state (sets balance to zero), and only then interacts with external addresses (sends ETH). By the time the attacker's fallback function fires, the balance is already zeroed โ reentering the withdraw function finds nothing to withdraw.
- Reentrancy guards (mutex locks): A mutex is a variable that locks the function while it's executing. The contract sets a flag to "locked" at the start and "unlocked" at the end. Any reentrant call hits the lock and fails. OpenZeppelin's ReentrancyGuard is the most widely used implementation.
- Pull over push payments: Instead of the contract sending ETH directly (push), it records what's owed and lets the user withdraw it separately (pull). This eliminates the external call during the state-changing function entirely.
What this means practically: The Checks-Effects-Interactions pattern is the minimum standard. A reentrancy guard adds a second safety net. Pull payments restructure the problem away.
Why Reentrancy Attacks Still Happen
Given that the DAO hack was in 2016 and the defenses are well-known, you'd expect reentrancy to be a solved problem. It isn't.
- DeFi protocols compose with each other โ one contract calls another calls another โ creating cross-contract reentrancy paths that didn't exist when each was audited individually
- New token standards and features (like ERC-777 tokens with transfer hooks) reintroduce callback mechanisms that can trigger reentrancy in contracts that didn't anticipate them
- Curve Finance lost approximately $70 million in July 2023 due to a reentrancy vulnerability in the Vyper compiler โ the bug wasn't in the contract logic but in the language tooling itself
- Audit coverage varies wildly; many deployed contracts have never been audited, and even audited contracts can have bugs in their integrations
What this means practically: Reentrancy is a category of vulnerability, not a single bug. New forms keep emerging as the ecosystem grows more complex.
Quick Recap
- A reentrancy attack exploits the gap between a contract sending funds and updating its own records โ the attacker calls back in during that gap to withdraw repeatedly
- The 2016 DAO hack drained ~$60 million using this exact flaw and led to Ethereum's most controversial hard fork
- Three variants exist (single-function, cross-function, cross-contract), and the newer forms are harder to detect
- Standard defenses โ Checks-Effects-Interactions, reentrancy guards, and pull payments โ work when applied, but composability and new token standards keep creating fresh attack surfaces