If a user extends their lock time to the maximum amount using votingEscrow:updateLockTime and sets maxLockEnabled to true, the RevenueHandler:claim function will fail when the user attempts to claim their rewards.
Vulnerability Details
The RevenueHandler:claim function allows users to earn rewards every epoch for maintaining a veALCX position. Users can call this function at any time after locking BPT. The function calculates and distributes all the epoch rewards since the last claim to the user.
In line L322, the VotingEscrow:balanceOfTokenAt function checks for the user's underlying voting power/BPT. If the user has opted for the maximum lock, the balanceOfTokenAt function will return the maximum balance.
Let's say a user initially sets a lock end time of 90 days. After 2 epochs (4 weeks), they decide to extend the lock time to the maximum (1 year) by calling updateLockTime with maxLockEnabled set to true.
If they then try to claim the rewards for the first 2 epochs from the revenue handler, the _claimable function will behave as if they always had the maximum lock enabled. It will not account for the initial 90-day lock covering the first 2 epochs. The attempt to distribute rewards will fail because the contract will not have enough funds to distribute.
Impact Details
Users will lose rewards. This is a loss of unclaimed yield for user which is considered as high impact.
We can see what are the steps taken here to reach the revert state.
functiontestChangeLockTimeAfterTwoEpochAndClaimAll() public {//Lock BPT for 3 monthsuint256 tokenId =createVeAlcx(admin, TOKEN_1,3*30days,false);uint256 revAmt =1000e18;_accrueRevenueAndJumpOneEpoch(revAmt); // Deposit 1000 DAI hevm.startPrank(admin); veALCX.updateUnlockTime(tokenId, MAXTIME,true); // Enable max lock so we get max rewards from now onwards hevm.stopPrank();_accrueRevenueAndJumpOneEpoch(revAmt); // Deposit 1000 DAI hevm.startPrank(admin);expectError("Not enough revenue to claim"); revenueHandler.claim(tokenId, alusd,address(alusdAlchemist), revenueHandler.claimable(tokenId, alusd), admin); hevm.stopPrank(); }
Output
Ran1testforsrc/test/RevenueHandler.t.sol:RevenueHandlerTest[FAIL. Reason: revert: Not enough revenue to claim] testChangeLockTimeAfterTwoEpochAndClaimAll() (gas:3287974)Suiteresult:FAILED.0passed; 1failed; 0skipped; finishedin145.80s (113.26s CPUtime)Ran1testsuitein148.30s (145.80s CPUtime): 0 tests passed, 1 failed, 0 skipped (1totaltests)Failingtests:Encountered1failingtestinsrc/test/RevenueHandler.t.sol:RevenueHandlerTest[FAIL. Reason: revert: Not enough revenue to claim] testChangeLockTimeAfterTwoEpochAndClaimAll() (gas:3287974)