# Re-adding a deprecated gauge in a new epoch before calling updatePeriod()/queueRewardsForCycle() will leave some gauges without rewards

Re-adding a deprecated gauge with residual votes before queueRewardsForCycle() corrupts totalWeight accounting, causing some gauges' getAccruedRewards() to revert and lose rewards.

- Payout: Contest reward
- Severity: high
- Program: Maia DAO Ecosystem
- Credited: Voyvoda
- Disclosed: 2023-09-18
- Write-up quality: 7/10
- Original: https://github.com/code-423n4/2023-05-maia-findings/issues/639
- Library page: https://bugbountylibrary.com/r/c4%3A2023-05-maia%3AH-13
- Tags: domain:web3 impact:dos impact:funds-loss kind:contest platform:code4rena sev:high tech:solidity technique:source-review vuln:accounting-error vuln:dos vuln:reward-distribution

**Target:** ERC20Gauges / FlywheelGaugeRewards reward accounting
**Root cause:** _addGauge() writes the re-added gauge's weight into _totalWeight and advances totalWeight.currentCycle before updatePeriod/queueRewardsForCycle runs, so _getStoredWeight() returns the stale storedWeight instead of the updated currentWeight when calculateGaugeAllocation() computes shares.
**Steps:**
1. Deploy gauges with weights (e.g. 75%/25% or 2/2/6/4 split) and run a normal queueRewardsForCycle + getAccruedRewards cycle.
2. removeGauge(gaugeX) to deprecate it; its weight is subtracted from totalWeight but gauge/user weights persist.
3. Warp to a new cycle (block.timestamp + 1000) so gaugeCycle advances.
4. Call addGauge(gaugeX) before queueRewardsForCycle(); _writeGaugeWeight adds weight to _totalWeight and sets currentCycle to the new cycle.
5. Call queueRewardsForCycle(); calculateGaugeAllocation reads _getStoredWeight(_totalWeight, currentCycle) which returns storedWeight (old, smaller) since currentCycle < currentCycle is false.
6. Call getAccruedRewards() for each gauge; the last gauge(s) revert because allocated rewards exceed the queued amount.
**How found:** Code review of _addGauge() noting that gauge weight persists after deprecation and is re-added to _totalWeight; traced _getStoredWeight() cycle comparison to see the storedWeight is returned when currentCycle was already bumped by addGauge before queueRewardsForCycle.
**Exploitation:** A malicious actor monitors for a gauge re-add and front-runs getAccruedRewards() (e.g. via newEpoch() in BaseV2Gauge) for all gauges except a targeted 'whale' gauge, causing the whale gauge's claim to revert and denying its voters rewards for the epoch.
**Escalation:** Targeting a high-weight gauge maximizes the number of voters denied rewards and damages the gauge's reputation; can be repeated each epoch.
**Insight:** When a weight/accounting struct caches storedWeight vs currentWeight keyed by cycle, any write path that advances currentCycle outside the normal period-update flow (like re-adding a deprecated entity) can make later reads return the stale snapshot; audit every writer of the cycle field, not just the intended updater.
