Lucene search

K
code423n4Code4renaCODE423N4:2023-01-POPCORN-FINDINGS-ISSUES-761
HistoryFeb 07, 2023 - 12:00 a.m.

Changing reward speed calculates wrong rewardsEndTimestamp

2023-02-0700:00:00
Code4rena
github.com
3
vulnerability
calculation
reward tokens
mitigation steps
contract balance

Lines of code

Vulnerability details

Impact

In MultiRewardStaking.changeRewardSpeed the new rewardsEndTimetamp is calculated based on the current balance of reward tokens in the contract. However, a fraction of this balance might already be accrued and accounted as reward, but just has not been claimed yet. This can cause more tokens to be claimable than the contract contains, leading to a β€œfirst come first serve” scenario for users concerning rewards.

Proof of Concept

The function MultiRewardStaking.changeRewardSpeed uses the current reward token balance of the contract as input for the calculation of the new rewardsEndTimeStamp:

uint256 remainder = rewardToken.balanceOf(address(this));

uint32 prevEndTime = rewards.rewardsEndTimestamp;
uint32 rewardsEndTimestamp = _calcRewardsEnd(
    prevEndTime > block.timestamp ? prevEndTime : block.timestamp.safeCastTo32(),
    rewardsPerSecond,
    remainder
);
rewardInfos[rewardToken].rewardsPerSecond = rewardsPerSecond;
rewardInfos[rewardToken].rewardsEndTimestamp = rewardsEndTimestamp;

The idea is that the current balance of reward tokens is what still needs to be distributed (as the name remainder implies). However, as mentioned under #Impact a fraction of this balance might already be attributed to users and is just unclaimed. Using the full balance will lead to rewardsEndTimeStamp being later than it should be, causing more tokens to be accounted for claiming than the contract holds:

function _calcRewardsEnd(
    ...
    //amount = rewardToken.balanceOf(address(this));
    return (block.timestamp + (amount / uint256(rewardsPerSecond))).safeCastTo32();
}

Tools Used

Manual Review

Recommended Mitigation Steps

Only the fraction of reward tokens that has not yet accrued should be used to calculate the rewardsEndTimestamp when changing the rewardspeed. A separate variable might be necessary to track the amount of tokens that are eligible to be claimed. That variable would need to be incremented on all _accrueStatic calls that occur when totalSupply > 0.


The text was updated successfully, but these errors were encountered:

All reactions