Morpho PAXG/USDC Market Oracle Decimal Misconfiguration (2024-10-13)

  • Name: Morpho PAXG/USDC market oracle decimal misconfiguration
  • Date: 2024-10-13
  • Protocol: Morpho Blue ecosystem market deployed by LeadBlock
  • Chain: Ethereum
  • Category: oracle configuration failure / decimal mismatch / permissionless-market misconfiguration
  • Loss: about $230,000 borrowed against roughly $350 of PAXG at exploit time
  • Summary: A newly created PAXG/USDC market on Morpho Blue was launched with a misconfigured oracle that overvalued PAXG by 10^12. The mistake was not in Morpho Blue’s immutable core contracts, but in the market-specific oracle setup used for the new market. Because the configured SCALE_FACTOR failed to reconcile PAXG’s 18 decimals with USDC’s 6 decimals, the market treated tokenized gold as if it were worth about $2.6T per unit. An opportunistic user supplied about $350 of PAXG, borrowed about $230,000 USDC, and left the market with a visibly broken balance sheet until the curator handled repayment and remediation outside the market itself.
  • Trigger / entry point:
    • The affected market was LeadBlock’s new PAXG/USDC AdaptiveCurve market with LLTV 91.5%, oracle 0xDd1778F71a4a1C6A0eFebd8AE9f8848634CE1101, and market ID 0x8eaf7b29f02ba8d8c1d7aeb587403dcb16e2e943e4e2f5f94b0963c2386406c9.
    • Morpho Blue oracle contracts expect price() to return the price of 1 unit of collateral quoted in the loan asset and scaled by 1e36.
    • MorphoChainlinkOracleV2 explicitly assumes the deployer passes correct token decimals into the constructor when computing SCALE_FACTOR.
    • In this market, the decimal configuration was wrong, so the final onchain oracle output drastically overstated PAXG collateral value relative to USDC.
  • Root cause:
    • This was a unit / decimal integration failure. The market-specific oracle setup did not correctly account for the 18 decimals of PAXG against the 6 decimals of USDC.
    • Security writeups describe the bug as a miscalculated SCALE_FACTOR that inflated collateral value by 10^12.
    • Morpho Blue itself behaved as designed: permissionless markets can choose custom oracle configurations, and this market chose a bad one.
    • The meaningful failure was therefore at the deployment and review layer: incorrect oracle parameters were accepted into a live lending market with high LLTV and no additional sanity check that the final quoted PAXG/USDC price was dimensionally plausible.

Three oracle-authority promotions this incident collapsed

  1. A configurable market-local oracle path already existed

    • MorphoChainlinkOracleV2 was a legitimate oracle adapter, and the market had a syntactically valid deployed price path for PAXG / USDC.
    • The weak first fact was only this market now has some reachable local oracle object with constructor-supplied decimal assumptions.
  2. That locally valid adapter instance was over-promoted into canonical solvency truth for this market

    • The bad SCALE_FACTOR meant the deployed price() answer no longer described the economically correct 1 PAXG in USDC object, but the market still accepted it as if asset identity, quote direction, and decimal normalization were all already resolved.
    • The stronger promotion was therefore not just the oracle contract deployed successfully, but this exact deployed parameter tuple deserves to define borrow power for the live PAXG/USDC market.
  3. That accepted local truth inherited immediate live borrowing authority with real liquidity behind it

    • A fresh curator-deployed market with high LLTV and available USDC liquidity let the wrong price object become spendable debt authority immediately, instead of first passing through a quarantine or canary stage.
    • Once that happened, the cash-out path was trivial: supply a tiny amount of PAXG, let the misconfigured oracle mark it at fantasy size, then borrow real USDC through the ordinary market flow.
  • Exploit path:
    1. A new PAXG/USDC Morpho market went live with a misconfigured oracle.
    2. The faulty oracle made PAXG appear worth roughly 10^12 times more than intended, with public writeups summarizing the result as gold priced around $2.6T.
    3. The attacker identified the bad price and supplied only about $350 of PAXG as collateral.
    4. Using the fake collateral value, the attacker borrowed about $230,000 USDC from the market in attack tx 0x256979ae169abb7fbbbbc14188742f4b9debf48b48ad5b5207cadcc99ccb493b.
    5. The borrowed USDC left the market while the residual PAXG collateral was economically meaningless.
    6. The market curator later stated that repayments and lender protection were handled outside Morpho to prevent further opportunistic extraction, which is why the market UI did not cleanly reflect the remediation path.
  • What the attacker needed to be true:
    • The market’s final oracle output had to be accepted as authoritative borrow power without a reasonableness bound.
    • The market needed available USDC liquidity and a high enough LLTV for the fake PAXG valuation to monetize into a large real borrow.
    • The attacker only needed to notice the bad price before the market was paused or socially quarantined; no flash loan or external market manipulation was required.
    • Curator-side remediation had to be slower than the initial borrow, because the profitable step was a single ordinary collateralized loan against a wildly wrong price.
  • Technical anatomy worth remembering:
    • This is a clean example of the difference between oracle manipulation and oracle configuration failure: nobody had to move the gold market or distort Chainlink itself; the deployer simply instantiated the quoting logic with bad decimal assumptions.
    • Morpho’s oracle code is unusually explicit about the danger. The constructor comments for MorphoChainlinkOracleV2 literally list Decimals passed as argument are correct as an assumption, and SCALE_FACTOR is derived directly from token and feed decimals.
    • That makes the preserved lesson sharper than someone typed the wrong number. The oracle abstraction was flexible enough to support many feed compositions, but that flexibility pushed dimensional correctness onto the market deployer.
    • It is also a useful permissionless-market case study: the immutable base protocol can remain technically correct while a curator-deployed market still goes live with self-destructive pricing.
  • Representative transactions and artifacts:
    • Attack transaction: 0x256979ae169abb7fbbbbc14188742f4b9debf48b48ad5b5207cadcc99ccb493b
    • Attacker EOA: 0x02DBE46169fDf6555F2A125eEe3dce49703b13f5
    • Reported attack contract: 0x4095F064B8d3c3548A3bebfd0Bbfd04750E30077
    • Misconfigured oracle: 0xDd1778F71a4a1C6A0eFebd8AE9f8848634CE1101
    • Market ID: 0x8eaf7b29f02ba8d8c1d7aeb587403dcb16e2e943e4e2f5f94b0963c2386406c9
    • Morpho Blue core contract used by the market: 0xBBBBBbbBBb9cC5e90e3b3Af64bdAF62C37EEFFCb
  • Response / mitigation:
    • LeadBlock published a retrospective on 2024-10-15 stating that the affected PAXG/USDC market was its smallest vault allocation at about 4%, that no user funds were ultimately lost, and that the market should not be used going forward.
    • That same retrospective emphasized the issue was in the market’s oracle setup, not in Morpho Blue’s immutable smart contracts, and said the bad borrow had been repaid to the vault and the lender.
    • A follow-up governance-forum reply then pointed out that the public market UI still appeared to show residual undercollateralized debt and direct-lender exposure; LeadBlock answered that some repayments had been handled outside Morpho specifically to avoid malicious players exploiting the cleanup flow, which is why the onchain market state and the full remediation path did not line up cleanly in the UI.
    • The operator-side takeaway was to add stronger market-creation and testing procedures before launch, especially around final oracle parameter review and live-price sanity checks for curator-deployed markets.

Pattern tags

  • oracle-configuration-failure
  • unit-normalization-failure
  • The preserved failure shape is not Chainlink broke or Morpho core bug: a curator-deployed market instantiated a mathematically general oracle contract with the wrong decimal assumptions, and that constructor-time choice became finished solvency truth for a live PAXG/USDC market.
  • This is also a useful permissionless market launch published nonsense balance-sheet truth exemplar. Morpho Blue’s immutable core kept its contract-level promises, but the market still failed because the deployer-controlled final price() object was dimensionally wrong and immediately honored as borrow power.

Why the exploit worked

  • Morpho’s own oracle contract is unusually explicit about the trust boundary. MorphoChainlinkOracleV2 says outright that Decimals passed as argument are correct is an assumption, and its constructor computes one shared SCALE_FACTOR = 10 ** (36 + quoteTokenDecimals + quoteFeedDecimals - baseTokenDecimals - baseFeedDecimals) ... so that price() returns the quantity of loan asset per unit of collateral, scaled by 1e36.
  • That means the dangerous object was not the raw Chainlink feed. The dangerous object was the deployer’s final dimensional statement about the market: what exactly 1 PAXG should mean in USDC terms after the oracle multiplied feed answers and applied the constructor-time exponent.
  • The attacker succeeded because the market launched with that final dimensional statement already wrong by 10^12. Once the misconfigured oracle published a huge but syntactically valid price() answer, Morpho Blue’s immutable lending core had no basis to reject it as economically absurd; it simply converted the fake collateral mark into real USDC borrowing capacity.
  • The exploit path was therefore the clean permissionless-market version of deposit tiny collateral -> borrow real assets. No external price manipulation, flash-loan distortion, or governance race was needed. The protocol itself exposed the borrow path after the misconfigured market treated about $350 of PAXG like protocol-scale collateral.
  • The preserved lesson is broader than double-check decimals. In a permissionless or curator-deployed system, constructor arguments, deployment manifests, and market-listing review are part of the balance sheet whenever they decide the final oracle object the core will honor.

Detection and audit pivots

  • Reconstruct the final market price as dimensional algebra before launch: collateral / loan, final scale 1e36, raw feed decimals, and the exact exponent encoded into SCALE_FACTOR. If reviewers cannot write that object down plainly, the market is not ready for live borrowing.
  • Treat constructor arguments and deployment calldata as security-critical oracle artifacts, not harmless wiring. Morpho’s oracle code was transparent about its assumptions; the missing control was independent review of the concrete decimals and resulting live answer.
  • Compare the deployed market’s final price() against at least one external reference before enabling meaningful debt. If PAXG appears able to support six-figure USDC borrows from a few hundred dollars of collateral, the system should fail closed before anyone can call the ordinary borrow path.
  • Permissionless-market safety needs a second boundary after immutable-core correctness. Curator factories, front-end listing flows, and market-launch playbooks should quarantine fresh markets with tight caps or isolated exposure until the live price path is reconciled.
  • Cleanup design matters too. LeadBlock’s retrospective and the follow-up forum question preserve a secondary operations lesson: when remediation occurs partly outside the lending core, public onchain state may no longer cleanly express who is economically made whole, so incident notes should preserve both the protocol-state view and the operator-side settlement narrative.

Failure-prevention checklist

  • Treat the final deployed oracle object as the review target, not the upstream feeds in isolation. Reviewers should sign off on what 1 unit of collateral means in loan-token terms, the exact 1e36 scaling, and the constructor-time exponent that produces that answer.
  • Require an independent decimals worksheet before launch. baseTokenDecimals, quoteTokenDecimals, feed decimals, vault-conversion samples, and the implied SCALE_FACTOR should be recomputed outside the deployment script and matched against the onchain constructor arguments.
  • Use deterministic deployment helpers and typed manifests, then still review the manifest. Morpho’s own README recommends the factory, but factory use is not enough if the underlying decimal inputs are wrong.
  • Gate new markets behind low caps, isolated lender exposure, borrow-side throttles, or observation-only windows until the live price() output is reconciled against at least one trusted external quote and one unit test that exercises the exact deployed parameters.
  • Add hard sanity bounds at market activation time: if the final price implies obviously impossible borrow power for a dust collateral deposit, the market should pause or fail closed before debt opens.
  • Separate immutable-core confidence from curator-market confidence. Permissionless or semi-permissionless listing flows still need explicit launch review because the published oracle object is itself a balance-sheet authority surface.
  • Preserve a remediation lane that can make lenders whole without silently leaving ambiguous residual onchain state. If repairs happen partly outside the market, publish that split clearly so later reviewers do not mistake UI residue for unresolved protocol exposure or vice versa.
  • Keep deployment artifacts, oracle manifests, and reviewed constructor arguments together. The dangerous diff is often not contract code changing after audit, but a market going live with one bad parameter tuple.

Audit prompts

  • What exact quantity does this market’s deployed price() claim to return, in plain units, before anyone borrows against it?
  • Where did reviewers independently recompute SCALE_FACTOR, and did they use the collateral token decimals, loan token decimals, feed decimals, and any vault-underlying decimals rather than guessing from UI labels?
  • If I deposit one minimal but nonzero unit of collateral, what borrow power does the market grant, and is that output economically plausible relative to external spot references?
  • Are factory defaults, deployment scripts, or copied manifests carrying hidden decimal assumptions from a prior market with a different quote token or vault shape?
  • What launch-stage controls keep a brand-new market from receiving full borrow authority the moment its oracle is deployed?
  • If the market is permissionless or curator-run, who owns the last this price object is fit for live debt decision, and where is that approval recorded?
  • Can front ends, monitoring, or keepers surface absurd live prices quickly enough to stop opportunistic first-borrow extraction, or is the first meaningful alert still the bad debt itself?
  • If remediation must occur outside the core market, how will operators distinguish economic losses are covered from onchain state still looks broken, and how will lenders verify which view is authoritative?

Lessons that should already be standard

  • Immutable core correctness does not rescue a market whose published price object is nonsense. In lending, the final oracle configuration is part of the balance sheet.
  • Decimal parameters are not clerical metadata. They are solvency logic whenever they decide the exponent on live collateral values.
  • Factory deployment and audited oracle code reduce risk, but neither substitutes for reviewing the exact constructor tuple that the market will honor as debt authority.
  • Fresh markets deserve quarantine. A brand-new collateral path with a new oracle should not inherit full borrow power just because the contracts are standard.
  • The protocol worked as designed and the market was safe are different claims. Permissionless-market systems need explicit controls for the gap between them.
  • Operator remediation can hide or repair economic damage without cleaning up the protocol’s visible state. Incident writeups should preserve that distinction because it affects how later readers interpret residual debt, lender exposure, and UI artifacts.

Why this incident matters

  • It is a compact, high-signal unit-mismatch exemplar for oracle-configuration-failure notes.
  • It preserves a recurring permissionless-protocol lesson: immutable core safety does not save lenders if market deployers can publish nonsense collateral prices.
  • It also shows why dimensional sanity checks belong on the final market price, not just on upstream feed freshness or on faith that a permissionless market deployer passed the constructor arguments correctly.

Internal linkages

Control surface