Core Contracts

Regnum Aurum Acquisition Corp
HardhatReal World AssetsNFT
77,280 USDC
View results
Submission Details
Severity: medium
Valid

The function lastTimeRewardApplicable() will return a wrong value for the first 3000 times

Summary

The lastTimeRewardApplicable() function in BaseGauge.sol should return the latest applicable reward time but always returns periodFinish() with an incorrect value for the first 3,000 calls.

Vulnerability Details

The lastTimeRewardApplicable() function is used to calculate how many reward tokens a user should receive

function getRewardPerToken() public view returns (uint256) {
if (totalSupply() == 0) {
return rewardPerTokenStored;
}
return
rewardPerTokenStored + (((lastTimeRewardApplicable() - lastUpdateTime) * rewardRate * 1e18) / totalSupply());
}

Now lets look closer in the lastTimeRewardApplicable() ->

function lastTimeRewardApplicable() public view returns (uint256) {
return block.timestamp < periodFinish() ? block.timestamp : periodFinish();
}
function periodFinish() public view returns (uint256) {
return lastUpdateTime + getPeriodDuration();
}

it uses the PeriodFinish function with lastUpdateTime, the lastUpdateTime is only updated when a user use stake, withdraw or getReward, but it is not updated correctly

This is the path how it is updated ->

let's say the the block.timestamp = 1700000000

getPeriodDuration = 7 days

  1. A user stake and it uses LastTimeRewardApplicable() it will check if the block.timestamp < PeriodFinish(), this will return false,
    because PeriodFinish = lastUpdateTime + getPeriodDuration()
    = 0 (because this is the first time called) + 7 days
    = 7 days

    and the lastTimeRewardApplicable will return the periodFinish() = 7 days

  2. Now, the lastUpdateTime has to be updated in the _updateReward()

function _updateReward(address account) internal {
rewardPerTokenStored = getRewardPerToken();
lastUpdateTime = lastTimeRewardApplicable();
//code
}

But Because the lastTimeRewardApplicable returned 7 days, lastUpdateTime will be equal to 7 days, not to block.timestamp

Now another user wants to stake tokens, to whole proccess is repeated again

  1. Check if the block.timestamp < periodFinish() ? block.timestamp : periodFinish();

    but now the PeriodFinish will equal to

    = 7 days (lastUpdateTime) + 7 days (getPeriodDuration) = 14 days

    which will again be false, and the return the PeriodFinish()

  2. LastUpdateTime = 14 days

This whole process has to be repeated 3000 times untill the periodFinish be greater than the block.timestamp

Impact

Miss behaviour of the function, it will return a wrong value for the first 3000 calls

Tools Used

Recommendations

if it is the first time that is called _updateReward

Set the lastUpdateTime = block.timestamp

Updates

Lead Judging Commences

inallhonesty Lead Judge 5 months ago
Submission Judgement Published
Validated
Assigned finding tags:

BaseGauge period end time miscalculation creates circular dependency between periodFinish() and lastUpdateTime, preventing periods from naturally ending and disrupting reward distribution

inallhonesty Lead Judge 5 months ago
Submission Judgement Published
Validated
Assigned finding tags:

BaseGauge period end time miscalculation creates circular dependency between periodFinish() and lastUpdateTime, preventing periods from naturally ending and disrupting reward distribution

Support

FAQs

Can't find an answer? Chat with us on Discord, Twitter or Linkedin.