# Reward tokens mismanagement can cause users losing rewards

PirexRewards zeroes user reward state before transferring, so owner misconfiguration of reward token list silently drops rewards while emitting Claim.

- Payout: Contest reward
- Severity: medium
- Program: Redacted Cartel
- Credited: Jeiwan
- Disclosed: 2023-01-27
- Write-up quality: 6/10
- Original: https://github.com/code-423n4/2022-11-redactedcartel-findings/issues/271
- Library page: https://bugbountylibrary.com/r/c4%3A2022-11-redactedcartel%3AM-12
- Tags: domain:web3 impact:funds-loss kind:contest platform:code4rena sev:medium tech:solidity technique:source-review vuln:accounting-error vuln:business-logic vuln:misconfiguration

**Target:** PirexRewards/PirexGmx reward claim contracts
**Root cause:** PirexGmx hardcodes reward tokens while PirexRewards uses an owner-managed per-producer list; claim() sets user reward state to 0 before iterating the owner list, so any mismatch (removed hardcoded token, unsupported added token, empty list) skips transfers without reverting.
**Steps:**
1. Owner removes a hardcoded reward token via removeRewardToken or adds an unsupported token via addRewardToken on PirexRewards.
2. User calls claim(); harvest() pulls GMX rewards and producer.claimRewards() returns hardcoded tokens/amounts.
3. Rewards recorded into producerState.rewardStates[rewardTokens[i]] for hardcoded tokens.
4. p.userStates[user].rewards = 0 executes before any transfer.
5. Loop over owner-set p.rewardTokens finds no matching rewardState (or empty list), so claimUserReward is never called; Claim event still emitted.
**How found:** Code reading comparing the hardcoded rewardTokens array in PirexGmx.claimRewards against the owner-mutable ProducerToken.rewardTokens in PirexRewards, noticing the state-zeroing happens before the transfer loop and that no revert guards mismatches.
**Escalation:** Autocompounding contracts AutoPxGlp/AutoPxGmx hold large deposits, so a single misconfiguration can silently drop rewards for many users at once.
**Insight:** When two contracts each maintain a list of the same assets, check which list drives accounting vs which drives transfer; state resets placed before external transfers can silently swallow value if the transfer loop iterates a different list.
