# Users Receive Less Rewards Due To Miscalculations

PirexRewards accrues rewards as balance×time, ignoring dynamic emission rates, so users claiming after rate changes receive wrong (less or more) reward tokens.

- Payout: Contest reward
- Severity: high
- Program: Redacted Cartel
- Credited: xiaoming90
- Disclosed: 2023-01-27
- Write-up quality: 7/10
- Original: https://github.com/code-423n4/2022-11-redactedcartel-findings/issues/177
- Library page: https://bugbountylibrary.com/r/c4%3A2022-11-redactedcartel%3AH-02
- 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:** PirexRewards.sol reward accrual and claim logic
**Root cause:** Rewards are accrued as lastBalance × elapsed time and claim pays rewardState × userRewards / globalRewards, which assumes a constant emission rate; when the emission rate changes, globalRewards and rewardState diverge from actual entitlements.
**Steps:**
1. Observe _globalAccrue and userAccrue compute rewards = rewards + (block.timestamp - lastUpdate) * lastSupply/balance, ignoring emission rate changes.
2. Set emission 2 esGMX/GMX/sec from T80-T82 and 1 esGMX/GMX/sec from T83-T84.
3. Alice stakes 1 GMX T80-T84 (entitled 8), Bob stakes 4 GMX T83-T84 (entitled 8).
4. Harvest yields rewardState=16, but globalRewards=13, Alice userRewards=5, Bob userRewards=8.
5. Alice claims: (16*5)/13 = 6 (loses 2); Bob claims: (16*8)/13 = 9 (gains 1).
**How found:** Code review of the accrual math: noticed rewards are computed as balance×elapsed-time with no per-token reward-per-share accumulator, then reasoned that dynamic GMX esGMX emission rates break the proportionality between globalRewards and rewardState.
**Exploitation:** Any user staking across an emission-rate change can be under- or over-paid at claim time; a staker positioned like Bob (staking only during low-rate periods) extracts more than earned at others' expense.
**Insight:** When auditing staking/reward contracts, check whether accrual assumes constant emission; if claim uses rewardState×userRewards/globalRewards, any rate change between accruals desynchronizes the numerator and denominator, mispricing claims.
