# The bptpricestablepool() function of bptoracle.sol contract calculates the value of stable bpt incorrectly

BPTOracle's BptPriceStablePool takes the raw minimum token USD price without dividing by each token's rate, mispricing Balancer stable-pool BPT.

- Payout: Contest reward
- Severity: high
- Program: Opal
- Credited: GeneralKay
- Disclosed: 2024-03-30
- Write-up quality: 6/10
- Original: https://cdn.cantina.xyz/reports/cantina_competition_opal_feb2024.pdf#3.2.6
- Library page: https://bugbountylibrary.com/r/cantina%3Aopal_feb2024%3A3.2.6
- Tags: domain:web3 impact:funds-loss kind:contest platform:cantina sev:high tech:evm tech:solidity technique:source-review vuln:oracle-manipulation

**Target:** BPTOracle.sol BptPriceStablePool price oracle
**Root cause:** The minimum token price is selected from raw getUSDPrice values without normalizing by each token's RateProvider rate, so tokens at different peg levels are compared unfairly.
**Steps:**
1. Read `BptPriceStablePool(poolId)` in BPTOracle.sol: it loops pool tokens, takes `min` of `getUSDPrice(token)`, then returns `min * IRateProvider(poolAddress).getRate() / 1e18`.
2. Note constituent tokens may have differing rates/pegs; raw USD prices are not comparable.
3. Observe the result diverges from Balancer's documented valuation (divide each price by its rate before taking min).
4. Fix: `value = value * 1e18 / pool.getTokenRate(token)` before the min comparison, defaulting rate to 1e18 when absent.
**How found:** Code review of the oracle's stable-pool branch against Balancer's documented BPT valuation formula, spotting the missing per-token rate normalization.
**Insight:** When auditing oracle pricing of LP/BPT tokens, compare the implementation line-by-line against the AMM's own documented valuation formula; rate/peg normalization before min/max aggregation is a common omission.
