# Incorrect loop indexing in LibAddressStakingReleases.compact() leads to unprocessed pending releases

LibAddressStakingReleases.compact() compares absolute startIdx against relative pending count, so once processed releases exceed pending ones the loop never runs and claimable validator collateral stays locked.

- Payout: Contest reward
- Severity: high
- Program: Recall
- Credited: 0xAsen
- Disclosed: 2026-03-13
- Write-up quality: 7/10
- Original: https://code4rena.com/reports/2025-02-recall#h-07
- Library page: https://bugbountylibrary.com/r/c4%3A2025-02-recall%3AH-07
- Tags: domain:web3 impact:dos impact:funds-loss kind:contest platform:code4rena sev:high tech:solidity technique:source-review vuln:accounting-error vuln:business-logic

**Target:** LibAddressStakingReleases.compact() staking release queue in validator collateral claim flow
**Root cause:** The loop condition `while (i < length)` uses `i = self.startIdx`, an absolute monotonically increasing index, while `self.length` is the count of pending releases. Once startIdx exceeds length the loop body never executes.
**Steps:**
1. Queue 115 releases; 100 become claimable at current block.
2. Call compact(): i=startIdx=0, newLength=115; loop processes 100, sets startIdx=100, length=15.
3. Time passes so remaining 15 releases are claimable.
4. Call compact() again: i=100, condition `100 < 15` fails immediately, loop body never runs.
5. Releases at absolute indices 100-114 are never processed; collateral remains locked.
**How found:** Code reading of compact(): noticed `i` initialized to absolute self.startIdx but compared against self.length which tracks pending count, then traced a two-call scenario where startIdx (100) exceeds length (15).
**Exploitation:** Not an attacker-driven exploit; the flaw triggers naturally as startIdx grows monotonically, eventually making startIdx > length inevitable and permanently blocking collateral claims for validators.
**Insight:** When a storage struct mixes an absolute cursor (startIdx) with a relative count (length), audit every loop bound for unit mismatch; construct a two-epoch scenario where the cursor has advanced past the remaining count to expose the dead loop.
