# Incorrect Loop Condition in balanceOf() Includes Current Epoch's Rewards

Off-by-one loop in UnwindingModule.balanceOf credits current-epoch rewards on cancelUnwinding, inflating rewards and eventually underflowing totalShares.

- Payout: Contest reward
- Severity: high
- Program: infiniFi
- Credited: kvar
- Disclosed: 2025-05-23
- Write-up quality: 6/10
- Original: https://cdn.cantina.xyz/reports/cantina_competition_infinifi_april2025.pdf#3.1.5
- Library page: https://bugbountylibrary.com/r/cantina%3Ainfinifi_april2025%3A3.1.5
- Tags: domain:web3 impact:dos impact:funds-loss kind:contest platform:cantina sev:high tech:solidity technique:source-review vuln:business-logic vuln:precision-loss vuln:reward-distribution

**Target:** UnwindingModule.sol balanceOf()/cancelUnwinding() reward accounting
**Root cause:** balanceOf() loop uses `epoch <= currentEpoch`, so it adds rewardShares for the current epoch the user is still in; combined with totalRewardWeight decreasing per cancel, per-user reward weight inflates.
**Steps:**
1. Apply fix `totalShares -= _amountToShares(userBalance)` at L231 to expose the bug.
2. Create 9 positions of 1000e18 at epoch 10 via `_createPosition`.
3. Each user approves shareToken and calls `gateway.startUnwinding(shareTokenBalance, 10)`.
4. `advanceEpoch(1)` and `_depositRewards(3500)`.
5. Users 1-8 call `gateway.cancelUnwinding(startUnwindingTimestamp, 10)`; observe balanceOf rewards growing each cancel.
6. Adding user 9 triggers underflow on `totalShares -= _amountToShares(userBalance)`.
7. Fix: change loop to `epoch < currentEpoch`.
**How found:** Code review of balanceOf() loop bounds and cancelUnwinding() subtraction; noticed loop includes currentEpoch and that totalRewardWeight decrements while userRewardWeight stays fixed, hypothesizing reward inflation and underflow.
**Exploitation:** Any user repeatedly calling cancelUnwinding in the same epoch extracts progressively larger reward shares as totalRewardWeight shrinks, until totalShares underflows and reverts, breaking accounting.
**Insight:** When auditing reward/epoch accounting, check loop bounds for inclusive vs exclusive current-epoch handling and whether a denominator (totalRewardWeight) shrinks on each user action while the numerator stays fixed — that ratio drift is the inflation vector.
