# Repeated calls to multiStakerClaim in the same block leads to loss of funds

RewardDistributor checks claims per token ID, so merging a lock into a new ID within the epoch-change block lets an attacker re-claim the same epoch rewards and drain WETH.

- Payout: Contest reward
- Severity: high
- Program: Golom
- Credited: Krow10
- Disclosed: 2023-04-05
- Write-up quality: 8/10
- Original: https://github.com/code-423n4/2022-07-golom-findings/issues/139
- Library page: https://bugbountylibrary.com/r/c4%3A2022-07-golom%3AH-09
- Tags: domain:web3 impact:funds-loss kind:contest platform:code4rena sev:high tech:evm tech:solidity technique:race-window vuln:accounting-error vuln:business-logic vuln:reward-distribution

**Target:** RewardDistributor.multiStakerClaim / VoteEscrowCore merge
**Root cause:** Claim deduplication is keyed on token ID, but lock ownership/balance is mutable via merge; rewards are computed from live VE balances at epochBeginTime rather than a per-epoch checkpoint, so a new token ID inherits the same rewardable balance.
**Steps:**
1. Create lock #1 with `voteEscrow.create_lock(amount, 1 week)` before the target epoch.
2. Wait for an `addFee` tx that triggers the epoch change; target that block via Flashbots bundle.
3. In the epoch-change block call `rewardDistributor.multiStakerClaim([lockId], [0])` to claim epoch 0 rewards for lock #1.
4. Create lock #2 with `voteEscrow.create_lock(1, 1 week)` (can be pre-created).
5. Call `voteEscrow.merge(lockId, lockId + 1)` to move lock #1's tokens into lock #2.
6. Call `rewardDistributor.multiStakerClaim([lockId + 1], [0])` to claim the same epoch rewards again for the new token ID.
7. Repeat merge/claim within the same block to drain WETH.
**How found:** Code review of multiStakerClaim's require on line 185: noticed the claim guard is per token ID while reward is computed from live VE balance at epochBeginTime; realized merge creates a new token ID carrying the same balance, so same-block re-claim bypasses the guard.
**Exploitation:** Attacker holds locked GOLOM, monitors mempool for the epoch-changing addFee tx, and submits a Flashbots bundle included in that same block: claim for lock #1, merge into a fresh lock #2, claim again for lock #2, repeating to extract the epoch reward multiple times until RewardDistributor's WETH is drained. Only needs to land once; frequent epoch changes make eventual inclusion likely.
**Escalation:** Looping merge+claim within a single block multiplies the payout per epoch, converting a single reward into full WETH balance drain.
**Insight:** When a claim/reward guard is keyed on a mutable identifier (token ID, NFT, position) rather than the underlying balance snapshot, look for any operation that mints a new identifier carrying the same balance (merge, split, transfer, wrap) and check whether it can be done inside the same block as the epoch/checkpoint update.
