Core Contracts

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

Uninitialized `lastUpdateTime` Leads to Incorrect Reward Distribution

Summary

The BaseGauge::lastUpdateTime variable is not initialized in the constructor, leading to incorrect reward calculations during the first execution of updateReward(). This results in the first user claiming all accumulated rewards for an entire period (e.g., 7 days), causing a financial loss to the protocol.

Vulnerability Details

When updateReward() is called for the first time, the execution flow proceeds as follows:

updateReward() → _updateReward() → getRewardPerToken() → lastTimeRewardApplicable()

Looking at the lastTimeRewardApplicable() function:

function lastTimeRewardApplicable() public view returns (uint256) {
return block.timestamp < periodFinish() ? block.timestamp : periodFinish();
}
  • The periodFinish() function determines when the reward period ends:

function periodFinish() public view returns (uint256) {
return lastUpdateTime + getPeriodDuration();
}
  • However, since lastUpdateTime is initially 0, periodFinish() returns:

    0 + 7 days = 7 days

So the value of lastUpdateTime will set to wrong value which will be 7 days, as we can see in this function,

function _updateReward(address account) internal {
rewardPerTokenStored = getRewardPerToken();
lastUpdateTime = lastTimeRewardApplicable(); <<audit..
if (account != address(0)) {
UserState storage state = userStates[account];
state.rewards = earned(account);
state.rewardPerTokenPaid = rewardPerTokenStored;
state.lastUpdateTime = block.timestamp;
emit RewardUpdated(account, state.rewards);
}
}

Example Scenario:

  1. A user calls updateReward() for the first time.

  2. The protocol assumes 7 days of rewards have accrued instead of calculating from an actual lastUpdateTime.

  3. lastUpdateTime set's to the wrong value which is 7 days

Impact

  • Wrong calculation of reward tokens.

  • Very less amount of tokens will get distributed.

Tools Used

  • Manual Code Review

Recommendations

  1. Initialize lastUpdateTime in the constructor:

    constructor() {
    lastUpdateTime = block.timestamp;
    }
Updates

Lead Judging Commences

inallhonesty Lead Judge 7 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 7 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.

Give us feedback!