Moonwell cbETH/USD Oracle Misconfiguration / Bad-Debt Liquidations (2026-02-15)
- Name: Moonwell cbETH/USD oracle misconfiguration / bad-debt liquidations
- Date: 2026-02-15
- Protocol: Moonwell
- Chain: Base core market, via governance changes that activated additional OEV wrappers across Base and Optimism
- Category: oracle configuration failure / governance misconfiguration / liquidation cascade / bad debt
- Loss: approximately
$1.78Mof protocol bad debt and1,096.317 cbETHseized from borrowers - Summary: On 2026-02-15, Moonwell executed governance proposal
MIP-X43, which activated Chainlink OEV wrapper configurations for remaining markets. One Base-side cbETH configuration treated the rawcbETH / ETHexchange rate feed as though it were already a USD price. That collapsed Moonwell’s reported cbETH price from roughly$2,200to about$1.12, and liquidation bots immediately converted the error into mass collateral seizures. Borrowers lost most or all of their cbETH collateral while still retaining residual debt, a smaller set of opportunistic users borrowed underpriced cbETH against trivial collateral, and the protocol was left with about$1.78Mof bad debt. - Trigger / entry point:
MIP-X43executed at2026-02-15 18:01 UTC, enabling Chainlink OEV wrapper contracts across Moonwell core markets on Base and Optimism.- The proposal bundle is worth reading at the commit level, not just the merged PR headline. In commit
0898069b7e49b60e98cca0903c04f0ef96695ca3,proposals/ChainlinkOracleConfigs.solalready contained the Base mappingOracleConfig("cbETHETH_ORACLE", "cbETH", "MOONWELL_cbETH")while the same config family still showed the intended Optimism-sideCHAINLINK_cbETH_USD_COMPOSITE_ORACLEpath. - In later commit
318d67818a01ecc4404e90e67fc37e46db27fe40, Moonwell commented out several composite-oracle routes with the noteComposite oracles don't support latestRound(), deferred to follow-up, but the BasecbETHETH_ORACLE -> MOONWELL_cbETHmapping remained live. mip-x43.solthen readOracleConfig[] memory oracleConfigs = getOracleConfigurations(...), deployedcbETHETH_ORACLE_OEV_WRAPPER, and wiredCHAINLINK_ORACLE.setFeed(symbol, wrapperAddress)for the market symbol. In other words, the proposal mechanically provedwrapper deployment and feed wiring, then published the wrong semantic object with no extra normalization step.- That left the Base cbETH market wired to a single-feed OEV wrapper around the raw
cbETH / ETHexchange-rate oracle rather than to a composedcbETH / ETH * ETH / USDdollar price. - The resulting oracle answer was about
1.12, which was correct as acbETH / ETHratio but catastrophically wrong as a USD price.
- Root cause:
- Moonwell misconfigured a composite asset price during an oracle-wrapper migration. The protocol needed
cbETH / ETH * ETH / USDand instead used only the first leg. - This was not a case of attackers manipulating the underlying cbETH market. The false price came from Moonwell’s own configuration and became authoritative because the lending markets treated it as final collateral truth.
- The failure is best understood as an oracle-integration and governance-review breakdown: a lower-level exchange-rate feed was substituted into a slot that required a normalized dollar price.
- The proposal’s validation path was also semantically too weak for this migration.
mip-x43.solvalidated that each symbol pointed at an OEV wrapper and that each wrapper pointed at the configured source feed, but those checks only proved wiring correctness; they did not prove thatMOONWELL_cbETHresolved to a USD-denominated price rather than an exchange-rate ratio. - The commit history sharpens the review failure further: the broader config family already contained composite-oracle entries, and the later proposal revision explicitly deferred several of them because they lacked
latestRound(). That means the dangerous tradeoff was not hidden at runtime; it was visible in source aswe are shipping the simpler lower-level feed now and postponing the semantically safer composite path. - The damage was amplified by governance timing. Moonwell could quickly reduce caps, but correcting the oracle itself still required a five-day governance vote and timelock.
- Moonwell misconfigured a composite asset price during an oracle-wrapper migration. The protocol needed
- Exploit path:
- Moonwell governance executed
MIP-X43, activating additional OEV wrappers and deploying the faulty cbETH oracle configuration. - Moonwell’s cbETH market on Base began valuing cbETH at about
$1.12instead of roughly$2,200. - Liquidation bots immediately repaid tiny amounts of user debt and seized cbETH collateral at a near-total discount, effectively paying about
$1of debt to seize1 cbETH. - Borrowers lost most or all of their cbETH collateral, yet many positions still retained residual debt because the liquidation repayment amount was tiny relative to the true borrowed value.
- A smaller set of opportunistic users moved the other direction, supplying minimal collateral and borrowing cbETH at the fake oracle price, which created additional cbETH-denominated bad debt.
- At
18:05 UTC, Moonwell’s monitoring stack and Anthias Labs detected the discrepancy and reduced the cbETH market’s supply and borrow caps to0.01, preventing new meaningful borrowing and deposits. - The wrong oracle price itself could not be corrected immediately because governance still needed to push a follow-up proposal through a five-day vote and timelock.
- Moonwell governance executed
- What the attacker needed to be true:
- The protocol had to accept the misconfigured cbETH answer as the solvency-critical price for collateral and borrowing.
- Liquidations and borrowing had to remain permissionless, so bots could monetize the wrong price faster than governance could patch it.
- The cbETH market had to have enough open borrowing and collateral activity for the false price to crystallize into both liquidations and bad debt.
- Moonwell needed no onchain sanity check that a cbETH price of
$1.12was dimensionally absurd relative to the market and to the prior answer.
- Technical anatomy worth remembering:
- The decisive mistake was dimensional, not economic: Moonwell used a ratio feed where it needed a dollar price.
MIP-X43is a useful source artifact because the proposal code shows the exact configuration surface that changed. InChainlinkOracleConfigs.sol, Base addedOracleConfig("cbETHETH_ORACLE", "cbETH", "MOONWELL_cbETH")while composite oracle routes were deferred.- That detail matters because it shows the real integration hazard: OEV-wrapper rollout was not just a generic wrapper activation, but a migration that swapped in lower-level oracle primitives with different semantics.
- The proposal’s own validation logic in
mip-x43.solonly checked thatCHAINLINK_ORACLE.getFeed(symbol)now pointed to the new wrapper and that each wrapper’spriceFeed()matched the configured oracle source. That meant the governance pipeline could successfully provecbETH -> cbETHETH_ORACLE_OEV_WRAPPER -> cbETHETH_ORACLEwhile still missing the more important semantic question: whether the terminal value was actually a USD price. - The incident also preserves a second-order lesson about governance latency. Moonwell could freeze market activity quickly by cutting caps, but could not repair the broken price path at the same speed because the fix itself lived behind governance.
- The protocol’s own postmortem records two distinct monetization paths: unfair liquidations against existing borrowers and over-borrowing by new opportunistic users exploiting the underpriced cbETH market.
- This is also a useful
no single exploit contractcase study. The value extraction was spread across permissionless liquidators and opportunistic borrowers using ordinary protocol entrypoints after governance itself had already published the wrong price as collateral truth. The highest-signal forensic artifacts are therefore the proposal commits, Moonwell’s public bad-debt spreadsheet, and the remediation dashboard rather than one canonical attacker transaction.
- Representative contracts and change surfaces:
- Governance proposal:
MIP-X43/ GitHub PR#578—Add MIP-X43: Activate OEV wrappers for all remaining markets - Proposal config file:
proposals/ChainlinkOracleConfigs.sol - Proposal execution / validation file:
proposals/mips/mip-x43/mip-x43.sol - Pre-merge proposal commit showing both the bad Base mapping and the intended composite-oracle model elsewhere in the config family:
0898069b7e49b60e98cca0903c04f0ef96695ca3 - Later proposal commit that kept
OracleConfig("cbETHETH_ORACLE", "cbETH", "MOONWELL_cbETH")while commenting out several composite routes asdeferred to follow-up:318d67818a01ecc4404e90e67fc37e46db27fe40 - Base configuration path introduced in the proposal:
OracleConfig("cbETHETH_ORACLE", "cbETH", "MOONWELL_cbETH") - Base OEV wrapper address added in
chains/8453.json:cbETHETH_ORACLE_OEV_WRAPPER = 0x67996D1ff7a3711a91E2839d1059Fcb950C0495D - Proposal merge commit:
9a6738e189fa903bffa0abf10b9dd7ed2fe73a18 - Proposal wiring step worth preserving:
_wireCoreFeeds()pushedCHAINLINK_ORACLE.setFeed(symbol, wrapperAddress)for each configured symbol after wrapper deployment - Market-freeze response: cbETH market supply and borrow caps reduced to
0.01after detection - Public forensic spreadsheet referenced by Moonwell / Anthias:
cbETH incident 2/15/26 - Public remediation site referenced by Moonwell / Anthias:
https://cbeth-remediation.vercel.app/
- Governance proposal:
- Impact summary worth preserving:
1,096.317 cbETHseized by liquidators- Total bad debt: about
$1,779,044.83 - Full asset-level bad-debt breakout published in Moonwell’s incident summary:
467.7555896 cbETH/$1,033,393.71239.6643802 WETH/$478,998.02232,607.2797 USDC/$232,584.029,719.35397 EURC/$11,566.030.16685141 cbBTC/$11,442.175,481.145179 cbXRP/$7,947.661,520.178946 DAI/$1,520.031,053.312414 USDS/$1,052.15642.4149872 AERO/$204.87126.2302566 MORPHO/$171.670.06824221 wstETH/$164.49
- Moonwell and Anthias also published a spreadsheet of materially impacted user positions, unfair liquidations, and cbETH over-borrow addresses plus a dedicated remediation dashboard, which makes this incident unusually transparent despite not having one canonical exploit transaction.
- Response / mitigation:
- Anthias Labs and Moonwell detected the discrepancy within about four minutes and cut the affected market’s supply and borrow caps to
0.01. - Moonwell prepared a follow-up governance patch to correct the faulty oracle path, but the actual oracle repair still had to wait for governance voting and timelock completion.
- The incident later fed into remediation planning, user-impact dashboards, and community reimbursement discussions.
- Anthias Labs and Moonwell detected the discrepancy within about four minutes and cut the affected market’s supply and borrow caps to
Pattern tags
- oracle-configuration-failure
- unit-normalization-failure
- oracle-migration-quarantine-and-canary-design
- collateral-admission-lag-and-borrow-side-quarantine
- price-domain-and-reciprocal-range-discipline
- The preserved failure was not
Chainlink gave a bad cbETH price; it wasMoonwell published the wrong kind of Chainlink answer as final USD collateral truth. cbETH / ETHwas a real exchange-rate object, but the lending market needed a composed dollar price. Governance review proved wrapper wiring and source-feed identity without proving price-domain correctness.- The incident is therefore a clean
final price object was dimensionally wrong, yet immediately liquidation-authoritativecase rather than a market-manipulation case.
Why the exploit worked
- The migration validated the wrong property.
MIP-X43’s checks proved thatMOONWELL_cbETHnow pointed at an OEV wrapper and that the wrapper pointed at the intended underlying feed, but they never proved that the final answer was denominated in USD rather than incbETH / ETH. - Moonwell let a newly rewired price path become immediate collateral, borrow, and liquidation truth. There was no canary period, shadow-mode comparison, or borrow-side quarantine between
new wrapper is liveandliquidators may trust this output. - The protocol had no strong semantic or range sanity check at the publication boundary. A collateral asset worth about
$2,200was suddenly accepted at about$1.12, and the system treated that as a normal price update instead of a category error. - Liquidators and opportunistic borrowers did not need to manipulate cbETH, ETH, or the oracle network. Once Moonwell itself published the wrong number as money-good truth, permissionless actors only had to use the protocol as designed.
- Governance latency finished the exploit path. Caps could be cut quickly, but the actual oracle correction still lived behind a slower vote and timelock, so the protocol’s fastest response path was containment rather than immediate truth repair.
Detection and audit pivots
- For every listed collateral asset, ask what the final solvency unit is supposed to be: USD price, ETH ratio, stETH ratio, share price, exchange rate, or something else. Then verify that the object actually published into liquidation logic matches that unit exactly.
- Treat wrapper migrations and feed substitutions as semantic migrations, not just address rewires. Test the composed path end to end against expected quote currency, decimals, reciprocal behavior, and economically plausible range.
- Keep a shadow oracle or pre-migration reference path running long enough to diff
new outputversusold outputbefore granting the new path full borrow and liquidation authority. - Add publication-boundary sanity checks for category errors: if a large-cap collateral asset suddenly reprices from four digits to about
1, or a ratio feed is being consumed where a dollar feed is expected, fail closed rather than publish. - Separate
feed changed successfullyfrommarket may now liquidate on it. Borrow caps, collateral factors, or liquidation permissions should stay constrained until the new path survives monitoring and reconciliation. - Review governance validation code for semantic completeness.
wrapper points to source feedis not the same asmarket receives the right economic object.
Failure-prevention checklist
- Treat composite-oracle rollout as a balance-sheet migration, not a wiring chore. Governance proposals should prove the final published object, quote currency, decimals, and branch-local semantics before activation, not only that wrappers and source feeds are connected.
- Keep ratio feeds and dollar feeds in different typed configuration lanes where possible. If a market expects
asset / USD, a rawasset / ETHorasset / wrapperanswer should fail schema validation unless the missing quote leg is explicitly composed. - Gate new oracle paths behind canary or shadow mode before they receive full liquidation authority. A newly wired wrapper should first survive reference diffs, plausibility bands, and operator review while borrow caps, collateral factors, or liquidation permissions remain constrained.
- Encode dimensional sanity checks near publication. Large-cap collateral should not be allowed to reprice from the low thousands to about
1without failing closed, paging operators, or automatically freezing the market. - Review governance validation code for semantic completeness.
wrapper points to feedandfeed address is correctare necessary but insufficient if the market still expects a different economic object at the output boundary. - Keep emergency containment faster than semantic repair. If the real oracle fix must traverse vote and timelock latency, the protocol still needs a scoped fast path to freeze borrow and liquidation relevance immediately when a new answer is category-wrong.
- Rehearse wrapper migrations against independent references and historical output snapshots. The useful question is not only
does the new path return a numberbutdoes it still return the same market truth as the old path under realistic states.
Audit prompts
- What exact object is this market supposed to publish into solvency logic:
cbETH / USD,cbETH / ETH, wrapper share price, capped ratio, or something else? - Which contract or governance artifact is responsible for composing the final quote leg, and what proves that composition was not silently skipped during rollout?
- If a wrapper path is changed today, where does the protocol compare
old answer,new answer, and an independent reference before liquidations are allowed to trust the new branch? - Can governance or deployment automation bind a semantically valid lower-level feed into a slot that expected a different final unit object while still passing validation?
- What range checks make dimensionally absurd but syntactically valid outputs unpublishable, especially for blue-chip or large-cap collateral?
- Which emergency controls can immediately narrow borrow caps, collateral factors, or liquidation permissions when a newly deployed oracle path diverges from prior truth?
- Does the protocol distinguish
oracle branch is livefromoracle branch is allowed to trigger liquidations, or are those still the same state transition? - If a migration defers a safer composite path because it lacks one helper interface such as
latestRound(), what alternate proof shows the fallback branch still publishes the right market object rather than a locally correct but economically incomplete ratio?
Lessons that should already be standard
- A correctly addressed feed can still be the wrong answer. The decisive question is the final economic object the market publishes, not whether the source contract was real.
- Oracle-wrapper migrations are solvency events. Swapping a composite path for a lower-level primitive can liquidate users even when no external market was manipulated.
- Governance review must prove semantics, not only plumbing.
wrapper -> source feedvalidation is weaker thanmarket still receives the intended USD collateral price. - Newly activated oracle paths should not get instant liquidation authority. Shadow mode, canaries, caps, and borrow-side quarantine are not optional ceremony when the migration can change units or quote legs.
- Category errors deserve fail-closed treatment. A collateral asset repricing from thousands of dollars to about
1is usually notvolatility; it is a dimensional mistake until proven otherwise. - Fast containment and slow repair are different controls. Moonwell could shrink caps quickly, but the deeper lesson is that every governance-bound oracle system still needs a faster path to stop wrong truth from liquidating users.
- Permissionless users do not need to manipulate a market if the protocol misprices itself. Once Moonwell published fiction as liquidation truth, bots only had to act normally.
Why this incident matters
- It is a clean example of a protocol self-inflicting an oracle disaster without any underlying market manipulation.
- It shows how wrapper migrations and oracle-upgrade programs can silently change the dimensional meaning of a feed, even when the code review topic sounds operationally routine.
- It also preserves the governance-time mismatch: once a wrong price is live, permissionless liquidators run on millisecond time, while DAO repair often runs on day-scale time.
Internal linkages
- Core recurring pattern: oracle-configuration-failure.
- Narrower recurring lens: unit-normalization-failure.
- Rollout / governance-ops companion: operator-experimentation-separation-from-live-balance-sheet-authority — the decisive failure was not stolen governance power, but that an authorized wrapper-migration change surface could publish a new price semantic directly into liquidation truth.
- Price-shape companion: price-domain-and-reciprocal-range-discipline — useful when the sharper question is how governance and adapter code should have proved
this object is a USD collateral pricerather than onlythis object is some valid oracle answer. - Rollout-staging companion: oracle-migration-quarantine-and-canary-design — useful when the sharper question is how Moonwell should have previewed and bounded the new OEV-backed oracle path before granting it full collateral and liquidation authority.
- Borrow-side rollout companion: collateral-admission-lag-and-borrow-side-quarantine — useful when the sharper question is whether a freshly rewired collateral price path should have had immediate full borrow and liquidation authority at all.
- Adjacent but distinct family: oracle-manipulation.
- Useful comparison incident: aave-wsteth-capo-snapshot-misalignment-liquidations-2026-03-10.
Control surface
-
The leverage sat in Moonwell’s oracle-composition and governance-deployment path: which feed got wired into
MOONWELL_cbETH, how wrapper activation changed feed semantics, and whether any sanity check blocked a ratio from being treated as a dollar price. -
The incident is also a clean operator-separation failure:
MIP-X43was allowed to act as both rollout step and immediate publication of money-good collateral truth, with no bounded canary phase betweencandidate config existsandliquidations may use it. -
The market did not need to be manipulated. Once Moonwell itself published fiction as protocol truth, bots simply harvested the gap.
-
Sources:
- https://forum.moonwell.fi/t/mip-x43-cbeth-oracle-incident-summary/2068
- https://forum.moonwell.fi/t/mip-x43-cbeth-oracle-incident-summary/2068.json
- https://github.com/moonwell-fi/moonwell-contracts-v2/pull/578
- https://github.com/moonwell-fi/moonwell-contracts-v2/pull/578/commits/0898069b7e49b60e98cca0903c04f0ef96695ca3
- https://github.com/moonwell-fi/moonwell-contracts-v2/pull/578/commits/318d67818a01ecc4404e90e67fc37e46db27fe40
- https://raw.githubusercontent.com/moonwell-fi/moonwell-contracts-v2/0898069b7e49b60e98cca0903c04f0ef96695ca3/proposals/ChainlinkOracleConfigs.sol
- https://raw.githubusercontent.com/moonwell-fi/moonwell-contracts-v2/318d67818a01ecc4404e90e67fc37e46db27fe40/proposals/ChainlinkOracleConfigs.sol
- https://raw.githubusercontent.com/moonwell-fi/moonwell-contracts-v2/0898069b7e49b60e98cca0903c04f0ef96695ca3/proposals/mips/mip-x43/mip-x43.sol
- https://docs.google.com/spreadsheets/d/1AqMq56UD9u8kdRRtIju1NWRL-jLzh3pHQGhFW0FWEkw/edit?usp=sharing
- https://cbeth-remediation.vercel.app/
- https://rekt.news/moonwell-rekt
-
Last reviewed: 2026-08-05 UTC