# Incorrect TWAP calculation in BalancerPriceOracle allows price manipulation and breaks oracle guarantees

BalancerPriceOracle.updatePrice accumulates the derived averagePrice instead of the observed spot price, breaking TWAP and enabling pool price manipulation to corrupt the oracle.

- Payout: Contest reward
- Severity: high
- Program: Olas
- Credited: BenRai
- Disclosed: 2026-05-18
- Write-up quality: 6/10
- Original: https://code4rena.com/reports/2026-01-olas#h-04
- Library page: https://bugbountylibrary.com/r/c4%3A2026-01-olas%3AH-04
- Tags: domain:web3 impact:funds-loss kind:contest platform:code4rena sev:high tech:evm tech:solidity technique:source-review vuln:oracle-manipulation

**Target:** BalancerPriceOracle.sol updatePrice() TWAP oracle
**Root cause:** cumulativePrice accumulates snapshot.averagePrice (a derived average) rather than the actual observed price, and total elapsed time is recovered by dividing cumulativePrice by averagePrice, which is invalid after the first update and compounds rounding error.
**Steps:**
1. Read updatePrice() at BalancerPriceOracle.sol L84-L128.
2. Observe `snapshot.cumulativePrice += snapshot.averagePrice * elapsedTime` uses averagePrice, not the last observed spot price.
3. Observe averagePrice = (cumulativePrice + currentPrice*elapsedTime) / ((cumulativePrice/averagePrice) + elapsedTime), which assumes cumulativePrice = averagePrice*totalTime.
4. Wait minUpdateTimePeriod, manipulate Balancer pool spot price, call updatePrice() with manipulated currentPrice.
5. Broken formula fails to weight manipulation by duration, corrupting averagePrice for subsequent updates.
**How found:** Code review of the TWAP formula: noticed cumulativePrice accumulates averagePrice rather than the observed price, and that elapsed time is back-derived by dividing cumulativePrice by averagePrice, which only holds for the first update.
**Exploitation:** Attacker manipulates the Balancer pool price after minUpdateTimePeriod elapses, then calls updatePrice() with the manipulated price; the recursive averaging fails to discount the manipulation, corrupting the stored average and enabling further manipulation of subsequent price updates.
**Insight:** When auditing TWAP oracles, check that the accumulator stores the raw observed price (or price cumulative) per interval, not a derived average; any division to recover elapsed time signals a broken invariant that compounds over updates.
