Market.xyz / QuickSwap Lend stMATIC Curve LP Oracle Read-Only Reentrancy Exploit (2022-10-24)

  • Name: Market.xyz / QuickSwap Lend stMATIC Curve LP oracle read-only reentrancy exploit
  • Date: 2022-10-24
  • Protocol: Market.xyz (on QuickSwap Lend)
  • Chain: Polygon
  • Category: read-only reentrancy / Curve LP oracle manipulation / isolated-market collateral inflation / wrapper-collateral mispricing
  • Loss: about $220k; protocol statements said the damaged liquidity was seeded by QiDAO and that QuickSwap / QiDAO user funds were not directly lost
  • Summary: Market.xyz ran an isolated lending market on QuickSwap Lend that accepted Beefy-wrapped Curve stMATIC/WMATIC LP exposure as collateral and priced it through Curve’s get_virtual_price() path. The attacker used flash liquidity to build a huge temporary Curve LP position, then entered Curve’s liquidity-removal path so LP supply had already gone down while pool balances were still mid-withdrawal. During that callback window, the collateral wrapper’s value was read against an inflated Curve virtual price, which let the attacker borrow miMATIC against a transiently overvalued mmooCurve-stMATIC position. Once Curve finished settling, the mark fell back toward reality, but the borrow had already left the market.
  • Trigger / entry point:
    • QuickSwap Lend exposed Market.xyz isolated markets on Polygon.
    • The exploited market used a Curve LP oracle path vulnerable to the read-only reentrancy issue ChainSecurity had disclosed earlier in 2022.
    • The attacker targeted stMATIC / WMATIC Curve LP collateral wrapped through Beefy and deposited into the Market.xyz market before borrowing against it.
    • QuickSwap publicly attributed the root cause to the Curve oracle path Market.xyz was using and shut QuickSwap Lend after the exploit.
  • Root cause:
    • This was not a classic state-corruption reentrancy bug inside Market.xyz. The key mistake was trusting an upstream Curve LP valuation during the exact callback window where Curve’s balances and LP supply were inconsistent.
    • In vulnerable Curve pools, remove_liquidity() (and more aggressively remove_liquidity_imbalance()) could burn LP supply before underlying balances were fully updated.
    • get_virtual_price() then divided a still-too-large invariant / balance view by an already-reduced LP supply, temporarily inflating LP value.
    • Market.xyz treated that callback-window price as if it were final collateral truth and extended real miMATIC borrowing power against it.
  • Exploit path:
    1. In transaction 0xb8efe839da0c89daa763f39f30577dc21937ae351c6f99336a0017e63d387558, the attacker assembled large temporary Polygon-side balances through flash liquidity, including tens of millions of WMATIC and roughly 19.66M stMATIC.
    2. Those funds were added into the relevant Curve stMATIC / WMATIC pool, minting a very large LP position.
    3. The LP exposure was routed through Beefy’s moo vault wrapper, producing mooCurve stMATIC that the attacker then supplied into the Market.xyz isolated market, where it minted the corresponding collateral / interest-bearing position commonly shown in the exploit trace as IB Market Moo Curve stMATIC (mmooCur...).
    4. The attacker then unwound a large chunk of the underlying Curve LP through the vulnerable liquidity-removal path.
    5. During Curve’s callback window, LP supply had already been reduced but pool balances had not fully normalized, so the Curve virtual price temporarily overstated the value of the remaining LP-backed collateral.
    6. While that inflated mark was live, the attacker borrowed miMATIC from the Market.xyz market; the transaction trace shows a representative 249,000 miMATIC borrow during the main exploit sequence.
    7. After the callback returned, Curve settlement completed and the collateral valuation should have fallen back, but the borrow had already been extracted.
    8. Public incident reports put the total damage around $220,000, after which QuickSwap shut down its lending product rather than keep the vulnerable market open.
  • What the attacker needed to be true:
    • Market.xyz had to accept Curve-LP-derived wrapper collateral and trust a live same-transaction pricing path.
    • The relevant Curve pool had to expose a callback-capable withdrawal path where LP supply and balances were temporarily inconsistent.
    • The wrapper / market stack had to propagate the inflated LP price quickly enough that borrow capacity could be minted before any delayed sanity check or lock probe rejected it.
    • The isolated market had to hold borrowable miMATIC liquidity worth stealing.
  • Technical anatomy worth remembering:
    • This is a useful early production example of Curve read-only reentrancy actually being monetized through an isolated lending market, not merely identified as a theoretical downstream risk.
    • The attacker did not need to keep the false price around. They only needed Market.xyz to believe it for one borrow-relevant instant.
    • The extra wrapper layer matters: the economically dangerous object was not raw Curve LP sitting directly in a lending market, but Beefy-wrapped LP exposure whose value still ultimately flowed from Curve’s callback-window get_virtual_price().
    • The market was small relative to later Conic- and EraLend-style losses, but the exploit path is analytically clean because it shows how little capital needs to be at risk for an unsafe LP-pricing integration to become drainable.
  • Representative transactions and assets:
    • Main exploit transaction: 0xb8efe839da0c89daa763f39f30577dc21937ae351c6f99336a0017e63d387558
    • Borrowed asset: miMATIC
    • Manipulated upstream exposure: Curve stMATIC / WMATIC LP virtual price
    • Collateral wrapper visible in public traces: Beefy mooCurve stMATIC and the corresponding Market.xyz interest-bearing wrapper (mmooCur...)
  • Response / mitigation:
    • QuickSwap announced that QuickSwap Lend was closing after the exploit and said only the Market.xyz lending market was compromised.
    • Public reporting and later technical writeups tied the exploit to the same Curve LP oracle read-only reentrancy class ChainSecurity had previously disclosed.
    • The incident reinforced the integration lesson that Curve LP valuations needed explicit reentrancy-context defenses such as lock-probing or other safe-read guards before being used for collateral decisions.

Pattern tags

  • Curve callback-window virtual price escaped one protocol boundary and stayed borrow-authoritative through a wrapper stack
  • isolated market constrained blast radius but did not create a safer collateral-read surface
  • wrapper collateral inherited unsafe same-transaction pricing from the upstream LP despite one more abstraction layer
  • one transient overvaluation was enough because borrow authority, not durable mark control, was the scarce object

Why the exploit worked

  • Market.xyz did not need to corrupt its own accounting or keep the false price alive for long. It only needed to let one same-transaction Curve get_virtual_price() answer become immediate borrow authority.
  • The Beefy wrapper layer did not create a new valuation boundary; it simply repackaged Curve LP exposure. That meant the economically decisive read surface still lived upstream in Curve’s callback window even though the lending market only saw wrapped collateral.
  • The isolated-market architecture limited how much miMATIC could be stolen, but it did not change the trust assumption. Once the market accepted the wrapped Curve LP mark as settled collateral truth, the attacker could monetize a single transient markup into real debt.
  • The sharper preserved lesson is that wrapper, vault, or isolated market labels are not safety properties. If the final borrow path still depends on a callback-sensitive upstream view, then the whole stack is only as trustworthy as that unsettled read surface.

Detection and audit pivots

  • Trace collateral pricing all the way through wrappers and vault layers to the first upstream state object that actually authors value. If the final answer still bottoms out in Curve get_virtual_price() or a similar callback-sensitive view, the wrapper stack did not add safety by itself.
  • Ask whether the protocol exposes one canonical safe-read surface before collateral can mint borrow power. If the answer is the market just reads the current LP mark, assume isolated-market blast-radius controls are being mistaken for oracle hardening.
  • Test same-transaction paths where large temporary liquidity is added, partially removed, and then immediately reused for borrow authorization. The dangerous question is not only can the LP price move, but can any wrapper or lending module consume that move before upstream settlement finishes.
  • Review whether integration code distinguishes informational LP pricing from borrow-authoritative pricing. Useful for estimating wrapper value and safe to back live debt right now are different claims.
  • Look for protocols that accept third-party wrapper collateral while relying on external price semantics they do not directly control. Extra abstraction layers often make reviewers focus on the wrapper math and forget to ask whether the upstream pool still has a safe read surface at all.

Failure-prevention checklist

  • Do not let wrapper collateral inherit borrow authority directly from callback-sensitive upstream view functions. If the wrapped position ultimately prices off Curve get_virtual_price(), the lending market needs either an explicit safe-read guard or a delayed settled snapshot before that mark can back live debt.
  • Bind wrapper exchange rates, LP valuation, and borrow capacity to one quiescent observation boundary. Wrapper says this is the current value is not enough if the wrapper’s answer is still derived from reserves before settlement / supply after burn state underneath.
  • Treat isolated markets as blast-radius controls, not oracle hardening. If a market is small but can still consume transient upstream marks as collateral truth, the safety boundary has not been fixed.
  • Apply the same safe-read discipline to every borrow-authoritative path: collateral deposits, borrow actions, collateral toggles, liquidation eligibility, and any helper or vault adapter that can refresh wrapper value mid-transaction.
  • Prefer previous-block or delayed snapshots for third-party LP wrappers when the upstream venue cannot expose a trustworthy same-transaction read surface. A slower but settled collateral mark is healthier than a fast mark that only exists inside a callback frame.
  • Cap newly listed wrapper collateral aggressively until its full pricing stack has been tested under add-liquidity, remove-liquidity, and callback-window stress. The dangerous capital can live outside the lending market; the on-market seed only needs to be large enough to monetize the transient mark.
  • Monitor for same-block sequences where a wrapped LP position spikes in borrow value immediately after a large liquidity add/remove cycle, especially when the position was only just minted or refreshed. That is a strong production signature that wrapper collateral is inheriting unsettled upstream truth.
  • Make upstream-safe-read assumptions explicit in integration docs and code reviews. If the protocol depends on Curve is fully settled before we read, that assumption should be machine-checked, not left as unwritten context.

Audit prompts

  • What exact upstream state object ultimately gives this wrapper collateral its borrow value: Curve get_virtual_price(), vault share math, a custom oracle adapter, or a delayed snapshot? Which of those objects can still be read while the underlying pool is mid-withdrawal?
  • Does the wrapper layer actually introduce a new settled valuation boundary, or does it simply relay the same callback-sensitive price signal one abstraction layer higher?
  • Can a large temporary off-market LP position distort the upstream reserve/supply relation while only a much smaller wrapped remainder is held inside the lending market? If yes, the market’s small TVL is not evidence that the pricing surface is safe.
  • Are isolated-market or collateral-specific caps being mistaken for oracle defenses? What prevents one transient wrapper overvaluation from immediately becoming real borrow power inside that isolated silo?
  • Which entrypoints can refresh, sample, or depend on wrapper value during the same transaction as a liquidity removal or vault-share transition? Are all of them forced through the same safe-read rule?
  • If the upstream protocol offers no canonical safe to read now primitive, why is the downstream lending market allowed to consume same-block wrapper prices at all?
  • Would a simple invariant like wrapped LP collateral cannot gain borrow power during the same callback-bearing unwind that reduced upstream LP supply have failed in testing?
  • After the callback window closes and the mark normalizes, what onchain control limits loss: borrow caps, delayed borrowing, pause authority, or liquidation quarantine? If the answer is nothing until after debt leaves, the design is still trusting perfect live valuation.

Lessons that should already be standard

  • Wrapper layers do not sanitize upstream callback windows. If the wrapped asset still bottoms out in an unsafe LP price surface, the wrapper has only moved the trust boundary, not repaired it.
  • Isolated market is not a synonym for safe market. Siloing limits how much value can be stolen after a pricing mistake; it does not change whether the pricing surface itself is money-good.
  • A third-party vault or wrapper deserves the same oracle review as the underlying AMM. The dangerous question is always what exact settled state does this share claim represent right now?
  • Fast same-transaction collateral marks are a luxury, not a baseline requirement. When an upstream venue cannot prove quiescent state during callbacks, slower settled snapshots are the healthier design.
  • Small exploit size can hide a high-quality lesson. Market.xyz is useful precisely because the path is compact: one callback-window LP overvaluation, one wrapped collateral read, one real borrow, done.
  • New collateral listings should be treated as execution-order risk, not just parameter risk. A wrapper can look economically reasonable in steady state and still be catastrophically wrong for one stack frame.

Why this incident matters

  • It adds an earlier realized exploit to the Curve-LP read-only-reentrancy branch, instead of letting that branch jump straight from the generic ChainSecurity postmortem to later 2023 incidents like Conic.
  • It preserves the lending-market wrapper variant, where the dangerous collateral object is one more layer removed from Curve but still inherits Curve’s unsafe callback-window price surface.
  • It is also a good reminder that isolated-market design does not remove oracle risk; it only constrains blast radius once the integration has already failed.
  • Just as importantly, it preserves the healthier-side lesson that wrapper collateral still needed one canonical safe-read surface before any same-transaction Curve-derived mark deserved borrow authority.

Internal linkages

Control surface