DEV Community

Heemin Kim
Heemin Kim

Posted on • Originally published at contract-scanner.raccoonworld.xyz

Reentrancy: From The DAO to Euler Finance

Reentrancy: From The DAO to Euler Finance

The single vulnerability class that has drained the most funds in smart contract history: reentrancy. From the 2016 DAO hack (~$60M) to Cream Finance in 2021 ($130M) and Euler Finance in 2023 ($197M), it has accounted for billions of dollars in losses.

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-executing logic against stale state.

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!
    }
}
Enter fullscreen mode Exit fullscreen mode

The DAO (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;
}
Enter fullscreen mode Exit fullscreen mode

→ Directly caused the ETH/ETC hard fork

Cream Finance (2021, ~$130M)

The AMP token's ERC1820 callback was used as a cross-contract reentrancy vector. Collateralized AMP was repeatedly borrowed through reentrant calls.

Euler Finance (2023, ~$197M)

donateToReserves() did not validate the health factor, allowing an artificial liquidation state to be created. Combined with a flash loan for the exploit.

Defense 1: CEI Pattern

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);
}
Enter fullscreen mode Exit fullscreen mode

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);
    }
}
Enter fullscreen mode Exit fullscreen mode

Defense 3: Pull Payment

Instead of pushing ETH directly, let the recipient pull it themselves.

Checklist

  • [ ] Is state fully updated before any external calls? (CEI)
  • [ ] Is the nonReentrant modifier applied?
  • [ ] Have ERC777/ERC1155 callback tokens been reviewed?
  • [ ] Has cross-contract reentrancy been considered?

Detecting These Issues with ContractScan

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


Try ContractScan free — automated Solidity vulnerability scanning powered by Slither, Semgrep, Mythril, and AI.

Top comments (0)