# It is impossible to claim rewards in zlrewardscontroller.sol

ZLRewardsController.claim() always reverts because it never approves StreamedVesting to burn vested tokens, permanently breaking reward claims.

- Payout: Contest reward
- Severity: high
- 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.1.1
- Library page: https://bugbountylibrary.com/r/cantina%3Azerolend_jan2024%3A3.1.1
- Tags: domain:web3 impact:dos kind:contest platform:cantina sev:high tech:evm tech:solidity technique:source-review vuln:access-control vuln:business-logic vuln:reward-distribution

**Target:** ZLRewardsController.claim / StreamedVesting.createVestFor
**Root cause:** claim() calls streamedVesting.createVestFor, which invokes vestedToken.burnFrom(ZLRewardsController, amount); the controller never approved StreamedVesting as spender, so _spendAllowance reverts.
**Steps:**
1. Deploy core fixture; transfer vestedToken to ZLRewardsController.
2. addPool, registerRewardDeposit, start; register user via afterLockUpdate.
3. Advance time so pendingReward > 0.
4. Call `zLRewardsController.claimAll(user)` -> reverts with `ERC20: insufficient allowance`.
**How found:** Code reading of claim() -> _vestTokens -> createVestFor -> _createVest -> burnFrom, noticing the missing approve() that other contracts (StreamedVesting, BonusPool) perform during initialization.
**Insight:** When a contract calls burnFrom/transferFrom on itself as `from`, verify it granted allowance to the caller; compare against sibling contracts' init-time approvals to spot the missing one.
