All audits
FailedSolanaIndependent

Mango Markets Oracle (2022)

Smart contract security audit by TierZero · 2023-05-15

Independent research. This is an unsolicited security analysis published by TierZero — not a commissioned audit, and no certificate is issued. It reflects our own review of public code and on-chain events.

Findings summary

1
Critical
2
High
2
Medium
1
Low
1
Informational

Independent research — not a commissioned audit. This analysis is based solely on public post-incident reporting, on-chain data, and Mango DAO/governance disclosures. TierZero was not involved with Mango Markets and did not access non-public information.

What the protocol did

Mango Markets was a Solana-based margin trading and lending protocol (Mango Markets v3) that let users deposit collateral and open leveraged spot and perpetual futures positions from a single cross-margined account. Collateral value, margin requirements, and liquidation thresholds were all computed from on-chain price oracles feeding into a shared health-check function. Because positions were cross-margined, unrealized profit on any instrument — including Mango's own MNGO perpetual — counted as collateral that could immediately be borrowed against in other assets.

On October 11, 2022, an attacker (later identified as Avraham Eisenberg) used this design to drain an estimated $114–117 million in USDC, BTC, SOL, and other assets from the protocol in a matter of minutes.

The vulnerability

The root cause was a manipulable price oracle combined with a collateral model that treated unrealized, self-inflated PnL as spendable collateral. MNGO's spot markets had thin real liquidity, so the oracle price for the MNGO-PERP market could be moved a long way with a relatively modest amount of capital, and the protocol had no mechanism — TWAP smoothing, deviation cap, position-size limit relative to open interest — to resist that move before using the resulting mark price for borrowing power.

The flawed pattern, in simplified pseudocode, looked like this:

fn account_health(account) -> i64 {
    let mut health = 0;
    for position in account.positions {
        let mark_price = oracle.get_price(position.market); // spot-quoted, no TWAP, no deviation check
        health += position.unrealized_pnl(mark_price);
    }
    health
}

fn borrow(account, amount, asset) {
    require(account_health(account) >= 0, "insufficient collateral");
    // borrow proceeds against health computed from a price the borrower can move
}

Nothing here checks whether the price feeding unrealized_pnl reflects real market depth, or whether the position being marked was opened by the same actor now trying to borrow against it. Collateral value and borrowing power were derived from a single, thinly-traded, attacker-influenced number.

How the exploit worked

  1. The attacker funded two Mango accounts with USDC.
  2. Account A opened a large long MNGO-PERP position; Account B took the matching short, so the trade was internally netted and required no external counterparty.
  3. Using the same capital, the attacker aggressively bought MNGO on the open spot market, which fed Mango's oracle. This pushed the MNGO price roughly 30x higher — from a few cents to about $0.91 — within minutes, on volume that was small relative to the protocol's exposure but large relative to MNGO's actual liquidity.
  4. The pumped price flowed straight into the oracle used for perp mark-to-market. Account A's long position now showed a massive unrealized profit, which the health-check function counted as collateral.
  5. The attacker borrowed against that inflated collateral, draining nearly all of Mango's lending pools — USDC, BTC, SOL, and other listed assets — before the price could revert or anyone could intervene.
  6. The attacker publicly negotiated with Mango DAO afterward, returning a portion of the funds while keeping the rest, framing it as a "profitable trading strategy" rather than theft. He was later tried in U.S. federal court on fraud and market-manipulation charges tied to the incident; the case saw a conviction followed by post-trial legal challenges, underscoring that regulators and courts increasingly treat oracle manipulation as securities/commodities fraud, not a gray-area DeFi maneuver.

How an audit catches this

This is a design-level flaw, not a typo, so catching it requires reviewing the protocol's economic assumptions, not just its Rust/Anchor code. Concretely, in an engagement like our smart contract audit work we would flag:

  • Oracle source and depth review. For every market used in collateral or liquidation math, we check total on-chain liquidity against the position sizes the protocol permits. A market where a single actor can move price double-digit percent with capital smaller than the protocol's own exposure is a finding, full stop.
  • No-TWAP / no-deviation-cap flags. Any get_price() call feeding a health or margin calculation that uses an instantaneous spot price instead of a time-weighted or deviation-bounded price is flagged as a control gap, independent of whether we can demonstrate an exploit in the time available.
  • Self-referential collateral tests. We specifically test whether an account can post collateral in an asset whose price it can materially influence through its own trading (spot or perp), and whether that collateral is then borrowable in unrelated assets.
  • Adversarial simulation. We simulate concentrated buy pressure against listed markets and measure the resulting borrowing power increase, comparable to the checks described in our Solana trading program audit checklist, and cross-check any oracle-adjacent CPI calls the way we outline in our piece on CPI risk in Solana DeFi.
  • Open-interest and single-market concentration limits. We look for hard caps tying maximum borrowable value against a given market to that market's independently verified liquidity, not just its listed price.

Remediation

The fixes that would have prevented this are now standard in well-designed perp/lending protocols: aggregate prices from multiple independent, deep venues rather than a single on-chain market; apply TWAP or median-of-sources smoothing with a bounded max deviation per update; cap the fraction of protocol-wide borrowing power any single market or position can contribute; and require unrealized PnL above a threshold to be independently corroborated (e.g., via a slower settlement path) before it becomes spendable collateral elsewhere. None of these are exotic — they are the kind of invariant-level checks we build into every margin or perp system we review, whether the engagement is a full audit or a lighter-weight code review pass ahead of a mainnet listing decision.

If your protocol lets unrealized PnL on a thinly traded market become borrowing power anywhere else in the system, that is worth a second look before it becomes a postmortem — talk to TierZero about an audit.

Thinking about a review like this for your own contracts? Get a smart contract audit from an independent team.

Need your contracts audited?

Manual review + tooling across TON, Solana and EVM — certificate and full report included.

Get an audit