# Removedamount will remain stuck forever in the contract if totalvesting becomes 0

Early claimer's removedAmount stays stuck in EscrowedToken when totalVesting hits zero, since ratePerToken is never incremented and funds are untracked.

- Payout: Contest reward
- Severity: medium
- Program: Opal
- Credited: 0xadrii
- Disclosed: 2024-03-30
- Write-up quality: 6/10
- Original: https://cdn.cantina.xyz/reports/cantina_competition_opal_feb2024.pdf#3.3.23
- Library page: https://bugbountylibrary.com/r/cantina%3Aopal_feb2024%3A3.3.23
- Tags: domain:web3 impact:funds-loss kind:contest platform:cantina sev:medium tech:solidity technique:source-review vuln:accounting-error vuln:business-logic vuln:precision-loss

**Target:** EscrowedToken.sol vesting/claim contract
**Root cause:** When totalVesting becomes 0, the ratePerToken increment is skipped to avoid division by zero, and removedAmount is never tracked or recoverable, leaving tokens stranded.
**Steps:**
1. Deploy EscrowedToken and create a vesting of 1 ether for alice with startTimestamp 50
2. Warp block.timestamp to 60 (early in vesting)
3. Call `escrowedToken.claim(0)` as alice
4. Observe `ratePerToken()` still equals SCALED_ONE and `token.balanceOf(address(escrowedToken)) > 0`
5. Funds remain permanently stuck with no recovery path
**How found:** Code review of _claim(): noticed the `if (totalVesting > 0)` guard around the ratePerToken update and reasoned that removedAmount is silently dropped when no vesting remains.
**Exploitation:** A user claims early while being the only/last vesting user; totalVesting drops to 0, removedAmount is neither redistributed nor tracked, so the proportional unclaimed tokens are locked in the contract forever.
**Insight:** When a division-by-zero guard skips an accounting update, check whether the skipped value is tracked elsewhere; unaccounted remainders become permanently locked funds.
