# User can steal all rewards due to checkpoint after transfer

StakerVault.transfer() checkpoints rewards after balance update, letting users cycle staked tokens across fresh accounts to repeatedly claim full rewards and drain the pool.

- Payout: Audit contest
- Severity: high
- Program: Backd
- Credited: 0xDjango
- Disclosed: 2022-06-02
- Write-up quality: 7/10
- Original: https://github.com/code-423n4/2022-04-backd-findings/issues/36
- Library page: https://bugbountylibrary.com/r/c4%3A2022-04-backd%3AH-01
- Tags: domain:web3 impact:funds-loss kind:contest platform:code4rena sev:high tech:solidity technique:source-review vuln:accounting-error vuln:reward-distribution

**Target:** StakerVault.sol / LpGauge reward accounting
**Root cause:** transfer() updates balances before calling userCheckpoint(), so the recipient's perUserShare is computed on the new balance against a stale perUserStakedIntegral of 0, minting rewards for the full period.
**Steps:**
1. Account A stakes 1000 LP via stakeFor(); userCheckpoint sets perUserShare[A] correctly.
2. A transfers all 1000 to fresh Account B via transfer().
3. transfer() updates balances[B] first, then userCheckpoint computes perUserShare[B] += 1000 * (poolStakedIntegral - 0) = 1000.
4. B calls claimRewards() to mint 1000 reward tokens.
5. B transfers the 1000 staked tokens to fresh Account C, repeating the same miscalculation.
6. Repeat across fresh accounts until reward tokens are drained.
**How found:** Reviewer noticed every actionable function except transfer() calls userCheckpoint() before state effects; transfer() calls it after balance updates, breaking the checkpoint-before-effect invariant.
**Exploitation:** Attacker stakes once, then cycles the staked balance through a chain of fresh accounts; each transfer checkpoints the recipient with perUserStakedIntegral=0, granting full-since-inception rewards claimable via claimRewards(), draining the reward pool.
**Escalation:** Repeated across many fresh accounts to drain all reward tokens rather than a single claim.
**Insight:** When auditing reward/checkpoint accounting, verify the checkpoint-before-mutation invariant holds in every state-changing path including transfer/mint/burn; a single function ordering it after balance updates lets recipients inherit unearned accrual.
