# Oracle round and version selection errors

Oracle round/version selection returning wrong or invalid rounds, bricking settlement, limits, or provider switches

_Area: DeFi oracles, liquidations and flash loans. Seen in 3 disclosed reports; latest 2023-10-26. Updated 2026-11-06._

## How it works
Oracle settlement code must pick a specific round or version and decide whether it is valid. The assumption that breaks is that the selection logic (binary search, switch gating, invalidation) always returns a usable round and that exceptional paths still enforce the same invariants as the normal path. When selection returns 0, an uncommittable timestamp, or an invalidated version, downstream accounting runs on a wrong or missing price and either reverts forever or accepts a bad value [E1](https://bugbountylibrary.com/r/sherlock%3A2023-10-perennial%3AM-3.md)[E2](https://bugbountylibrary.com/r/sherlock%3A2023-07-perennial%3AM-3.md)[E3](https://bugbountylibrary.com/r/sherlock%3A2023-05-perennial%3AM-1.md).

## Where it shows up
- Binary search over roundIds with a `minRoundId+1 !< maxRoundId` style termination that only validates the upper bound [E3](https://bugbountylibrary.com/r/sherlock%3A2023-05-perennial%3AM-1.md)
- Sparse or invalid round ranges, new phases with a single valid round [E3](https://bugbountylibrary.com/r/sherlock%3A2023-05-perennial%3AM-1.md)
- State machines that gate a transition on "last item committed" where the commit path has time-bounded validation [E2](https://bugbountylibrary.com/r/sherlock%3A2023-07-perennial%3AM-3.md)
- Provider-switch logic (`_handleLatest`) that waits on the previous provider's last request [E2](https://bugbountylibrary.com/r/sherlock%3A2023-07-perennial%3AM-3.md)
- Invalidation of pending positions that adjusts global accounting outside normal limit checks [E1](https://bugbountylibrary.com/r/sherlock%3A2023-10-perennial%3AM-3.md)
- Asymmetric guards: one direction has a `closable`/limit check, the mirror direction has none [E1](https://bugbountylibrary.com/r/sherlock%3A2023-10-perennial%3AM-3.md)

## How to review
For each selection path, ask: can this return 0, an out-of-window timestamp, or an invalidated version, and what does the caller do with it? The invariant: every returned round/version is validated against the target timestamp and is either usable or causes a bounded, recoverable failure — never a permanent revert or a silently wrong price. Check that both directions of any accounting asymmetry enforce the same limits, including exceptional invalidation paths [E1](https://bugbountylibrary.com/r/sherlock%3A2023-10-perennial%3AM-3.md). Check that any "wait for last commit" gate has a commit path that cannot become permanently impossible [E2](https://bugbountylibrary.com/r/sherlock%3A2023-07-perennial%3AM-3.md). For binary searches, verify the lower-bound candidate is validated before returning and test the single-valid-round edge case [E3](https://bugbountylibrary.com/r/sherlock%3A2023-05-perennial%3AM-1.md).

## How finders spotted it
- Read `_processPositionGlobal` invalidation and `_invariant`; noticed underflow guarded by `closable` but the symmetric increase had no `openable` guard [E1](https://bugbountylibrary.com/r/sherlock%3A2023-10-perennial%3AM-3.md)
- Read `Oracle._handleLatest` switch condition against `PythOracle.commitRequested`'s bounded timestamp window; reasoned the last request could fall outside any available price window [E2](https://bugbountylibrary.com/r/sherlock%3A2023-07-perennial%3AM-3.md)
- Manual review of binary search loop bounds and termination; reasoned about a phase with one valid round [E3](https://bugbountylibrary.com/r/sherlock%3A2023-05-perennial%3AM-1.md)

## Variants seen
- Invalid oracle version invalidates pending positions without limit checks, pushing global maker over `makerLimit` [E1](https://bugbountylibrary.com/r/sherlock%3A2023-10-perennial%3AM-3.md)
- Provider switch deadlock when the previous provider's last request can never be committed [E2](https://bugbountylibrary.com/r/sherlock%3A2023-07-perennial%3AM-3.md)
- Binary search returns 0 when only one valid round exists in a new phase [E3](https://bugbountylibrary.com/r/sherlock%3A2023-05-perennial%3AM-1.md)

## Impact and severity
All three brick or DOS the Market: `Market.update` reverts until the limit is raised, potentially permanently near 2^62-1 [E1](https://bugbountylibrary.com/r/sherlock%3A2023-10-perennial%3AM-3.md); price updates freeze with no fix path [E2](https://bugbountylibrary.com/r/sherlock%3A2023-07-perennial%3AM-3.md); oracle temporarily DOSed or wrong roundId selected [E3](https://bugbountylibrary.com/r/sherlock%3A2023-05-perennial%3AM-1.md). Impact rises when the brick is permanent and blocks withdrawals [E1](https://bugbountylibrary.com/r/sherlock%3A2023-10-perennial%3AM-3.md), or when a workaround exposes an invalid zero-price version a backrunner can exploit [E2](https://bugbountylibrary.com/r/sherlock%3A2023-07-perennial%3AM-3.md).

## Evidence
- E1: https://bugbountylibrary.com/r/sherlock%3A2023-10-perennial%3AM-3.md
- E2: https://bugbountylibrary.com/r/sherlock%3A2023-07-perennial%3AM-3.md
- E3: https://bugbountylibrary.com/r/sherlock%3A2023-05-perennial%3AM-1.md
