# Wrong reward distribution in Bribe because deliverReward() won't set tokenRewardsPerEpoch[token][epochStart] to 0 

Bribe.deliverReward() never zeroes tokenRewardsPerEpoch after transfer, so repeated Voter.distribute() calls in one epoch re-send the same bribe rewards.

- Payout: Contest reward
- Severity: medium
- Program: Velodrome Finance
- Credited: unforgiven
- Disclosed: 2022-08-08
- Write-up quality: 6/10
- Original: https://github.com/code-423n4/2022-05-velodrome-findings/issues/141
- Library page: https://bugbountylibrary.com/r/c4%3A2022-05-velodrome%3AM-14
- Tags: domain:web3 impact:funds-loss kind:contest platform:code4rena sev:medium tech:evm tech:solidity technique:source-review vuln:accounting-error vuln:reward-distribution

**Target:** Bribe.sol / Voter.distribute bribe delivery
**Root cause:** deliverReward() transfers tokenRewardsPerEpoch[token][epochStart] but never resets it to 0, so the stored amount persists and is re-transferred on every call.
**Steps:**
1. Observe Bribe.deliverReward() reads tokenRewardsPerEpoch[token][epochStart] and _safeTransfer's it without zeroing.
2. Note Gauge.deliverBribes() loops rewards calling deliverReward, invoked by Voter.distribute() when claimable[_gauge]/DURATION > 0.
3. Anyone calls Voter.notifyRewardAmount() with amount >= totalWeight/1e18 to bump index, increasing claimable[_gauge] via _updateFor.
4. Call Voter.distribute(gauge) again within the same epoch so claimable/DURATION > 0, triggering deliverBribes() and a second deliverReward() transfer of the same epoch rewards.
**How found:** Code reading of deliverReward() revealed the missing state reset; traced the call path Gauge.getReward -> Voter.distribute -> Gauge.deliverBribes -> Bribe.deliverReward and reasoned claimable can exceed DURATION multiple times per epoch because index grows on any notifyRewardAmount.
**Exploitation:** Attacker repeatedly triggers Voter.distribute() within one epoch (cheaply via notifyRewardAmount to inflate index/claimable), causing Gauge.deliverBribes() to re-invoke Bribe.deliverReward() and drain bribe tokens earmarked for other epochs, misallocating rewards.
**Insight:** When auditing reward/claim functions, check whether the claimed amount mapping is zeroed after transfer; any permissionless function that bumps an index feeding claimable can re-trigger the claim path within the same period.
