# Allowing merge of expired locks may prevent reward claiming and break some invariants

ZeroLocker.merge allows merging an expired lock into a new one, reactivating it across a zero-veSupply period, causing division-by-zero revert in FeeDistributor.claim and locking rewards.

- Payout: Contest reward
- Severity: medium
- Program: Zerolend
- Credited: elhaj
- Disclosed: 2024-09-16
- Write-up quality: 6/10
- Original: https://cdn.cantina.xyz/reports/cantina_competition_zerolend_jan2024.pdf#3.2.15
- Library page: https://bugbountylibrary.com/r/cantina%3Azerolend_jan2024%3A3.2.15
- Tags: domain:web3 impact:dos impact:funds-loss kind:contest platform:cantina sev:medium tech:evm tech:solidity technique:source-review vuln:business-logic vuln:reward-distribution

**Target:** ZeroLocker / FeeDistributor reward accounting
**Root cause:** merge() does not reject expired tokenIds and overwrites locked[_to].end with the newer lock's end, reactivating an expired lock whose unclaimed epochs span a period where veSupply was zero.
**Steps:**
1. Bob creates lock tokenId1 for 3 weeks with 10 ether.
2. Skip 5 weeks with weekly emissions.distribute(); lock expires with unclaimed rewards.
3. Bob creates tokenId2 for 10 weeks.
4. Call `locker.merge(tokenId2, tokenId1)` — expired _to gets end extended.
5. Call `feeDistributor.claim(tokenId1)` — reverts because veSupply was 0 during weeks 4-5, causing divide-by-zero in reward math.
**How found:** Code review of merge() noticing it only takes max of end times without checking expiry, then tracing FeeDistributor._checkpointTotalSupply where veSupply clamps to zero when no locks exist, and reasoning that claim math divides by that zero.
**Exploitation:** User merges a fresh lock into their own expired lock, extending its end; subsequent claim walks epochs where veSupply was zero and reverts, permanently blocking accumulated rewards for that tokenId.
**Insight:** When a staking/lock contract lets you extend an expired position, check whether reward-accounting checkpoints recorded zero total supply in the gap — any per-epoch division by total supply will revert and permanently strand rewards.
