# Trades in blocks where the bid or ask drops to zero will be priced using the previous block's price

GMX oracle treats a zero secondary price as 'empty', so trades in a block where bid/ask hits zero are priced at the stale primary price.

- Payout: Contest reward
- Severity: medium
- Program: Gmx
- Credited: IllIllI
- Disclosed: 2022-09-22
- Write-up quality: 6/10
- Original: https://github.com/sherlock-audit/2023-02-gmx-judging/issues/155
- Library page: https://bugbountylibrary.com/r/sherlock%3A2023-02-gmx%3AM-21
- Tags: domain:web3 impact:funds-loss kind:contest platform:sherlock sev:medium tech:solidity technique:source-review vuln:business-logic vuln:oracle-manipulation

**Target:** GMX Synthetics oracle price selection (Oracle.getLatestPrice)
**Root cause:** Price.Props uses zero as a sentinel for 'empty', so a legitimately reported zero secondary price is ignored and the older primary price is used for position increase/decrease pricing.
**Steps:**
1. Oracle stores primaryPrices[token] and secondaryPrices[token] per block.
2. `getLatestPrice` returns secondaryPrice only if `!secondaryPrice.isEmpty()` (i.e. non-zero).
3. If secondary bid or ask is zero, isEmpty() is true, so primaryPrice (previous block) is returned.
4. User submits increase/decrease order in that block; trade executes at stale primary price instead of zero.
**How found:** Code reading of Price.isEmpty() sentinel semantics combined with getLatestPrice fallback logic; noticed zero is overloaded as 'no price set'.
**Exploitation:** A user exits a position in the first block where the price touches zero, receiving the older primary price rather than the true zero price, shifting loss onto the pool and counterparty.
**Insight:** When a struct uses zero as an 'unset' sentinel, check every consumer that treats zero as valid data—price feeds, timestamps, IDs—for silent fallback to stale values.
