Two Oracle Failures. Thirty Days. The Same Root Cause.

On February 18, a DeFi lending protocol woke up to find its cbETH market had been drained overnight. Not by a hacker. By legitimate liquidation bots doing exactly what they were designed to do, because the oracle told them cbETH was worth $1.12. The real price was around $2,200.
By the time the governance vote passed and the five-day timelock expired, Moonwell was sitting on $1.78 million in bad debt.
Twenty days later, on March 10, a similar sequence played out at Aave. A parameter mismatch in the CAPO oracle system undervalued wstETH by roughly 2.85%. Liquidation bots responded immediately. By the time the incident was contained, $27 million in positions had been liquidated across 34 accounts, and liquidators had captured 499 ETH in bonuses that should never have been available.
Two different protocols. Two different oracle systems. Two different assets. Thirty days apart.
Same root cause.
The problem is not the oracle. It is the gap.
In both incidents, the oracle components worked exactly as specified. That is the part worth sitting with.
Moonwell's Chainlink OEV wrapper was configured correctly for the cbETH/ETH exchange rate. The problem was that the system was supposed to multiply that rate by an ETH/USD price feed to produce a dollar value. Somewhere in the MIP-X43 governance execution, those two parameters got separated. Each one was valid. Together, they produced a price of $1.12.
Aave's CAPO system uses a time-weighted reference exchange rate with a built-in ceiling to prevent price manipulation. The ceiling works by comparing the current price against a cached reference value. When the reference value in one contract and the timestamp in another contract fell out of sync, the CAPO ceiling activated at the wrong level. The oracle was doing its job. The inputs it was working from were stale.
This is oracle configuration drift. Parameters that are defined separately, updated on different schedules, or written to different contracts at different points in a governance execution can become valid in isolation and wrong in combination. Static audits catch this when the code ships. They cannot catch it after a governance vote runs six months later and one parameter gets updated while another does not.
The question is not whether your oracle code passed its last audit. It is whether your oracle inputs are consistent with each other right now.
What a runtime monitoring layer changes
Both incidents share a specific signature that is detectable before liquidations start: the reported oracle price and the expected price derived from alternative sources diverge by an amount that is implausible given normal market conditions.
cbETH trading at $1.12 when every other data source shows it above $2,000 is not a volatile market condition. It is a system error. The same is true for a CAPO ceiling activating on a liquid, well-behaved asset like wstETH during a period of ordinary price movement.
A Watcher keeper monitoring for this signature does not need to understand the internal architecture of your oracle system. It needs three things: the reported oracle price, a reference price from an independent source (a DEX TWAP, a second Chainlink feed, the underlying asset price adjusted by the exchange rate), and a threshold at which the divergence triggers an alert or a circuit breaker.
When the reported price falls outside the expected range, the Watcher fires. The response can be an alert to your team, an automated call to pause a specific market's borrows, or a trigger to execute a predefined emergency governance action. The choice of response belongs to the protocol. What matters is that the detection happens in real time, not after the liquidation bots have already run.
This is not a replacement for audits. Audits catch structural problems at deployment time. Continuous monitoring catches runtime drift after deployment. You need both, for the same reason that a building inspection and a fire alarm serve different functions: one checks whether the building was constructed correctly, the other catches what goes wrong during operation.
What this means in practice
KeeperHub's Watcher keeper runs continuous cross-source checks on oracle prices. When the reported price diverges from an independent reference beyond a configurable threshold, it fires before the liquidation window opens. It does not need to understand the oracle's internal mechanics. It only needs to know what price the oracle is reporting and what price it should plausibly report. The delta tells the story.
That is what a Reliable Execution Layer means in practice: not just running your automations reliably, but watching the conditions those automations depend on, continuously, so that when a parameter drifts, you find out before the bots do.
Yield-bearing asset oracles are structurally more complex than simple price feeds, and every added component is another place where a parameter can drift after a governance action updates one piece of the system. These two incidents are not outliers. They are early examples of a pattern that will recur as long as protocols rely on deployment-time verification alone.
The cost was $29 million. The fix, in both cases, was straightforward once the anomaly was detected. Detection was the problem.
If you are running a lending protocol with yield-bearing asset collateral, the question worth asking is not whether your oracle passed its last audit. It is whether anything would catch the next configuration drift before it reaches a liquidation engine.
We're happy to walk through what that looks like for your setup. Talk to us.


