# _claimRewardsOnBehalf() User's rewards may be lost

StaticATokenLM._claimRewardsOnBehalf with forceUpdate=false caps payout at contract balance but zeroes _unclaimedRewards, silently destroying user rewards held in the incentives controller.

- Payout: Contest reward
- Severity: medium
- Program: Reserve
- Credited: bin2chen
- Disclosed: 2023-11-13
- Write-up quality: 7/10
- Original: https://github.com/code-423n4/2023-07-reserve-findings/issues/10
- Library page: https://bugbountylibrary.com/r/c4%3A2023-07-reserve%3AM-12
- Tags: domain:web3 impact:funds-loss kind:contest platform:code4rena sev:medium tech:evm tech:solidity technique:source-review vuln:accounting-error vuln:business-logic

**Target:** StaticATokenLM wrapper contract reward-claiming function
**Root cause:** _claimRewardsOnBehalf caps reward to REWARD_TOKEN.balanceOf(address(this)) when forceUpdate=false, but then unconditionally sets _unclaimedRewards[onBehalfOf]=0, discarding the portion of accrued rewards not yet collected from INCENTIVES_CONTROLLER.
**Steps:**
1. Deposit into StaticATokenLM and advance time so rewards accrue.
2. Call `collectAndUpdateRewards()` to leave a small REWARD_TOKEN balance in the contract.
3. Advance time again so pending rewards exceed contract balance.
4. Call `claimRewardsToSelf(false)` (forceUpdate=false) so no fresh collection occurs.
5. Observe reward capped to totBal, `_unclaimedRewards[user]` zeroed, and `getClaimableRewards` drops by the uncollected remainder (test shows ~1.49e15 lost).
**How found:** Code reading of _claimRewardsOnBehalf noticed the mismatch: _updateRewards/_updateUser only accrue accounting without moving REWARD_TOKEN from INCENTIVES_CONTROLLER, so _unclaimedRewards can exceed contract balance; the cap branch then zeroes the ledger entry. Confirmed with a Hardhat test printing pendingRewardsBefore/After and 'user lost'.
**Exploitation:** User (or anyone claiming on behalf with forceUpdate=false) triggers the capped payout path; the excess accrued rewards are wiped from _unclaimedRewards and never transferred, effectively burned. No attacker profit; loss borne by the claiming user.
**Insight:** When a claim function caps payout to on-hand balance, check whether the ledger is decremented by the paid amount or zeroed entirely — accrued-but-uncollected rewards tracked separately from the token balance are the classic loss vector.
