# A voter lose bribe rewards if another voter voted before claim.

Magicsea BribeRewarder uses a single global _lastUpdateTimestamp not keyed by periodId, so a new-period deposit before claim wipes out prior-period accrued bribe rewards.

- Payout: Contest reward
- Severity: high
- Program: Magicsea
- Credited: 0xAsen
- Disclosed: 2024-07-15
- Write-up quality: 7/10
- Original: https://github.com/sherlock-audit/2024-06-magicsea-judging/issues/52
- Library page: https://bugbountylibrary.com/r/sherlock%3A2024-06-magicsea%3AH-2
- Tags: domain:web3 impact:funds-loss kind:contest platform:sherlock sev:high tech:evm tech:solidity technique:source-review vuln:business-logic vuln:precision-loss vuln:reward-distribution

**Target:** BribeRewarder.sol reward accounting (magicsea-staking)
**Root cause:** _lastUpdateTimestamp is a single state variable shared across all periods rather than a per-periodId mapping, so once it advances into the next period the unclaimed rewards of the previous period can no longer be computed correctly.
**Steps:**
1. Fund rewarder for periods 1-2 with `rewarder.fundAndBribe(1, 2, 10e18)`.
2. Set current period 1, start/end 0/100; at t=0 voter 1 deposits 0.2e18 via `rewarder.deposit(1, 1, 0.2e18)`.
3. At t=50 voter 2 deposits 0.2e18 via `rewarder.deposit(1, 2, 0.2e18)`.
4. At t=100 advance to period 2; at t=150 `getPendingReward(1)` = 7.5e18, `getPendingReward(2)` = 2.5e18.
5. Before claims, another voter calls `rewarder.deposit(2, 3, 0.1e18)` for period 2.
6. Re-check: `getPendingReward(1)` drops to 5e18 and `getPendingReward(2)` to 0; claims yield 5e18 and 0 instead of 7.5e18 and 2.5e18.
**How found:** Code reading of BribeRewarder reward math: noticed _lastUpdateTimestamp is not keyed by periodId, hypothesized that a period-2 deposit would overwrite the timestamp used to compute period-1 pending rewards, and confirmed with a modified forge test showing pending rewards dropping after a later-period deposit.
**Exploitation:** Any voter (or the passage of time into a new period) calling deposit for the next period before prior-period voters claim advances the shared _lastUpdateTimestamp, causing the [50,100] segment of period 1 rewards to be unclaimable; no attacker capital needed, loss is passive.
**Insight:** In reward-accrual contracts, check whether the 'last updated' timestamp/accumulator is global or per-epoch; if a single variable is shared across periods, any state-changing call in a later period can retroactively corrupt unclaimed accruals of earlier periods. Write a test that deposits in period N+1 before claiming period N.
