# Epoch-boundary checkpoints retroactively qualify for the previous epoch’s rewards

Epoch-end snapshot uses inclusive timestamp lookup, so a lock created at the first second of epoch N+1 retroactively earns epoch N emissions.

- Payout: Contest reward
- Severity: medium
- Program: Intuition
- Credited: 0xastronatey
- Disclosed: 2026-04-27
- Write-up quality: 7/10
- Original: https://code4rena.com/reports/2026-03-intuition#m-02
- Library page: https://bugbountylibrary.com/r/c4%3A2026-03-intuition%3AM-02
- Tags: domain:web3 impact:funds-loss kind:contest platform:code4rena sev:medium tech:evm tech:solidity technique:incomplete-fix technique:source-review vuln:accounting-error vuln:business-logic vuln:reward-distribution

**Target:** CoreEmissionsController / TrustBonding / VotingEscrow reward snapshot logic
**Root cause:** epochTimestampEnd(n) equals the first timestamp of epoch n+1, and VotingEscrow's historical lookup is inclusive (ts <= t), so checkpoints written exactly at the boundary are counted in epoch n's reward snapshot.
**Steps:**
1. Wait until block.timestamp == epochTimestampEnd(prevEpoch); currentEpoch() already returns prevEpoch+1.
2. In that same second call `create_lock`, `increase_amount`, or `increase_unlock_time` on VotingEscrow, writing a checkpoint with ts == epochTimestampEnd(prevEpoch).
3. Call `claimRewards()`; it targets prevEpoch and `_userEligibleRewardsForEpoch` reads `_balanceOf`/`_totalSupply` at that boundary timestamp.
4. Inclusive lookup selects the new checkpoint, so attacker is credited for prevEpoch despite not being bonded during it.
5. Repeat each cycle to dilute honest participants or capture full emissions if sole bonder.
**How found:** Code reading of epoch arithmetic: noticed epochTimestampEnd(n) returns the first moment of n+1, then cross-checked VotingEscrow's inclusive binary-search lookup and TrustBonding's boundary snapshot usage; distinguished from prior underflow fix which required ts > t.
**Exploitation:** Attacker times a lock mutation to the exact epoch boundary second, then immediately claims the previous epoch's rewards; capital is only locked, not spent, so cost is timing plus temporary lock.
**Escalation:** Generalizes to every epoch: any veTRUST holder can top up/extend at the boundary to retroactively inflate prior-epoch weight each cycle.
**Insight:** When a protocol snapshots balances at an 'end' timestamp, verify whether the end helper is inclusive or exclusive and whether the historical lookup uses <= or <; off-by-one at epoch boundaries lets post-epoch actions count toward prior-epoch accounting.
