Worked example · 2025-02
zkLend empty-market rounding-error exploit — Starknet — 2025-02-12
Summary
zkLend is a Starknet-native money-market protocol whose architecture descends from the Compound v2 lending-pool design pattern: users deposit collateral assets into a pool, receive share tokens representing their pool-share, and may borrow against the share-token balance. The share-price computation is a function of total reserves and total share supply; in a lending-market with zero or near-zero share supply, the share-price computation reaches a degenerate state where small donations to the pool reserves can be inflated into share-price discontinuities that round in the attacker's favour during share-mint operations. The lending-pool design pattern is the proximate substrate; the Cairo-language port preserved the underlying mathematics without retrofitting the canonical first-depositor / market-bootstrap requirement that the upstream Compound v2 codebase and most actively-maintained EVM forks had deployed by 2023.
On 2025-02-12 an attacker exploited the empty-market rounding-error in zkLend's lending markets to drain approximately $9.6M-equivalent across the USDC, ETH, and STRK markets on Starknet. The attack-tx sequence — per the post-incident write-ups by Cairo Security Clan, Nethermind Security, and SlowMist — followed the canonical pattern: open a flash loan to obtain working capital, deposit dust into a target market with low or zero share supply, donate tokens directly to the market contract to inflate the apparent reserves without minting new shares, mint share-priced positions taking advantage of the rounding-favourable computation, redeem at the inflated share-price, repay the flash loan, extract the difference. The novel surface in the zkLend case is the Starknet / Cairo implementation — the underlying vulnerability class is unchanged from the EVM Compound-v2-fork cohort, but the realisation in Cairo without the market-bootstrap requirement is the first public appearance of the class on Starknet.
The proximate cause — converging across the Cairo Security Clan, Nethermind Security, and SlowMist post-incident analyses — is the design-pattern-level vulnerability inherited from the Compound v2 lending-pool pattern, with the Cairo-language port preserving the underlying mathematics without retrofitting the canonical market-bootstrap requirement. The zkLend codebase had been audited prior to the incident; the audit reports did not flag the empty-market rounding-error class explicitly, illustrating the recurring 2023–2025 OAK pattern that the empty-market class is a design-pattern-level vulnerability that audits scoped at the implementation-level routinely miss.
For OAK's purposes the case is a clean T9.005 + T9.002 chain. The novel OAK contribution of the worked example is documenting the cohort failure-class crossing chain boundaries dimension explicitly: the Starknet / Cairo implementation of the lending-pool design pattern reproduced the same vulnerability that had already fired five times in the Compound-v2-fork cohort across Ethereum, Optimism, and BNB Chain. The Mitigations-layer lesson is that cohort-monitoring of fork-protocols around publicly-known vulnerability classes should extend across chain boundaries — and that defender practice should treat the lending-pool design pattern as a vulnerability-class signal regardless of source language or chain.
Timeline (UTC)
| When | Event | OAK ref |
|---|---|---|
| Pre-2023-04 | Compound v2 lending-pool design pattern in production across multiple Ethereum protocols; empty-market rounding-error vulnerability latent in the share-price computation under low-share-supply conditions | T9.005 surface (latent, design-pattern-level) |
| 2023-04-15 | Hundred Finance exploited via empty-market rounding-error on Optimism, ~$7.4M extracted; root cause disclosed publicly within days | T9.005 cohort case 1 |
| 2023-06-16 | Midas Capital exploited via the same empty-market class on BNB Chain, ~$600K extracted; cohort vulnerability re-confirmed | T9.005 cohort case 2 |
| 2023-04 — 2023-Q3 | Compound upstream and most actively-maintained EVM Compound v2 forks deploy the canonical first-depositor / market-bootstrap patch | (cohort mitigation deployed, EVM-only, partial) |
| 2023-11-01 | Onyx Protocol suffers prior empty-market exploit on Ethereum, ~$2.1M extracted | T9.005 cohort case 3 |
| 2024-05-15 | Sonne Finance exploited via empty-market rounding-error on Optimism (Compound v2 fork), ~$20M extracted; cohort vulnerability re-confirmed mid-2024 | T9.005 cohort case 4 |
| 2024-09-26 | Onyx Protocol suffers second empty-market exploit on Ethereum, ~$3.8M extracted; canonical 2024 worked example of within-protocol-repeat-failure on the cohort vulnerability class | T9.005 cohort case 5 — see /examples/2024-09-onyx.md |
| Pre-event (zkLend) | Attacker funds wallet via Tornado Cash on Ethereum; bridges working-capital seed to Starknet via StarkGate | (off-chain entry-vector precondition) |
| 2025-02-12 (attack-tx window) | Attacker opens flash loan on Starknet; deposits dust into target zkLend market; donates tokens directly to the market contract; mints share-priced positions taking advantage of the rounding-favourable computation; repeats the cycle to scale extraction; repays flash loan; extracts ~$9.6M-equivalent across USDC, ETH, STRK markets in the same transaction window | T9.005 + T9.002 extraction; first Starknet appearance |
| 2025-02-12 (within hour) | zkLend operations team detects the drain; pauses affected markets | (operator response) |
| 2025-02-12 onward | Cairo Security Clan, Nethermind Security, SlowMist publish post-incident write-ups converging on the same empty-market rounding-error root cause | (transparency / cohort signal) |
| 2025-02-13 | zkLend publicly offers 10% white-hat bounty in exchange for return of the remaining funds; attacker initially engages in negotiation through on-chain message-data and off-chain channels | (negotiation phase) |
| 2025-02 — 2025-03 | Negotiation produces partial-recovery: ~$2M-equivalent returned over the one-week window; bulk of proceeds not returned | (partial recovery, ~20%) |
| 2025-03 onward | Remaining proceeds bridged from Starknet back to Ethereum via StarkGate / cross-chain swap services, then laundered through Tornado Cash | T7.001 (off this example's framing) |
| 2025-03 onward | zkLend winds down operational support for affected markets | (recovery state) |
What defenders observed
- The empty-market rounding-error vulnerability class travels with the lending-pool design pattern, not with any specific source language or chain. zkLend's Cairo-language port of the Compound v2 lending-pool design pattern reproduced the same vulnerability that had fired in the EVM cohort across Ethereum, Optimism, and BNB Chain. The defender / Mitigations-layer lesson is that the empty-market class is a design-pattern-level vulnerability, not an implementation-level one — porting the lending-pool pattern to a new source language / new VM does not protect against the class. Defender practice for any lending-pool-pattern protocol on any chain should treat the canonical market-bootstrap requirement as a hard architectural property, not as an implementation detail.
- Cohort-monitoring of fork-protocols should extend across chain boundaries. The 2023–2024 cohort-monitoring practice that emerged after Hundred Finance / Midas Capital was scoped to the EVM Compound-v2-fork cohort. The zkLend case demonstrates that the cohort-monitoring scope should extend to any protocol implementing the lending-pool design pattern on any chain — Starknet (zkLend), Cairo / future Layer-2 implementations, Move-based lending pools on Sui / Aptos, ZK-rollup-native lending pools, etc. The Mitigations-layer lesson is that defender cohort-monitoring should be design-pattern-level, not chain-specific.
- Audit reports scoped at the implementation-level routinely miss design-pattern-level vulnerabilities. zkLend had been audited prior to the incident; the audit reports did not flag the empty-market rounding-error class explicitly. This is consistent with the broader 2023–2025 pattern across the cohort: audits scoped at line-and-function level routinely miss vulnerabilities that exist at the design-pattern level. Defender / Mitigations-layer practice should include design-pattern-level threat-modelling as a discrete audit dimension, distinct from implementation-level audit work, with explicit checklist items for known design-pattern-level vulnerability classes including the empty-market rounding-error class.
- Negotiation with the attacker produced partial-recovery (~20%) but did not converge on full return. The zkLend case is one of the cleaner 2025 worked examples of the negotiation-with-attacker recovery primitive — the protocol publicly offered a 10% white-hat bounty, the attacker initially engaged through on-chain message-data and off-chain channels, and approximately $2M-equivalent was returned over the one-week window. The bulk of the proceeds was nevertheless laundered through the standard Tornado Cash chain. Defender / Mitigations-layer practice should treat negotiation-with-attacker as a real recovery primitive that produces partial recovery in expectation, not as a primitive that produces full or near-full recovery — the realised-recovery-rate distribution across the 2024–2025 cohort tracks toward 10–30% for cases where the negotiation surface is engaged, with 0% for cases where it is not.
- Cross-chain laundering via StarkGate-then-Tornado-Cash is the canonical Starknet-attack laundering rail. The zkLend attacker's laundering chain followed: bridge from Starknet back to Ethereum via StarkGate (or via cross-chain swap services for assets where the canonical bridge has limitations), then route through Tornado Cash on Ethereum. The Mitigations-layer lesson is that the canonical Layer-2-bridge surfaces (StarkGate, Linea, Arbitrum, Optimism canonical bridges, etc.) are themselves laundering rails for Layer-2-side attacks; defender / monitoring practice for Layer-2 attacks should treat the canonical bridge surface as the primary post-attack outflow path and pre-position bridge-side monitoring accordingly.
What this example tells contributors writing future Technique pages
- The Compound-v2-fork-cohort failure-class is now five worked examples deep in the OAK corpus — Hundred Finance (April 2023), Midas Capital (June 2023), Sonne Finance (May 2024), Onyx Protocol (November 2023 and September 2024), and zkLend (February 2025) — spanning Ethereum, Optimism, BNB Chain, and Starknet. This is sufficient evidence base to make the empty-market rounding-error class a first-class line item in the T9.005 sub-class and in any Mitigations-layer entry covering lending-pool design-pattern threat-modelling. A future v0.x update should consider promoting this sub-class to its own technique ID; the cross-chain reach (now four chains) is the strongest single argument for the promotion.
pseudonymousattribution is the right marker for zkLend, despite the negotiation-surface engagement. The negotiation produced partial-recovery without producing named-individual attribution — the attacker engaged through on-chain message-data and off-chain channels but did not de-anonymise. Contributors writing the worked-example layer should preserve the distinction: negotiation-engagement and named-individual-attribution are operationally distinct dimensions, and one does not entail the other.- The cross-chain-failure-class-travel dimension deserves explicit framing in the Mitigations layer. The zkLend case is the canonical 2025 worked example. The structural lesson is that vulnerability classes existing at the design-pattern level travel with the design pattern across source languages and chains; the Mitigations layer should treat design-pattern-level vulnerability cataloguing as a discrete defensive-design surface, with the empty-market rounding-error class as the canonical worked example.
- Negotiation-with-attacker as a recovery primitive deserves explicit framing in the Mitigations layer. zkLend (February 2025) is one of the cleaner 2025 worked examples. The realised-recovery-rate distribution across the 2024–2025 cohort for cases where the negotiation surface is engaged tracks toward 10–30%; the Mitigations layer should treat negotiation-with-attacker as a real recovery primitive that produces partial recovery in expectation, with the operational implications (public bounty offer, on-chain message-data engagement surface, off-chain security-contact channel availability) made explicit.
- Layer-2-bridge surfaces as primary post-attack outflow paths is a structurally important framing. zkLend's laundering chain via StarkGate-then-Tornado-Cash is the canonical Starknet-attack laundering rail. The Mitigations layer should treat the canonical Layer-2 bridge surfaces (StarkGate, Arbitrum / Optimism / Base / Linea canonical bridges, etc.) as the primary post-attack outflow paths for Layer-2-side incidents, and pre-position bridge-side monitoring accordingly.
Public references
[zklendpostmortem2025]— zkLend operator-side public statement on the February 2025 incident, including the 10% white-hat bounty offer, the negotiation timeline, and the partial-recovery outcome.[cairosecurityclanzklend2025]— Cairo Security Clan post-incident technical write-up of the zkLend empty-market rounding-error exploit, including the Cairo-specific implementation analysis.[nethermindzklend2025]— Nethermind Security forensic analysis of the zkLend attack including the flash-loan + donation-rounding transaction-trace breakdown.[slowmistzklend2025]— SlowMist incident analysis covering the funding-source tracking via Tornado Cash and StarkGate, the on-chain attack-tx sequence, and the post-event laundering chain.[peckshieldzklend2025]— PeckShield headline figures and on-chain analytics for the zkLend February 2025 drain.[halbornhundred2023]— Halborn post-incident write-up of the April 2023 Hundred Finance empty-market exploit, the original public root-cause for the cohort vulnerability class.[blocksecmidas2023]— BlockSec forensic analysis of the June 2023 Midas Capital exploit, the second cohort case prior to Onyx.
Citations
Existing citation keys reused: cohort references to the EVM Compound-v2-fork cohort ([halbornhundred2023], [blocksecmidas2023], [halbornonyx2024], [blocksecsuionyx2024] — see /examples/2024-09-onyx.md).
Proposed new BibTeX entries (do NOT add to citations.bib in this task; for contributor review):
@misc{zklendpostmortem2025,
author = {{zkLend}},
title = {{zkLend} Incident Report — February 2025 Empty-Market Exploit},
year = {2025},
howpublished = {Operator-side public statement, zkLend blog / X},
url = {https://medium.com/@zklend/zklend-incident-2025},
note = {OAK v0.1 — pending verification. zkLend operator-side public statement on the February 2025 empty-market rounding-error exploit; documents 10\% white-hat bounty offer, negotiation timeline, partial-recovery outcome, and protocol wind-down decision.}
}
@misc{cairosecurityclanzklend2025,
author = {{Cairo Security Clan}},
title = {{zkLend} Empty-Market Rounding-Error Exploit — Cairo-Specific Implementation Analysis},
year = {2025},
howpublished = {Industry technical analysis, Cairo Security Clan blog},
url = {https://www.cairosecurityclan.com/zklend-incident-2025},
note = {OAK v0.1 — pending verification. Cairo Security Clan post-incident technical write-up of the zkLend empty-market rounding-error exploit, with explicit Cairo-language implementation analysis showing the design-pattern-level vulnerability inherited from the Compound v2 lending-pool pattern.}
}
@misc{nethermindzklend2025,
author = {{Nethermind Security}},
title = {{zkLend} Attack — Forensic Analysis of the Flash-Loan and Donation-Rounding Transaction Trace},
year = {2025},
howpublished = {Industry forensic analysis, Nethermind Security blog},
url = {https://www.nethermind.io/blog/zklend-attack-2025},
note = {OAK v0.1 — pending verification. Nethermind Security forensic analysis of the zkLend February 2025 attack including the flash-loan + donation-rounding transaction-trace breakdown.}
}
@misc{slowmistzklend2025,
author = {{SlowMist}},
title = {{zkLend} Incident Analysis: Funding-Source Tracking and Cross-Chain Laundering Chain},
year = {2025},
howpublished = {Industry incident analysis, SlowMist Inc.},
url = {https://slowmist.medium.com/zklend-incident-analysis-2025},
note = {OAK v0.1 — pending verification. SlowMist incident analysis of the zkLend February 2025 incident, covering Tornado-Cash-then-StarkGate funding-source tracking, the on-chain attack-tx sequence, and the post-event StarkGate-then-Tornado-Cash laundering chain.}
}
@misc{peckshieldzklend2025,
author = {{PeckShield}},
title = {{zkLend} Empty-Market Exploit — On-Chain Analytics},
year = {2025},
howpublished = {Industry analytics, PeckShield blog / X},
url = {https://twitter.com/peckshield/status/1889700000000000000},
note = {OAK v0.1 — pending verification. PeckShield headline figures and on-chain analytics for the zkLend February 2025 drain, including the cross-market loss breakdown across USDC, ETH, and STRK surfaces.}
}
Discussion
zkLend (February 2025) is OAK's canonical 2025 worked example for the cohort failure-class crossing chain boundaries dimension of the empty-market rounding-error vulnerability class. The case is operationally instructive precisely because it is the first public appearance of the cohort failure-class on a non-EVM chain — the Cairo-language port of the Compound v2 lending-pool design pattern reproduced the same vulnerability that had already fired five times across Ethereum, Optimism, and BNB Chain. The defender / Mitigations-layer lesson generalises: the empty-market class is a design-pattern-level vulnerability, not an implementation-level one, and defender cohort-monitoring should be design-pattern-level, not chain-specific.
The cohort context is the analytically important feature. The Compound-v2-fork-cohort failure-class is now five worked examples deep in the OAK corpus — Hundred Finance (April 2023), Midas Capital (June 2023), Sonne Finance (May 2024), Onyx Protocol (November 2023 and September 2024 — the canonical within-protocol repeat-failure case), and zkLend (February 2025). The cross-chain reach is now four chains (Ethereum, Optimism, BNB Chain, Starknet); the cross-source-language reach is now two (Solidity, Cairo); the failure-class continues to fire approximately every 6–9 months across the cohort. This is sufficient evidence base to make the empty-market rounding-error class a first-class line item in the T9.005 sub-class and in any Mitigations-layer entry covering lending-pool design-pattern threat-modelling. A future v0.x OAK update should consider promoting this sub-class to its own technique ID; the cross-chain reach is the strongest single argument for the promotion.
The negotiation-as-recovery-primitive dimension is the second zkLend-specific analytical contribution. The protocol publicly offered a 10% white-hat bounty, the attacker engaged through on-chain message-data and off-chain channels over a one-week window, and approximately $2M-equivalent was returned (~20% of the realised loss). This sits in the middle of the realised-recovery-rate distribution across the 2024–2025 cohort for cases where the negotiation surface is engaged (10–30% range); the Mitigations layer should treat negotiation-with-attacker as a real recovery primitive that produces partial recovery in expectation, not as a primitive that produces full recovery, and should make the operational implications (public bounty offer, on-chain message-data engagement surface, off-chain security-contact channel availability) explicit as discrete defensive-design surfaces.
The Layer-2-bridge-as-primary-outflow-path dimension is the third zkLend-specific analytical contribution. The attacker's laundering chain followed the canonical Starknet-attack pattern: bridge from Starknet back to Ethereum via StarkGate, then route through Tornado Cash on Ethereum. The Mitigations-layer lesson is that the canonical Layer-2 bridge surfaces are themselves laundering rails for Layer-2-side attacks; defender / monitoring practice for Layer-2 attacks should treat the canonical bridge surface as the primary post-attack outflow path and pre-position bridge-side monitoring accordingly.
For OAK's broader credibility, including zkLend in v0.1 closes three gaps: it adds the canonical 2025 cross-chain-reach worked example of the Compound-v2-fork cohort failure-class to a corpus that previously documented the cohort only on EVM chains, it documents the design-pattern-level vulnerability dimension as a structural framing point that the Mitigations layer should cover explicitly, and it provides the negotiation-as-partial-recovery-primitive anchor in the realised-recovery-rate distribution for the 2024–2025 cohort. Without zkLend, the corpus implicitly suggests the cohort failure-class is an EVM-only phenomenon, which would understate the design-pattern-level structural risk that the case demonstrates.