# streamedvesting.claimvestearlywithpenalty() charges much more penalties than required

StreamedVesting applies early-claim penalty to the entire unclaimed amount instead of only the non-vested portion, overcharging users who skip claimVest first.

- Payout: Contest reward
- Severity: medium
- Program: Zerolend
- Credited: osmanozdemir1
- Disclosed: 2024-09-16
- Write-up quality: 6/10
- Original: https://cdn.cantina.xyz/reports/cantina_competition_zerolend_jan2024.pdf#3.2.9
- Library page: https://bugbountylibrary.com/r/cantina%3Azerolend_jan2024%3A3.2.9
- Tags: domain:web3 impact:funds-loss kind:contest platform:cantina sev:medium tech:solidity technique:source-review vuln:business-logic vuln:fee-bypass

**Target:** StreamedVesting.sol claimVestEarlyWithPenalty
**Root cause:** claimVestEarlyWithPenalty computes pendingAmt as vest.amount - vest.claimed (the full unclaimed balance) and applies penaltyPct to all of it, ignoring the portion already vested penalty-free via _claimable.
**Steps:**
1. Create two vests of 100e18 each for owner and otherAccount
2. Fast forward 60 days (86400*60)
3. otherAccount calls `claimVest(1)` then `claimVestEarlyWithPenalty(1)`
4. owner calls only `claimVestEarlyWithPenalty(2)`
5. Observe otherAccountDelta > ownerDelta despite identical vest terms
**How found:** Code reading of claimVestEarlyWithPenalty noticed pendingAmt caches the whole unclaimed amount and penaltyPct is applied to it, then compared behavior against claimVest+claimVestEarlyWithPenalty path to confirm inconsistency.
**Exploitation:** A user who claims early without first calling claimVest pays penalty on the full unclaimed amount, losing the vested 2/3 portion's worth of penalty to the dead address; no attacker gain, user self-harm via inconsistent API.
**Insight:** When a contract offers both partial penalty-free claims and full early claims, check whether the early path recomputes the penalty-free vested portion or naively penalizes the entire unclaimed balance.
