# User's Accrued Rewards Will Be Lost

PirexRewards.claim truncates reward payout to zero for small stakers due to integer division, then zeroes their accrued rewards, losing funds.

- 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/184
- Library page: https://bugbountylibrary.com/r/c4%3A2022-11-redactedcartel%3AH-04
- Tags: domain:web3 impact:funds-loss kind:contest platform:code4rena sev:high tech:evm tech:solidity technique:source-review vuln:accounting-error vuln:precision-loss vuln:reward-distribution

**Target:** PirexRewards.sol claim() reward accounting
**Root cause:** Reward amount computed as (rewardState * userRewards) / globalRewards truncates to zero when userRewards is tiny relative to globalRewards; user state is zeroed regardless of whether amount was paid.
**Steps:**
1. Alice stakes 1 GMX, Bob stakes 99999 GMX for same duration; emission 0.1 esGMX/GMX/sec.
2. At claim time: rewardState=60000, globalRewards=600000, Alice userRewards=6.
3. Call `PirexRewards.claim(pxGMX, alice)`; amount = 60000*6/600000 = 0.
4. Line 391 sets `p.userStates[alice].rewards = 0` even though amount==0, so Alice loses accrued 1.2 esGMX.
**How found:** Code reading of claim() reward math; noticed integer division truncation combined with unconditional state reset, then constructed a two-user numeric example to demonstrate zero payout.
**Exploitation:** Permissionless claim lets an attacker call claim(victim) at a moment when victim's accrued reward rounds to zero, zeroing their rewards; leftover tokens remain in contract and are captured by other stakers.
**Insight:** In reward-accounting contracts, check whether the user's accrued state is cleared before verifying a nonzero payout; integer division of (rewardState*userRewards)/globalRewards silently truncates for dust stakers, and permissionless claim lets anyone trigger the loss.
