OAK — OnChain Attack Knowledge

Worked example · 2022-20

Smart-Contract Architecture Exploit Cohort — 2022–2025

OAK Techniques observed
OAK-T9.008, OAK-T9.009, OAK-T9.010
Attribution
unattributed (aggregate cohort).
Loss
T9.008 ~$10.6M (Li.Fi July 2024, ~$10M; Li.Fi v1 March 2022, ~$600K); T9.009 aggregate undocumented at class level (reinitialisation attacks typically in the low-to-mid seven figures per incident, distributed across unaudited proxy deployments); T9.010 ~$10M+ (Curve/Market.xyz July 2023 chain canonical; broader read-only reentrancy cohort across Cygnus Finance March 2024 and others).
Key teaching point
These three Techniques share the property that the vulnerability is at the architecture layer rather than at the single-function logic-flaw layer. T9.008 exploits the diamond proxy's multi-facet audit-scope gap; T9.009 exploits the upgradeable proxy's initialisation-discipline gap; T9.010 exploits the cross-protocol trust-substrate gap where a view function returns stale state mid-transaction. Each requires a different audit discipline: facet-scope auditing (T9.008), initialise-then-interact ordering (T9.009), and integration-level read-only reentrancy assessment (T9.010).

Summary

Timeline

T9.008 from ~2022-07 (Li.Fi v1) to 2024-07 (Li.Fi ~$10M); T9.009 from 2023 onward (post-UUPS/transparent-proxy proliferation); T9.010 from 2022 (class characterised; Market.xyz on QuickSwap, October 2022 canonical anchor).

T9.008 — Diamond-Pattern Facet-Audit Incomplete

The diamond proxy (EIP-2535) pattern's multi-facet architecture creates a structural audit-scope gap: an audit of Facets A, B, C does not cover Facet D added post-audit. The audited facets match their audited bytecode; the gap is the unaudited facets.

Canonical case: Li.Fi — July 2024 — ~$10M. A post-audit facet addition (a new bridge integration facet) introduced a call-verification gap. The diamond's existing facets had been audited; the newly added facet was not covered by the audit scope. The audit claim was truthful about the audited facets but misleading about the diamond's overall security posture. Earlier Li.Fi exploit (March 2022, ~$600K) was also a diamond-pattern facet-addition vulnerability.

Two sub-patterns:

  • Post-audit facet addition introduces exploit surface. New facet holds treasury-routing, fee-extraction, or upgrade-authority functionality.
  • Facet-selector collision. Post-audit facet shadows an audited facet's function selectors, redirecting calls to unaudited logic.

T9.009 — Cross-Contract Reinitialization Attack

The attacker triggers the initialisation function of a target contract via a callback from a peer contract, exploiting the absence of reinitialisation guards across the cross-contract boundary. The target contract's initializer modifier sets a storage flag after the first successful initialisation — but the flag is set at the end of the initialiser's execution, not before external calls made during initialisation.

The structural parallel to T9.005 (Reentrancy): in T9.005, the defence is checks-effects-interactions; in T9.009, the defence is initialise-then-interact — performing all external calls after the initialisation state flag has been set.

The attack is a specific failure of the UUPS/Transparent proxy initialisation discipline but generalises to any contract with a once-only initialisation path that makes external calls before setting the "already initialised" flag.

T9.010 — Read-Only Reentrancy

The attacker re-enters a view/pure function on a target contract while the target is mid-execution, reading state that is temporarily inconsistent. The re-entry is a staticcall (EVM-enforced as read-only), and no state is double-spent on the target — the loss is realised on a third protocol that consumes the stale view-function output for pricing, accounting, or liquidation decisions.

Canonical case: Market.xyz — QuickSwap (Polygon), October 2022. Market.xyz priced Curve LP-token collateral via get_virtual_price() — a view function. Curve's remove_liquidity transfers a native asset to the caller before settling the pool's accounting, so during that callback get_virtual_price() returns an inflated value; the attacker re-entered Market.xyz's borrow path in that window and extended a loan against inflated collateral (~$220K, flash-loan-funded). The Curve pool itself lost nothing; Market.xyz lost the loan principal. See examples/2022-10-market-xyz-curve-lp-oracle-read-only-reentrancy.md. (Distinct from the July 2023 Curve/Vyper compiler-lock event, whose primary losses were direct-drain reentrancy — T9.005 — and did not involve Market.xyz.)

Additional cases: Cygnus Finance — March 2024 — read-only reentrancy in DeFi lending via Balancer LP integration. See examples/2024-03-cygnus-finance-read-only-reentrancy.md.

The T9.005 mitigation (checks-effects-interactions + ReentrancyGuard) does not close T9.010 because ReentrancyGuard does not block staticcall to view functions. The mitigation is at the consumer-protocol layer: only read view functions from target contracts whose outermost call frames have returned in the current transaction.

Public references

  • [lifi2024july] — Li.Fi July 2024 post-mortem (~$10M diamond facet exploit)
  • [lifi2022march] — Li.Fi March 2022 post-mortem (~$600K diamond facet exploit)
  • [eip2535] — EIP-2535 Diamond Proxy standard
  • [openzeppelininitializable] — OpenZeppelin Initializable contract and initializer modifier
  • [curvevyper2023] — Curve Finance Vyper compiler reentrancy incident (July 2023)
  • [quillauditsmarketxyz2022] — Market.xyz (QuickSwap / Polygon) Curve LP-oracle read-only reentrancy, October 2022, ~$220K (see examples/2022-10-market-xyz-curve-lp-oracle-read-only-reentrancy.md)
  • [cygnus2024] — Cygnus Finance read-only reentrancy (March 2024)

Techniques demonstrated (3)