Curve Finance — Vyper compiler exploit
A reentrancy bug in specific Vyper compiler versions drained $70M from multiple Curve Finance pools; the attacker voluntarily returned a portion of the stolen funds.
- Target
- Curve Finance — Vyper compiler exploit
- Date public
- 30 July 2023
- Sector
- Crypto
- Attack type
- Vulnerability Exploit
- Threat actor
- Multiple opportunistic exploiters
- Severity
- High
- Region
- Global — Ethereum
Curve Finance is one of the largest cryptocurrency exchanges for stablecoins, operating through "liquidity pools" where users deposit funds that others can trade against. The pools run on smart contracts — programs that execute automatically on the Ethereum blockchain. Those programs were written in a language called Vyper. Vyper had a feature called a "reentrancy guard" — protection against a type of attack where a malicious program calls back into a contract repeatedly before it has finished updating its records (the same type of flaw that hit The DAO in 2016). The guard was supposed to block those recursive calls. The problem was that specific older versions of the Vyper compiler — the tool that converts the human-readable code into machine-executable code — had a bug. The bug silently generated the reentrancy guard code incorrectly, leaving it non-functional without the developer ever knowing. The contracts looked correct when the developers read them; the guard appeared to be in place. But the compiled output that actually ran on the blockchain didn't enforce it properly. Multiple pools were attacked at once. White-hat bots raced the attackers and managed to front-run some of the theft, recovering about $52M of the $73M taken. The remaining $21M was lost. The case is now cited whenever people talk about "supply chain risk" in software — because the bug wasn't in the protocol's own code but in the compiler it trusted.
What happened
On 30 July 2023 multiple Curve Finance liquidity pools were exploited through a reentrancy vulnerability that had been silently introduced by specific affected versions of the Vyper smart-contract compiler. The pools targeted included CRV/ETH, alETH/ETH, msETH/ETH, and pETH/ETH, all of which had been compiled using Vyper versions 0.2.15, 0.2.16, or 0.3.0. Approximately $73 million was drained across the attacks.
White-hat responders, most notably Coinbase’s MEV bot operating under the address known as c0ffeebabe.eth, front-ran multiple attacker transactions using the same on-chain mempool visibility that attackers use in normal MEV operations. By inserting their own rescue transactions ahead of the exploit transactions in the block ordering, the white-hat bots were able to drain funds into safe custody before the attacker’s transactions could claim them. Approximately $52 million was recovered through this process and returned to Curve. Net loss was approximately $21 million.
The exploits triggered a wider market concern because the Curve founder, Michael Egorov, held approximately $100 million in CRV-collateralised loans on multiple DeFi lending protocols. A sharp CRV price decline in the aftermath threatened cascading liquidations. Egorov subsequently sold large CRV positions OTC to various investors to raise capital and de-risk the loan positions, averting a broader liquidity crisis.
How it worked
Reentrancy is one of the oldest and most studied attack classes in smart contract security. The basic mechanism: Contract A calls Contract B; Contract B, before returning, calls back into Contract A; Contract A’s state has not yet been updated, so the second call operates on stale data and may produce an unintended outcome. The DAO hack in 2016 was a reentrancy attack. Solidity introduced its own reentrancy guards; Vyper’s language design included a @nonreentrant decorator that was intended to provide the same protection automatically.
The Vyper reentrancy guard worked by setting a lock variable at function entry and clearing it at function exit — any re-entry attempt while the lock was held would revert. The bug in versions 0.2.15, 0.2.16, and 0.3.0 was in the compiler’s handling of this locking mechanism: under specific conditions involving how the guard interacted with the compiled output’s storage slot allocation, the compiler generated code that did not correctly initialise or enforce the lock. The contracts’ source code contained the @nonreentrant decorator in the correct positions. The compiled bytecode that was actually deployed did not enforce it. Developers reading the source code had no indication anything was wrong.
In the affected Curve pools, the reentrancy guard was supposed to protect the remove_liquidity function — the function that allows users to withdraw their deposited assets. An attacker who could re-enter remove_liquidity could manipulate the pool’s internal price oracle during the withdrawal sequence, because the price is updated after the transfer rather than before. By re-entering before the price update, the attacker could exploit the stale price to withdraw more than their proportionate share of pool assets, effectively stealing from other liquidity providers.
The practical exploit required a malicious contract as the recipient of the withdrawal, because the reentrancy had to be triggered from within the token transfer callback. The attacker deployed a contract that, when it received ETH during the withdrawal, immediately called back into the pool contract before the state update completed. On affected Vyper versions, the reentrancy guard failed to block this call, and the attack succeeded.
The vulnerability had existed in the compiler since 2021. The three affected versions were considered stable releases and had been used in production by Curve and other protocols for months to years without incident. No prior audit had flagged the issue because the compiler output was not routinely subjected to independent bytecode verification.
Timeline
- 2021 — Vyper versions 0.2.15, 0.2.16, and 0.3.0 are released with the reentrancy guard compiler bug present. Curve and other protocols compile and deploy contracts using these versions.
- 30 July 2023, ~21:00 UTC — Attacks begin. The CRV/ETH pool (Curve’s own pool containing founder Michael Egorov’s tokens) is hit first, followed by alETH/ETH, msETH/ETH, and pETH/ETH.
- 30 July 2023, simultaneously — White-hat MEV bots, primarily c0ffeebabe.eth, begin front-running attacker transactions, rescuing assets into safe wallets. A race between exploiters and rescuers plays out in the block ordering.
- 30 July 2023 — Vyper publishes a disclosure confirming the compiler reentrancy bug and naming the three affected versions. Curve Finance confirms the exploit and names the affected pools.
- 31 July 2023 — White-hat funds returned to Curve. Total recovered: approximately $52M. Net loss: approximately $21M. CRV token price falls sharply, raising concerns about Egorov’s collateralised loans.
- August 2023 — Egorov conducts OTC sales of CRV tokens to multiple DeFi investors and funds to raise capital and reduce loan risk. Loan positions are gradually unwound over subsequent weeks without cascade liquidations.
- Ongoing — Vyper releases patched versions. The Ethereum security community conducts a broader audit of deployed contracts compiled with affected versions to identify other potentially vulnerable protocols.
What defenders should learn
The Curve exploit is the canonical case study for compiler-layer supply chain risk in smart contracts. The developer wrote correct, audited code. The deployed bytecode was incorrect because the tool that translated the code failed silently. This failure mode sits in the gap between two normally separate trust domains — the application security audit (which reviews source code) and the toolchain (which is assumed correct). When the toolchain itself is compromised or buggy, source-code audits provide false assurance.
The mitigation is bytecode verification as a distinct and mandatory step in deployment. Bytecode verification compares the compiled output against the expected behavior of the source code, either through formal verification tools or by independently compiling with a known-good environment and comparing outputs. For protocols where specific compiler versions are used across a large codebase, periodic re-audit of deployed bytecode against the source is warranted when compiler vulnerabilities are disclosed. Version pinning in deployments — recording exactly which compiler version produced each deployed contract — is a prerequisite for being able to assess exposure when a compiler bug is disclosed.
The white-hat MEV bot recovery demonstrates a counter-intuitive positive application of MEV infrastructure. The same block-ordering advantages that are typically discussed as extracting value from DeFi users were used here to extract value from attackers and return it to victims. Several protocols and security teams have since formalised relationships with white-hat MEV operators to pre-position rescue transactions for known vulnerability scenarios — an emerging security practice that treats on-chain MEV capabilities as a defensive tool.
The post-incident CRV liquidity concern is a reminder that DeFi protocol risks are systemically interconnected. An exploit in Curve pools did not stay within Curve: it propagated through the CRV token price to create liquidation risk in lending protocols on Aave and elsewhere, which in turn required emergency OTC capital operations by the Curve founder. Protocols holding positions in governance tokens of other protocols, and users who have taken leveraged positions backed by such tokens, should model the impact of sharp price movements in those tokens during crisis scenarios — not just normal market conditions.
Sources
- Curve Finance — Wikipedia // reporting
- Vyper — compiler reentrancy bug disclosure // primary
- Curve Finance — official statement on pool exploits // primary
- BlockSec — analysis of the Curve Finance exploit // analysis