3.10 Learning from Past Exploits

Smart contract security has been written in losses. Every major vulnerability class catalogued in Section 3.8 became prominent because some specific contract failed in some specific way and the community learned from the wreckage. The defenses in Section 3.7 exist because earlier protocols did not have them. The audit practices in Section 3.9 exist because earlier audits — or the absence of them — proved insufficient.

This section walks through eight specific exploits in detail. Each one taught the industry something that became part of the standard playbook. Reading these cases is not optional homework for a security-conscious developer — it is the most direct way to internalize why the patterns and anti-patterns matter, beyond the abstract argument that they do.

The cases were not chosen for their dollar value, though most are eye-watering by that measure. They were chosen for what each one taught. The DAO defined reentrancy as a category. Parity defined access-control-plus-delegatecall as a category. bZx defined flash-loan oracle manipulation. Each subsequent case extended or recombined existing categories in ways that produced new defensive patterns. The lessons compounded.

How to Read This Section

Each case study follows a consistent template:

Context — what the protocol was, what it was used for, how much value it held, and when the incident occurred. The setting matters for understanding the consequences.

Vulnerable Code — the actual (or simplified-but-faithful) code pattern that contained the bug. Where the original source is public, the case study quotes it directly. Where it is not, the case study reproduces the pattern with enough fidelity for the bug to be visible.

The Attack — step-by-step reconstruction of how the exploit was executed. Most attacks took place over a few transactions; some required intricate setup. The reconstruction reveals what the attacker did and why each step was necessary.

Root Cause — the underlying flaw, framed in the vocabulary of Section 3.8. Most cases involve multiple compounding root causes; the case study identifies each one and traces how they combined.

Lessons — what the industry learned from the incident, what defensive patterns emerged, and what subsequent protocols built differently. Cross-references to the relevant sections of the book that codify each lesson.

Modern Reproduction — a Foundry test or simplified contract demonstrating the bug pattern in current Solidity. Provides hands-on access to the failure mode rather than only abstract description.

Cross-References — pointers to the relevant patterns (Section 3.7), vulnerabilities (Section 3.8), and architectural concerns (Section 3.11) that each case illustrates.

The template is deliberately the same across cases. Some readers will read the section linearly; others will dip into specific cases that interest them. The consistent structure supports both reading patterns.

The Eight Cases

The cases progress roughly chronologically, which corresponds roughly to increasing complexity of the underlying mechanism:

3.10.1 The DAO (June 2016) — ~3.6M ETH drained ($60M+ at the time). Direct reentrancy. The exploit that prompted Ethereum's only hard fork (creating Ethereum Classic) and established reentrancy as the canonical smart contract vulnerability.

3.10.2 Parity Multi-Sig (July 2017 + November 2017) — Two incidents totaling $30M stolen and $280M permanently frozen. Access control failures and delegatecall to a self-destructing library. The Parity incidents collectively wrote much of the upgradeable-contract security playbook.

3.10.3 bZx (February 2020) — ~$1M across multiple attacks. Flash loan + oracle manipulation. The case that established flash loans as a capital primitive for attacks and oracle manipulation as a first-class vulnerability class.

3.10.4 Poly Network (August 2021) — $611M stolen (then mostly returned). Cross-chain signature verification + access control. One of the largest single-transaction thefts in crypto history.

3.10.5 Ronin Bridge (March 2022) — $625M stolen. Validator key compromise through social engineering. Not a smart contract bug per se, but a watershed moment for bridge security and operational risk.

3.10.6 Nomad Bridge (August 2022) — $190M drained in a "free-for-all" exploit. Initialization bug combined with merkle root validation failure. Notable for the chaotic mass-exploitation pattern — hundreds of independent attackers copy-pasted the exploit.

3.10.7 Wormhole (February 2022)](7-wormhole.md) — $326M drained, fully reimbursed by Jump Crypto. Missing account validation in a Solana program — the verifier accepted a forged "instructions sysvar" account that claimed signatures had been verified. The case is unique in the section as the only non-EVM exploit; its lessons about validating user-supplied account/contract references generalize directly to Solidity.

3.10.8 Euler Finance (March 2023) — $197M drained (then fully returned). Donation-based liquidation logic flaw. The most recent of the major DeFi exploits before this book's writing, demonstrating that mature, well-audited protocols continue to ship novel bugs.

What These Cases Have in Common

Five observations that recur across the cases:

1. The bug was almost always simple in retrospect. A missing modifier, a wrong comparison, an unguarded function, a misapplied signature check. None of these failures required novel cryptography or sophisticated attacks. The expertise was in finding the bug; understanding it after the fact requires only patience.

2. Each case involved multiple compounding issues. Single-bug failures are rare. The DAO needed reentrancy plus the specific structure of the withdrawal logic. Parity needed an unprotected initializer plus a self-destructing library plus delegatecall-based wallets. Wormhole needed missing signature validation plus a missing _disableInitializers() call on an unrelated contract. Defense in depth matters because attackers find compound chains, not isolated bugs.

3. The protocols had been audited. Not every protocol, and not always thoroughly — but most of these were not unaudited. The DAO had been reviewed extensively. Wormhole had been audited by Neodyme. Euler had been audited multiple times. Audits catch many bugs; the bugs here are the ones audits missed, and they reveal the limits of what audits can find.

4. The aftermath produced more secure systems. Each incident produced industry-wide changes: reentrancy guards became standard library components; _disableInitializers() became canonical; oracle manipulation defenses became table-stakes for DeFi. The losses, in a sense, paid for the security improvements that prevented worse subsequent losses.

5. The patterns recur. Despite the lessons, similar bugs keep appearing in new protocols. Read-only reentrancy in 2022 echoed direct reentrancy in 2016. The Wormhole bug in 2022 was the same class as missing modifiers in 2017. Knowing the history is not sufficient to prevent recurrence; the lessons must be applied to each new codebase from scratch.

Reading Order

For a developer encountering this material for the first time, reading the cases in numerical order is the right approach — the chronology matches the rough progression of complexity, and each case builds context for the next.

For a developer revisiting the material with specific concerns:

  • Building a DeFi protocol with oracles? Start with bZx (3.10.3), then Euler (3.10.8).
  • Building an upgradeable contract? Start with Parity (3.10.2), then Wormhole (3.10.7).
  • Building a bridge or cross-chain protocol? Start with Poly Network (3.10.4), Ronin (3.10.5), Nomad (3.10.6), Wormhole (3.10.7) in any order — these four together define the modern bridge-security threat model.
  • Building anything that holds value? The DAO (3.10.1) is the foundational case and remains worth reading first.

On the Numbers

Many of these losses are reported in USD figures at time-of-incident exchange rates. Crypto prices have varied substantially since each event, so the "current" values would differ. The dollar figures are intended to indicate scale and impact at the time, not contemporary value. Where the same incident produced both a recovered and unrecovered loss, both numbers are noted.

The figures also vary across sources. Different post-mortems count different things — sometimes the funds physically moved by the attacker; sometimes the protocol's reported loss; sometimes the realized loss to users after insurance, recovery, or community bailouts. Where the figures conflict significantly, this book cites the figure most commonly used in industry post-mortems, with notes where alternate accountings exist.

Conventions

The conventions used in Section 3.7 and 3.8 apply here:

  • Solidity ^0.8.20 is the modern target, though pre-0.8 contracts are quoted verbatim where the bug depended on pre-0.8 behavior
  • OpenZeppelin contracts are referenced as the standard library
  • Foundry is the primary test framework for any reproduction code

Some quoted code may be pre-Solidity 0.5 or pre-0.8 and use older syntax. Where this is the case, the code is left in its original form — modernizing it would obscure the historical context.

Sections 3.10.1 through 3.10.8 follow.