# Intra-transaction oracle price selection

Can one tx read two prices for the same asset, or flip an oracle status branch?

_Area: DeFi oracles, liquidations and flash loans. Seen in 4 disclosed reports; latest 2024-03-29. Updated 2026-10-23._

## How it works
A protocol assumes an asset's price is fixed for the duration of a transaction. That breaks when the oracle can be updated or re-read more than once inside one tx. In a pull oracle (Pyth), the caller supplies a signed price to each state-changing call, so two valid prices can price two legs of one economic action (deposit vs withdraw, adjust vs close) at two chosen values, capturing the difference risk-free. A distinct break: an oracle state machine returns a different branch on the second call in the same tx, so a status downgrade is undone and a stale price is replaced by a current one mid-transaction.

## Where it shows up
- Pull oracles (Pyth) where the caller supplies the price update argument to each state-changing call.
- Oracle reads guarded only by `maxAge`/timestamp, with no per-block or per-tx dedup.
- Oracle state machines with status flags (last-good vs current, trusted vs untrusted) where a branch returns a stale price and downgrades status.
- Paired operations in one tx: deposit+withdraw, adjust+close, announce+execute.

## How to review
The invariant: within a single transaction, every consumer of a given asset's price must see the same value. Check each path that accepts a price argument or calls `fetchPrice`/`updatePrice`; confirm updates are deduplicated per block/tx, or that the price is read once into a local variable and reused across both legs. For state machines, verify every status transition is idempotent: a second call in the same tx cannot undo a downgrade or return a different branch. Keep the two mechanisms separate: E1 is a state-machine branch flip, not a pull-oracle update.

## Variants seen
- State machine returns last-good then current price in one tx; downgrade undone by next call [E1](https://bugbountylibrary.com/r/c4%3A2023-10-badger%3AM-01.md).
- Two valid Pyth prices used to deposit and withdraw LP shares at different prices [E2](https://bugbountylibrary.com/r/sherlock%3A2024-02-perpetual%3AH-1.md).
- Two Pyth prices price adjust and limit-close legs differently [E3](https://bugbountylibrary.com/r/sherlock%3A2023-12-flatmoney%3AM-8.md).
- Withdrawal and deposit each take caller-supplied price, settled at different prices [E4](https://bugbountylibrary.com/r/sherlock%3A2024-03-flat-money-fix-review-contest%3AM-3.md).

## Why fixes fail
A fix that closes only the originally reported path leaves sibling paths consuming the same price argument exploitable [E4](https://bugbountylibrary.com/r/sherlock%3A2024-03-flat-money-fix-review-contest%3AM-3.md).

## Impact and severity
Risk-free value extraction from LPs and CDPs; high severity where paired operations exist. Worse when no fallback oracle is set [E1](https://bugbountylibrary.com/r/c4%3A2023-10-badger%3AM-01.md).

## Evidence
- E1: https://bugbountylibrary.com/r/c4%3A2023-10-badger%3AM-01.md
- E2: https://bugbountylibrary.com/r/sherlock%3A2024-02-perpetual%3AH-1.md
- E3: https://bugbountylibrary.com/r/sherlock%3A2023-12-flatmoney%3AM-8.md
- E4: https://bugbountylibrary.com/r/sherlock%3A2024-03-flat-money-fix-review-contest%3AM-3.md
