# Calling unstake() can cause locked funds

Unstaking vested tokens in Infinity Staker zeroes out a separate unvested stake tranche, permanently locking those funds in the contract.

- Payout: Audit contest
- Severity: high
- Program: Infinity NFT Marketplace
- Found by: Ruhum
- Disclosed: 2022-08-16
- Write-up quality: 7/10
- Original: https://github.com/code-423n4/2022-06-infinity-findings/issues/50
- Library page: https://bugbountylibrary.com/r/c4%3A2022-06-infinity%3AH-09
- Tags: area:web3 domain:web3 impact:funds-loss kind:contest platform:code4rena sev:high tech:solidity technique:source-review vuln:accounting-error vuln:business-logic

**Target:** InfinityStaker staking contract (unstake/_updateUserStakedAmounts)
**Root cause:** _updateUserStakedAmounts subtracts from each duration bucket sequentially and sets a bucket's amount to 0 whenever amount exceeds that bucket's vested total, even when the bucket's vested amount is 0, wiping out unvested stakes.
**Steps:**
1. Stake amountStaked with duration 2 (six months) via `infinityStaker.stake(amountStaked, 2)`
2. Advance time 181 days so the six-month stake vests
3. Stake amountStaked2 with duration 1 (three months) via `infinityStaker.stake(amountStaked2, 1)`
4. Call `infinityStaker.unstake(amountStaked)`
5. Observe getUserTotalStaked and getUserTotalVested both return 0 while amountStaked2 remains locked in the contract
**How found:** Reading the sequential bucket-subtraction logic in _updateUserStakedAmounts and noticing that when amount > noVesting but vestedThreeMonths == 0, the code still zeroes the THREE_MONTHS bucket and subtracts 0, then falls into the SIX_MONTHS branch and zeroes that too.
**Exploitation:** Any user with a vested long-duration stake who adds a shorter-duration stake and then unstakes the vested amount triggers the bug; their newer unvested tokens are zeroed from accounting and stuck in the contract with no withdrawal path.
**Insight:** In multi-tranche staking accounting, check the branch where a bucket's vested amount is zero but the loop still clears its stored amount; sequential subtraction loops that don't guard on `vestedAmount == 0` silently destroy unvested balances.

