# Malicious users can steal reward tokens via re-entrancy attack

Reentrancy via reward-token callback during vault redemption lets attacker re-claim rewards against stale vaultSharesBefore, stealing ~180 extra reward tokens.

- Payout: Contest reward
- Severity: high
- Program: Leveraged Vaults
- Credited: xiaoming90
- Disclosed: 2024-06-18
- Write-up quality: 7/10
- Original: https://github.com/sherlock-audit/2024-06-leveraged-vaults-judging/issues/64
- Library page: https://bugbountylibrary.com/r/sherlock%3A2024-06-leveraged-vaults%3AH-8
- Tags: domain:web3 impact:funds-loss kind:contest platform:sherlock sev:high tech:evm tech:solidity technique:chained technique:source-review vuln:business-logic vuln:reentrancy vuln:reward-distribution

**Target:** SingleSidedLPVaultBase / VaultRewarderLib reward accounting
**Root cause:** claimAccountRewards lacks a reentrancy guard and reads vaultSharesBefore from Notional storage that is only updated after _redeemFromNotional completes; a reward-token callback re-enters before debt/shares are settled.
**Steps:**
1. Bob holds 100 vault shares, rewardsPerVaultShare=1.0, rewardDebt=100.
2. rewardsPerVaultShare rises to 2.0; Bob calls redeem of 90 shares.
3. _redeemFromNotional calls _updateAccountRewards -> _claimAccountRewards -> _claimRewardToken; rewardToClaim=(100*2.0)-100=100, debt set to 10*2.0=20, transfer of 100 reward tokens to Bob.
4. Reward token has a hook (e.g. ERC777) that re-enters `claimAccountRewards(Bob)`; Notional storage still shows 100 shares.
5. Second claim: rewardToClaim=(100*2.0)-20=180, another 180 tokens transferred.
6. Bob ends with 280 reward tokens instead of 100.
**How found:** Code review of reward accounting flow: noticed _updateAccountRewards runs at end of _redeemFromNotional while Notional updates vault account shares only after the vault returns, and claimAccountRewards has no nonReentrant guard; hypothesized a callback-bearing reward token could re-enter with stale share balance.
**Exploitation:** Attacker uses a reward token with transfer callback (ERC777-style hook) and re-enters claimAccountRewards during the reward transfer inside redemption, exploiting stale vaultSharesBefore to double-claim rewards from the vault and other shareholders.
**Escalation:** Instance 2: _initiateWithdraw reads stale Notional vaultShares, allowing inflated withdraw requests during reentrancy.
**Insight:** When a vault's reward/accounting update depends on external protocol state (Notional vaultShares) that is only committed after the vault callback returns, any token transfer inside that window is a reentrancy surface — check reward-claim paths for missing nonReentrant even when the asset tokens are trusted.
