# If there hasn't been an update for more than an epoch prior to migration, migration will happen at wrong price

Doppler skips price decay for epochs with no rebalance before migration, so the pool migrates at a higher-than-intended price.

- Payout: Contest reward
- Severity: medium
- Program: Doppler
- Credited: deadrosesxyz
- Disclosed: 2025-02-18
- Write-up quality: 6/10
- Original: https://cdn.cantina.xyz/reports/cantina_competition_doppler.pdf#3.2.3
- Library page: https://bugbountylibrary.com/r/cantina%3Adoppler%3A3.2.3
- Tags: domain:web3 impact:funds-loss kind:contest platform:cantina sev:medium tech:evm tech:solidity technique:regression technique:source-review vuln:accounting-error vuln:business-logic

**Target:** Airlock migration / epoch price rebalancing hook
**Root cause:** Price only decays on rebalance calls; if no rebalance occurs for the final epochs before migration, the accumulated inactivity decay is never applied, so migration uses a stale higher price.
**Steps:**
1. Deploy via `test_create_DeploysV4()` to get hook and asset
2. `vm.warp(DEFAULT_ENDING_TIME - 2*DEFAULT_EPOCH_LENGTH)` and buy 1e18 via `router.buyExactOut(1e18)`
3. `vm.warp(DEFAULT_ENDING_TIME)` (skip last epoch, no rebalance) and call `airlock.migrate(asset)`
4. Read UniswapV2 pair reserves; reserve ratio ~2.5417e15
5. Compare with test 2 that adds a tiny buy at `DEFAULT_ENDING_TIME - 1` yielding ratio ~2.5094e15 — less buy activity but lower (correct) price
**How found:** Code reading of the migration loop: noticed price updates only happen inside rebalance/modifyLiquidity paths, so epochs without rebalance never apply decay; hypothesized migration would use stale price and confirmed with two PoC tests comparing reserve ratios.
**Exploitation:** Attacker (or passive holder) benefits because migration occurs at ~2% higher price than the intended decayed price; no active capital needed, just timing migration after an epoch with no rebalance.
**Insight:** In epoch-based Dutch-auction/decay designs, check whether decay is applied lazily on read vs. only on state-changing rebalance calls; a gap between last rebalance and a terminal action (migration, settlement) leaves stale price. Compare reserve ratios across scenarios with/without a final-epoch touch to expose it.
