← Back to Blog

Reentrancy: From The DAO to Euler Finance

2026-03-22 reentrancy smart-contract-security solidity defi-security vulnerability

Reentrancy: From The DAO to Euler Finance

Reentrancy has extracted more money from smart contracts than any other single vulnerability class. The 2016 DAO hack drained ~$60M. Cream Finance lost $130M in October 2021. Euler Finance lost $197M in March 2023. The root cause in each case was the same structural problem, expressed differently each time.

What Is Reentrancy?

When contract A makes an external call to contract B, B can call back into A before A's state has been updated — re-entering A's logic against stale data. The attacker's fallback function triggers the vulnerable function again, and the process repeats until gas runs out or funds are exhausted.

Vulnerable Code

contract VulnerableBank {
    mapping(address => uint256) public balances;

    function withdraw() external {
        uint256 amount = balances[msg.sender];
        require(amount > 0);
        // ⚠️ ETH sent before balance update — reentrancy possible
        (bool success, ) = msg.sender.call{value: amount}("");
        require(success);
        balances[msg.sender] = 0; // Too late!
    }
}

The call transfers ETH to msg.sender before zeroing the balance. An attacker's receive function calls withdraw() again, and balances[msg.sender] is still non-zero.

The DAO (June 2016, ~$60M)

function splitDAO(uint _proposalID) returns (bool) {
    if (!msg.sender.call.value(reward)()) throw; // Transfer first
    totalSupply -= balances[msg.sender]; // State update later
    balances[msg.sender] = 0;
}

The attacker recursively drained the DAO's ETH before the balance update could execute. The incident was severe enough to split Ethereum into ETH and ETC via a contentious hard fork.

Cream Finance (October 2021, ~$130M)

This was a cross-contract reentrancy rather than the classic single-contract pattern. The AMP token implements ERC1820 callbacks — a tokensReceived hook fires on every transfer. An attacker used that callback as a reentry point into Cream's lending logic, borrowing against collateral that was simultaneously being transferred. The lesson here: any token with transfer hooks is a potential reentrancy vector in the contracts that hold it.

Euler Finance (March 2023, ~$197M)

Euler's exploit was not classic reentrancy — it was a logic flaw amplified by a flash loan. The donateToReserves() function transferred eTokens to the reserve without checking whether the donor's position remained healthy afterward. An attacker borrowed a large DAI position via flash loan, deposited into Euler to receive eDAI, then called donateToReserves() to deliberately create an undercollateralized state on their own account. A separate attacker-controlled account then liquidated that position at a discount, capturing more collateral than the debt was worth. Euler later negotiated return of most funds.

The key distinction: this wasn't reentrancy in the traditional sense. It was a missing health-factor check after a state-changing function — but flash loans made it scalable.

Defense 1: CEI Pattern

Check-Effects-Interactions. Update all state before making external calls.

function withdraw() external {
    uint256 amount = balances[msg.sender];
    require(amount > 0);
    balances[msg.sender] = 0;  // Effects first
    (bool success, ) = msg.sender.call{value: amount}(""); // Interactions last
    require(success);
}

CEI alone is sufficient for most single-contract reentrancy. Cross-contract reentrancy requires more.

Defense 2: ReentrancyGuard

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

contract SafeBank is ReentrancyGuard {
    function withdraw() external nonReentrant {
        uint256 amount = balances[msg.sender];
        require(amount > 0);
        balances[msg.sender] = 0;
        (bool success, ) = msg.sender.call{value: amount}("");
        require(success);
    }
}

nonReentrant sets a lock at entry and clears it at exit, blocking any reentrant call regardless of which function is targeted. For systems with multiple interacting contracts, apply the guard consistently across the entire call surface — a lock on one function is useless if an attacker can reenter through another.

Defense 3: Pull Payment

Rather than pushing ETH to the recipient, record the owed amount and let them withdraw it separately. This eliminates the external call from sensitive logic entirely — there's nothing to reenter.

Checklist

Detecting These Issues with ContractScan

Slither + Semgrep + AI triple-layer analysis automatically detects reentrancy patterns.
ContractScan Free Scan

Important Notes

This post is for informational and educational purposes only. It does not constitute financial, legal, or investment advice. The security analysis provided is based on available data and automated tools, which may not capture all potential vulnerabilities. Always conduct a professional audit before deploying smart contracts.

Scan your contract for this vulnerability
Free QuickScan — Unlimited quick scans. No signup required.. No signup required.
Scan a Contract →