On March 13, 2023, at 08:50 UTC, an attacker known only as "Jacob" executed the largest DeFi exploit of the year — draining $197 million from Euler Finance, a lending protocol on Ethereum. What made this hack extraordinary wasn't just the amount, but the surgical precision: a single overlooked function, introduced through a governance proposal, combined with a novel leverage mechanism to create a perfect storm. Even more remarkably, the attacker returned every recoverable cent within three weeks.
This is the story of how a "donation" function broke a $264M protocol, why the governance process failed to catch it, and what every DeFi builder must learn from the donateToReserves vulnerability.
What Happened: Timeline of the Attack
The attack unfolded in minutes but had been months in the making — ever since Euler Improvement Proposal 14 (eIP-14) went live.
| Time (UTC) | Event |
|---|---|
| 08:50 | Attacker takes 30M DAI flash loan from AAVE |
| 08:51 | Deposits to Euler, mints 10x leverage, calls donateToReserves |
| 08:52 | Self-liquidates across 5 pools (DAI, USDC, wBTC, stETH, WETH) |
| 08:53 | Repays flash loan, extracts ~$197M profit |
| 08:55 | Funds dispersed to multiple addresses |
| Mar 25–28 | Attacker returns all funds after negotiations |
| Apr 3 | Full recovery confirmed; protocol plans restart |
The speed was staggering. In under three minutes, a 30M DAI flash loan — capital the attacker never owned — powered a $197M exploit. That's a 6,500x leverage ratio with zero upfront capital at risk. The flash loan wasn't the vulnerability; it was the force multiplier.
The Root Cause: donateToReserves and eIP-14
The Fateful Governance Proposal
Euler Improvement Proposal 14 (eIP-14) was a community governance proposal designed to add a "donate to reserves" feature. The intention was noble: allow users to voluntarily contribute collateral to the protocol's reserves, strengthening the system. The function EToken::donateToReserves was added to the Euler Token (eToken) contract.
`solidity // Simplified vulnerable function function donateToReserves(uint256 amount) external { _burn(msg.sender, amount); // Burns eTokens (collateral) totalReserves += amount; // Adds to protocol reserves // NO healthFactor() check! // NO dToken (debt) burning! } `
What the Function Actually Did
The function had two critical flaws that, in isolation, might have been caught — but together, they were catastrophic:
- Burns collateral (eTokens) without a health check — The protocol's core invariant is that every position must maintain a health factor ≥ 1.0 (collateral value ≥ debt value).
donateToReservesreduced collateral but never verified the position remained solvent.
- Does NOT burn debt (dTokens) — When you deposit collateral, you receive eTokens. When you borrow, you receive dTokens (debt tokens). The function burned eTokens but left dTokens untouched. This created asymmetric state change: collateral down, debt unchanged = instant bad debt.
Why This Passed Review
eIP-14 passed community governance because:
- The feature sounded beneficial (strengthening reserves)
- Code review focused on the "happy path" (user donates excess collateral)
- No one modeled the adversarial path (user donates to intentionally break their own position)
- Formal verification was not required for governance proposals
- The interaction with
mint()(self-collateralized leverage) was not analyzed
Lesson: Governance approval ≠ security audit. DAOs must separate feature approval from invariant testing.
Self-Collateralized Leverage: The Force Multiplier
Euler's mint() function enabled a novel primitive: borrowing against your own deposit in the same transaction. This wasn't traditional overcollateralized lending — it was recursive leverage.
How mint() Works
`solidity function mint(uint256 assets) external returns (uint256 shares) { // User deposits assets, receives eTokens // Then immediately borrows against those SAME eTokens // Creates recursive loop: deposit → borrow → deposit → borrow... _mint(msg.sender, shares); } `
The 10x Leverage Loop
The attacker exploited this to achieve 10x leverage in a single transaction:
- Deposit 20M DAI → Receive 20M eDAI (collateral)
- Call mint() → Borrow against eDAI → Receive dDAI (debt) → Convert to more DAI → Deposit again → Repeat
- Result: 195.6M eDAI collateral, 200M dDAI debt — all from 20M initial deposit
This self-collateralized leverage is the force multiplier. Without it, the attacker couldn't build a large enough position to make donateToReserves profitable. With it, a 30M DAI flash loan became a $197M weapon.
Soft Liquidation: The Exploit Mechanism
Euler used a "soft liquidation" mechanism — a gradual discount that increases as collateral ratio decreases. This was designed to be user-friendly (avoiding cliff-edge liquidations) but created an exploitable price oracle for attackers.
How Soft Liquidation Works
- Healthy position (collateral ratio > 1.2x): ~5% liquidation discount
- Moderate risk (1.0x–1.2x): 10–25% discount
- Underwater (< 1.0x): 50%+ discount, scaling dynamically
The Attacker's Calculation
By calling donateToReserves(100M eDAI), the attacker deliberately pushed their position from healthy (195.6M collateral vs 200M debt) to deeply underwater (95.6M collateral vs 200M debt).
The dynamic discount meant:
- Liquidator (attacker) could buy 200M dDAI debt for ~100M DAI worth of collateral
- Attacker's cost: 100M eDAI donated + gas
- Attacker's gain: ~190M DAI worth of assets from liquidation
- Net per pool: ~8.9M DAI profit
Critical insight: The attacker didn't need an external liquidator — they liquidated themselves via a separate contract. The protocol's own mechanism paid them to exploit it.
Step-by-Step Attack Walkthrough
Let's trace the exact execution across one pool (DAI), then multiply by 5.
Phase 1: Capital Acquisition (Flash Loan)
` AAVE Flash Loan → 30M DAI (no collateral, single transaction) ` Cost: Gas only. Risk: Zero — if attack fails, loan reverts.
Phase 2: Position Building
` Deposit 20M DAI → 20M eDAI (collateral tokens) mint() × 10 iterations → 195.6M eDAI collateral, 200M dDAI debt ` The mint() function allows borrowing against freshly minted eTokens in the same transaction. Each iteration: deposit → borrow → deposit → borrow.
Phase 3: The Vulnerable Call
` donateToReserves(100M eDAI) `
- Burns 100M eDAI (collateral reduced to 95.6M)
- dDAI debt remains 200M (never touched)
- Health factor: 95.6M / 200M = 0.478 (< 1.0 = underwater)
- Protocol reserves increase by 100M eDAI (attacker "donates" to protocol)
Phase 4: Self-Liquidation
` Liquidate own position via separate contract Receive discounted collateral: ~190M DAI value for 200M dDAI debt ` The liquidator (attacker's contract) pays off the 200M dDAI debt and receives the underwater collateral at the dynamic discount (~50%+).
Phase 5: Profit Extraction
` Withdraw ~38.9M DAI from liquidation Repay 30M DAI flash loan + fees Keep ~8.9M DAI profit ` Repeat across USDC, wBTC, stETH, WETH pools → $197M total.
The Unprecedented Recovery
What happened next stunned the industry. Instead of disappearing into mixers, the attacker — "Jacob" — began negotiating.
Negotiation Timeline
- March 14: Euler offers 10% white-hat bounty ($19.7M) for full return
- March 20: Attacker contacts team via on-chain message
- March 25–28: Funds returned in batches:
- 84,951 ETH (~$147.8M at the time)
- $29.9M DAI
- April 3: Euler confirms full recovery of all reclaimable funds
Why Did They Return It?
Theories abound:
- White-hat claim: Attacker argued they exposed the bug to save users (disputed — they exploited 5 pools first)
- Traceability: Funds were highly traceable; mixing $197M is nearly impossible
- Legal pressure: Euler engaged law enforcement; attacker may have faced identification
- Reputation: "Jacob" may want future white-hat credibility
Regardless of motive, this set a new precedent: post-hack negotiation can work. But it's not a defense strategy — it's a lucky outcome.
Lessons Learned: Beyond the Code
1. The "Donation" Anti-Pattern
donateToReserves represents a dangerous pattern: state mutation without invariant validation. Any function that reduces collateral, increases debt, or modifies core accounting must verify invariants after the change. This pattern appears elsewhere:
transferToVault()in yield aggregatorsdonateToInsuranceFund()in options protocolsburnForYield()in rebasing tokens
Audit rule: Every burn, transfer, or mint affecting collateral/debt must be followed by require(healthFactor() >= 1).
2. Self-Collateralized Leverage = Recursive Risk
Euler's mint() created a leverage primitive that bypassed traditional overcollateralization. The combination of:
- Recursive leverage (borrow against own deposit)
- No health check on collateral reduction
- Dynamic liquidation discount
...is a vulnerability class, not a one-off bug. Protocols with similar mechanics (Gearbox, Morpho, Notional) should audit their leverage loops against collateral reduction functions.
3. Governance Process Failure
eIP-14 passed without:
- Formal verification of new invariants
- Invariant testing (health factor preservation under all paths)
- Economic attack modeling (what if user wants to go underwater?)
- Integration testing with existing leverage functions
DAOs must require: Security audit + invariant test suite + economic review before any state-changing function goes live.
4. Soft Liquidation Design Flaw
The dynamic discount mechanism, while user-friendly, created an attacker-controlled oracle. The attacker could precisely calculate the profitable liquidation threshold because the discount formula was public and deterministic.
Hard liquidation (fixed penalty, e.g., 10% flat) would have been more resistant — the attacker couldn't "tune" the discount to their exact position.
5. Flash Loans as Force Multipliers
The attack required zero capital — a 30M DAI flash loan powered a $197M exploit. Any protocol with:
- Flash loan accessibility (AAVE, Balancer, Uniswap)
- State-changing functions without reentrancy guards
- Collateral/debt accounting mutations
...needs extra scrutiny. Flash loans don't cause bugs; they amplify them 6,500x.
FAQ
Q: What is the Euler Finance hack?
A: The Euler Finance hack was a $197 million exploit on March 13, 2023, where an attacker used a vulnerability in the donateToReserves function (introduced via eIP-14) combined with self-collateralized leverage to drain 5 lending pools on Ethereum.
Q: How did the donateToReserves vulnerability work?
A: The donateToReserves function burned collateral tokens (eTokens) without checking if the position remained solvent, and without burning the corresponding debt tokens (dTokens). This created instant bad debt that the attacker could exploit via self-liquidation at a steep discount.
Q: What is self-collateralized leverage in DeFi?
A: Self-collateralized leverage allows users to borrow against their own deposit in the same transaction, creating recursive leverage loops. Euler's mint() function enabled 10x leverage from a 20M DAI deposit, turning a 30M DAI flash loan into a $197M attack.
Q: How much was recovered from the Euler hack?
A: All recoverable funds were returned by April 3, 2023 — 84,951 ETH (~$147.8M) and $29.9M DAI, totaling ~$197M. This is one of the largest recoveries in crypto history.
Q: What lessons should DeFi protocols learn from the Euler hack?
A: 1) Every state-changing function must verify invariants post-execution. 2) Governance proposals need security audits, not just community votes. 3) Recursive leverage + collateral reduction = critical risk. 4) Dynamic liquidation discounts create attacker oracles. 5) Flash loans amplify any state mutation bug exponentially.
