# Moving average precision is lost

Olympus PRICE module divides each price delta by numObs before accumulating, so small deltas truncate to zero and the moving average drifts unboundedly from reality.

- Payout: Contest reward
- Severity: medium
- Program: Olympus DAO
- Credited: hyh
- Disclosed: 2022-10-26
- Write-up quality: 6/10
- Original: https://github.com/code-423n4/2022-08-olympus-findings/issues/483
- Library page: https://bugbountylibrary.com/r/c4%3A2022-08-olympus%3AM-25
- Tags: domain:web3 impact:funds-loss kind:contest platform:code4rena sev:medium tech:solidity technique:source-review vuln:oracle-manipulation vuln:precision-loss

**Target:** PRICE.sol moving average oracle module
**Root cause:** Each update adds (currentPrice - earliestPrice) / numObs to _movingAverage; when the delta is smaller than numObs the division truncates to 0, so increments are silently dropped and error accumulates without bound.
**Steps:**
1. Observe PRICE.sol L134-139: `_movingAverage += (currentPrice - earliestPrice) / numObs` (and symmetric subtraction).
2. Note delta/numObs truncates to 0 whenever delta < numObs.
3. Example: 10000 moves of size 990 with numObservations=1000 each contribute 0 instead of 9900 total increase.
4. getMovingAverage() returns the drifted _movingAverage directly.
**How found:** Code reading of the moving-average update: noticed division happens per-delta rather than on a cumulative sum, then constructed a numeric example showing truncation to zero.
**Escalation:** Error compounds over time with no bound, potentially leaking value to consumers of the moving average.
**Insight:** In accumulator/oracle code, divide only at read time; dividing each delta by the window size silently truncates small deltas and the error compounds unboundedly even though each step looks negligible.
