Core Contracts

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

Calling BaseGauge.notifyRewardAmount() after period has ended will make _updateRewards() revert

Summary

Calling BaseGauge.notifyRewardAmount() after period has ended will make _updateRewards() revert

Vulnerability Details

When BaseGauge.notifyRewardAmount() is called `lastUpdateTime` is updated to block.timestamp. _updateReward() being called after that (through the updateReward modifier in stake() or withdraw()) will revert in this code line of getRewardPerToken():

return rewardPerTokenStored + (
(lastTimeRewardApplicable() - lastUpdateTime) * rewardRate * 1e18 / totalSupply()
);

This happens because lastTimeRewardApplicable returns the period end timestamp, but lastUpdateTime now is a higher value (current timestamp > period end timestamp), therefore the function return for trying to compute a negative unsigned integer.

Impact

Users cannot stake() or withdraw() if notifyRewardAmount() has been called after the period finished, preventing them from getting the staked tokens and breaking the expected behaviour of the contract.

Tools Used

Manual review

Recommendations

Do never update lastUpdateTime to a timestamp higher than period end, it is not necessary to set it as updateReward modifier will do it.

function notifyRewardAmount(uint256 amount) external override onlyController updateReward(address(0)) {
if (amount > periodState.emission) revert RewardCapExceeded();
//Update reward rate as input amount to distribute / period duration (7 days)
rewardRate = notifyReward(periodState, amount, periodState.emission, getPeriodDuration());
periodState.distributed += amount;
uint256 balance = rewardToken.balanceOf(address(this));
if (rewardRate * getPeriodDuration() > balance) {
revert InsufficientRewardBalance();
}
- lastUpdateTime = block.timestamp;
emit RewardNotified(amount);
}
Updates

Lead Judging Commences

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

BaseGauge sets user's lastUpdateTime to uncapped block.timestamp while global lastUpdateTime uses capped lastTimeRewardApplicable(), generating reward calc inconsistencies after period ends

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

BaseGauge sets user's lastUpdateTime to uncapped block.timestamp while global lastUpdateTime uses capped lastTimeRewardApplicable(), generating reward calc inconsistencies after period ends

Support

FAQs

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