# Incorrect Reward Duration After Change in Reward Speed in MultiRewardStaking

MultiRewardStaking recomputes reward end time from contract token balance, ignoring unclaimed owed rewards, letting stakers over-earn and draining funds.

- Payout: Contest reward
- Severity: high
- Program: Popcorn
- Credited: waldenyan20
- Disclosed: 2023-09-07
- Write-up quality: 6/10
- Original: https://github.com/code-423n4/2023-01-popcorn-findings/issues/424
- Library page: https://bugbountylibrary.com/r/c4%3A2023-01-popcorn%3AH-03
- Tags: domain:web3 impact:funds-loss kind:contest platform:code4rena sev:high tech:evm tech:solidity technique:source-review vuln:accounting-error vuln:reward-distribution

**Target:** MultiRewardStaking.changeRewardSpeed reward accounting
**Root cause:** changeRewardSpeed derives the new rewardsEndTimestamp from the contract's reward-token balance, which includes tokens already accrued/owed to users, rather than the undistributed remainder.
**Steps:**
1. Deploy MultiRewardStaking and add reward token with rewardsPerSecond=0.1e18, amount=10e18, duration 100s.
2. Alice deposits 1e18, warp 50s, withdraws; accruedRewards(alice)=5e18 (half paid).
3. Call `changeRewardSpeed(token, 0.2e18)`; new end time computed from full 10e18 balance, not remaining 5e18.
4. Bob deposits 1e18, warp 200s, withdraws; accruedRewards(bob)=20e18 > contract balance.
**How found:** Code review of changeRewardSpeed's end-time calculation; noticed balance != undistributed rewards, then wrote a Foundry test to confirm double accrual.
**Exploitation:** Attacker stakes after a reward-speed change; because end time is stretched using already-owed tokens, they accrue more rewards than the contract holds, leaving later claimers unpaid and extracting excess value.
**Insight:** When a contract recomputes emission schedules, check whether it uses token balance (which includes accrued-but-unclaimed liabilities) instead of the remaining undistributed amount; test by changing speed mid-period with a partial withdrawal.
