# Stale oracle price consumption

Risk logic reads a price or rate without a real freshness check, or reuses a cached snapshot after value changed

_Area: DeFi oracles, liquidations and flash loans. Seen in 52 disclosed reports; latest 2025-02-24. Updated 2026-10-23._

## How it works
A protocol reads a price or exchange rate and treats it as current, but the value was captured earlier and never refreshed, or the source only updates on specific events (a swap, a heartbeat, a rebase). Between updates the number no longer reflects reality, yet minting, borrowing, liquidation, or settlement acts on it. The broken assumption is "the price I read equals the price now." A subtler failure: the read looks fresh because a timestamp check passes, but the timestamp itself is wrong, the tolerance is too wide, or the fallback silently returns a cached value, so freshness is asserted without being true.

## Where it shows up
- Oracle reads with no `updatedAt`/heartbeat comparison against `block.timestamp`.
- Wrappers that fall back to a cached rate when the oracle reports not-updated [E5](https://bugbountylibrary.com/r/sherlock%3A2024-02-tapioca%3AM-17.md).
- Order-time snapshots (`shortOrder.price`, stored CR) reused in later settlement math [E1](https://bugbountylibrary.com/r/c4%3A2024-07-dittoeth%3AH-02.md).
- Privileged staleness parameter (`staleAfter`) with no upper bound [E2](https://bugbountylibrary.com/r/sherlock%3A2024-08-perennial-v2-update-3%3AH-6.md).
- Instant redemption at an exchange rate before a pending rebase/slashing lands [E3](https://bugbountylibrary.com/r/sherlock%3A2024-05-napier-update%3AH-2.md).
- Feeds with wide deviation bands and long heartbeats pricing mints [E4](https://bugbountylibrary.com/r/c4%3A2023-11-kelp%3AH-01.md).
- Oracle wrapper returning block time instead of last-update time, so staleness checks always pass [E6](https://bugbountylibrary.com/r/c4%3A2024-11-nibiru%3AM-10.md).
- Fixed staleness buffer added to every heartbeat, tripling tolerance for short-heartbeat feeds [E8](https://bugbountylibrary.com/r/cantina%3Abima_december2024%3A3.1.1.md).
- Price that only decays on state-changing calls, so a terminal action (migration) uses a stale higher price [E7](https://bugbountylibrary.com/r/cantina%3Adoppler%3A3.2.3.md).

## How to review
The invariant: every value used for a risk-increasing or value-transferring action must be provably fresh and derived from the correct source at the moment of use. For each read: does it compare timestamp to a bounded heartbeat, e.g. `require(block.timestamp - updatedAt <= maxAge)`? Does it use live oracle price rather than a stored snapshot? Does the source update on the events that change value? For privileged parameters, are staleness limits bounded? For event-driven sources (rebase, slashing), confirm the triggering event is the only way value moves and that instant exits cannot precede it. For wrappers mimicking Chainlink, verify the keeper stores per-price timestamps, not `ctx.BlockTime()` [E6](https://bugbountylibrary.com/r/c4%3A2024-11-nibiru%3AM-10.md). Compare any fixed buffer against the shortest configured heartbeat [E8](https://bugbountylibrary.com/r/cantina%3Abima_december2024%3A3.1.1.md). For lazy-decay designs, confirm the terminal action applies accumulated decay rather than reading the last stored price [E7](https://bugbountylibrary.com/r/cantina%3Adoppler%3A3.2.3.md).

## Variants seen
- Stale order snapshot in partial-fill cleanup [E1](https://bugbountylibrary.com/r/c4%3A2024-07-dittoeth%3AH-02.md)
- Unbounded staleness parameter forcing settlement on old price [E2](https://bugbountylibrary.com/r/sherlock%3A2024-08-perennial-v2-update-3%3AH-6.md)
- Instant redemption before rebase/slashing reflected [E3](https://bugbountylibrary.com/r/sherlock%3A2024-05-napier-update%3AH-2.md)
- Wide deviation bands and long heartbeats lagging spot [E4](https://bugbountylibrary.com/r/c4%3A2023-11-kelp%3AH-01.md)
- Cached-rate fallback in solvency check [E5](https://bugbountylibrary.com/r/sherlock%3A2024-02-tapioca%3AM-17.md)
- Wrong timestamp source defeating staleness check [E6](https://bugbountylibrary.com/r/c4%3A2024-11-nibiru%3AM-10.md)
- Lazy decay skipped before migration [E7](https://bugbountylibrary.com/r/cantina%3Adoppler%3A3.2.3.md)
- Oversized fixed staleness buffer [E8](https://bugbountylibrary.com/r/cantina%3Abima_december2024%3A3.1.1.md)

## Why fixes fail
E1 shows a fix that still used the stale snapshot path. Verify the fix covers every sibling path and the exact arithmetic, not just the reported entry point: partial-fill cleanup must recompute from live oracle price and protocol-minimum CR. A patch adding a freshness check at one call site leaves other readers of the same snapshot or feed unprotected, and a wrapper that passes a timestamp check can still be stale if the timestamp is wrong [E6](https://bugbountylibrary.com/r/c4%3A2024-11-nibiru%3AM-10.md) or the tolerance is too wide [E8](https://bugbountylibrary.com/r/cantina%3Abima_december2024%3A3.1.1.md).

## Impact and severity
Led to under-collateralized minting [E1](https://bugbountylibrary.com/r/c4%3A2024-07-dittoeth%3AH-02.md), market collateral drain [E2](https://bugbountylibrary.com/r/sherlock%3A2024-08-perennial-v2-update-3%3AH-6.md), holder dilution [E4](https://bugbountylibrary.com/r/c4%3A2023-11-kelp%3AH-01.md), bad debt from atomic leverage [E5](https://bugbountylibrary.com/r/sherlock%3A2024-02-tapioca%3AM-17.md), and migration at a wrong price [E7](https://bugbountylibrary.com/r/cantina%3Adoppler%3A3.2.3.md). Severity rises when the stale value gates minting, liquidation, or settlement, and when the trigger is permissionless or mempool-visible.

## Evidence
- E1: https://bugbountylibrary.com/r/c4%3A2024-07-dittoeth%3AH-02.md
- E2: https://bugbountylibrary.com/r/sherlock%3A2024-08-perennial-v2-update-3%3AH-6.md
- E3: https://bugbountylibrary.com/r/sherlock%3A2024-05-napier-update%3AH-2.md
- E4: https://bugbountylibrary.com/r/c4%3A2023-11-kelp%3AH-01.md
- E5: https://bugbountylibrary.com/r/sherlock%3A2024-02-tapioca%3AM-17.md
- E6: https://bugbountylibrary.com/r/c4%3A2024-11-nibiru%3AM-10.md
- E7: https://bugbountylibrary.com/r/cantina%3Adoppler%3A3.2.3.md
- E8: https://bugbountylibrary.com/r/cantina%3Abima_december2024%3A3.1.1.md
