# `OracleVersion latestVersion` of `Oracle.status()` may go backwards when updating to a new oracle provider and result in wrong settlement in `_processPositionLocal()`.

Oracle provider switch can move latestVersion backwards, causing Market to settle positions against a non-existent version with zero values, mis-accounting collateral.

- Payout: Contest reward
- Severity: medium
- Program: Perennial
- Credited: WATCHPUG
- Disclosed: 2023-07-17
- Write-up quality: 7/10
- Original: https://github.com/sherlock-audit/2023-07-perennial-judging/issues/145
- Library page: https://bugbountylibrary.com/r/sherlock%3A2023-07-perennial%3AM-9
- Tags: domain:web3 impact:funds-loss kind:contest platform:sherlock sev:medium tech:solidity technique:source-review vuln:accounting-error vuln:oracle-manipulation

**Target:** Perennial Oracle and Market contracts
**Root cause:** Oracle.update(newProvider) does not require newProvider.latest().timestamp > oldProvider.latest().timestamp, so latestVersion can go backwards. Market._settle then skips the global settlement check and _processPositionLocal uses an empty version, settling PNL with zero maker/long/short values.
**Steps:**
1. At 13:40, oracleProvider1.latest().timestamp = 13:30; market.update(account1) stores _versions[13:00] and _versions[13:30].
2. At 13:41, call oracle.update(oracleProvider2) where oracleProvider2.latest().timestamp = 13:20.
3. Call market.update(account2) -> _settle(): L350 is skipped because global settled to newer timestamp; L356 sees 13:20 > 13:00 and enters _processPositionLocal().
4. In _processPositionLocal, nextPosition.timestamp = 13:20 but version is empty; L440 accumulates with empty version, producing wrong PNL and collateral deduction.
**How found:** Code review of Oracle.update and Market._settle/_processPositionLocal, noticing no monotonicity check on provider timestamps and that an empty version is used when nextPosition.timestamp has no stored version.
**Exploitation:** Requires admin oracle provider switch to a provider whose latest timestamp is older than the previous provider's latest. A user with a pending position at a timestamp after the previous provider's latest (and no new position request) gets settled against an empty version, temporarily mis-accounting collateral until a valid version appears.
**Insight:** When a contract allows swapping an external data source, check that the new source's monotonic invariants (e.g., timestamp ordering) are enforced; otherwise downstream settlement logic that assumes monotonic versions can silently use empty/default values.
