# stake() missing set lastEpochClaimed when userBalance equal 0

Bond OTLM stake() skipped initializing lastEpochClaimed for first-time stakers, forcing _claimRewards to loop from epoch 0 and eventually run out of gas.

- Payout: Contest reward
- Severity: medium
- Program: Bond
- Credited: Delvir0
- Disclosed: 2023-06-26
- Write-up quality: 6/10
- Original: https://github.com/sherlock-audit/2023-06-bond-judging/issues/108
- Library page: https://bugbountylibrary.com/r/sherlock%3A2023-06-bond%3AM-9
- Tags: domain:web3 impact:dos kind:contest platform:sherlock sev:medium tech:evm tech:solidity technique:source-review vuln:dos

**Target:** OTLM.sol fixed-strike liquidity mining staking contract
**Root cause:** In stake(), the first-time-staker branch only sets rewardsPerTokenClaimed[msg.sender] and never sets lastEpochClaimed[msg.sender], leaving it at 0. _claimRewards() then iterates from epoch 0 to the current epoch.
**Steps:**
1. Deploy OTLM and let epochs advance to a large number N.
2. New user calls `stake(amount_, proof_)` with stakeBalance[msg.sender]==0.
3. stake() sets only `rewardsPerTokenClaimed[msg.sender] = rewardsPerTokenStored`, leaving `lastEpochClaimed[msg.sender] == 0`.
4. On subsequent stake/claim, `_claimRewards()` runs `for (uint48 i = userLastEpoch; i <= epoch; i++)` starting at 0, looping N+1 times.
5. As N grows, gas cost exceeds block gas limit and stake() reverts with out-of-gas.
**How found:** Code review of stake()'s first-time-staker branch noticed lastEpochClaimed was not initialized, then traced _claimRewards()'s loop starting at userLastEpoch=0.
**Insight:** When auditing reward-accounting contracts, check that every per-user checkpoint variable (lastEpochClaimed, rewardDebt, etc.) is initialized on first deposit; an unset checkpoint turns an O(1) claim into an O(epochs) loop that can be gas-griefed as epochs accumulate.
