# Staking rewards can be drained

MultiRewardStaking.claimRewards clears accruedRewards after transferring ERC777 reward tokens, letting a recipient hook reenter and drain the full reward balance.

- Payout: Contest reward
- Severity: high
- Program: Popcorn
- Credited: 0xdeadbeef0x
- Disclosed: 2023-09-07
- Write-up quality: 7/10
- Original: https://github.com/code-423n4/2023-01-popcorn-findings/issues/402
- Library page: https://bugbountylibrary.com/r/c4%3A2023-01-popcorn%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:reentrancy

**Target:** MultiRewardStaking.claimRewards reward distribution contract
**Root cause:** claimRewards transfers reward tokens before zeroing accruedRewards[user][token], violating checks-effects-interactions; ERC777 tokensReceived hook reenters while accrued balance is still set.
**Steps:**
1. Deploy attacker contract, register ERC777TokensRecipient via setInterfaceImplementer on the ERC1820 registry
2. Deposit 1 ether staking token via staking.deposit
3. Add ERC777 reward token with rewards funded in staking contract
4. Warp time so rewards accrue
5. Call staking.claimRewards(attacker, rewardTokens); in tokensReceived hook, if staking reward balance > 0, call claimRewards again recursively
6. Repeat until staking reward token balance is drained; assert attacker received full rewards
**How found:** Code review of claimRewards noticed accruedRewards cleared after the transfer loop, and that ERC777 reward tokens invoke a recipient hook on transfer, enabling reentrancy.
**Exploitation:** Attacker deposits minimal stake, waits for accrual, then calls claimRewards. The ERC777 transfer to the attacker triggers tokensReceived, which reenters claimRewards before accruedRewards is zeroed, repeating until the staking contract's entire reward token balance is transferred to the attacker.
**Insight:** When a staking/reward contract supports arbitrary ERC20 reward tokens, check whether ERC777 (or other hook-bearing tokens) can be added; any state cleared after an outbound transfer becomes reentrancy-exploitable via tokensReceived.
