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 ~$9–12M (Li.Fi July 2024, reported between $8M and $11.6M by different sources; Li.Fi March 2022, ~$600K from 29 wallets); 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 ~$220K for the Market.xyz / QuickSwap Lend case (October 2022; the read-only-reentrancy characterisation is not stated in the news sources cited here); broader read-only reentrancy cohort not quantified.
Corrected 2026-10-11
Li.Fi loss figures and mechanisms were corrected (2022 was not a facet-addition case), and the Market.xyz loss is stated as ~$220K with its read-only-reentrancy mechanism flagged as unconfirmed by news sources.
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-03-20 (Li.Fi v1) to 2024-07 (Li.Fi, GasZipFacet); 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 — reported $8M–$11.6M. An arbitrary-call flaw in depositToGasZipERC20() of the GasZipFacet, deployed five days before the attack (Decurity), let the attacker drain wallets that had granted infinite approvals (Ethereum and Arbitrum, 153 wallets per Forklog); Threesigma noted the facet was deployed unaudited. The earlier Li.Fi exploit (2022-03-20, ~$600K from 29 wallets) was an unchecked external call (call injection) in the swap path, not a facet-addition case.

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 (mechanism per the OAK example file, not per the news sources cited here, which describe a flash-loan price manipulation of a Curve-based feed): Market.xyz — QuickSwap (Polygon), 2022-10-24, ~$220K. 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.)

Additional cases:

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

Techniques demonstrated (3)