Core Contracts

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

Reward Miscalculation Vulnerability in RAAC Gauge System

Summary

A reward miscalculation vulnerability exists in the GaugeController.sol and related gauge contracts (BaseGauge.sol, RAACGauge.sol, RWAGauge.sol). The issue arises when reward emissions are paused and restarted, leading to inaccurate reward calculations for stakers.

This issue allows users to accumulate extra rewards, even for periods where no active reward emissions occurred

Vulnerability Details

The functions getRewardPerToken and earned depend on the difference between lastUpdateTime and the current timestamp.

  • When a new reward distribution period starts, the function does not properly reset or account for any inactive time gap between the previous reward end and the new start.

  • As a result, extra rewards are accumulated for liquidity providers, which can be exploited for excessive claims.

/**
* @notice Calculates current reward per token
* @return Current reward per token value
*/
function getRewardPerToken() public view returns (uint256) {
if (totalSupply() == 0) {
return rewardPerTokenStored;
}
return rewardPerTokenStored + (
(lastTimeRewardApplicable() - lastUpdateTime) * rewardRate * 1e18 / totalSupply()
);
}
/**
* @notice Calculates earned rewards for account
* @param account Address to calculate earnings for
* @return Amount of rewards earned
*/
function earned(address account) public view returns (uint256) {
return (getUserWeight(account) *
(getRewardPerToken() - userStates[account].rewardPerTokenPaid) / 1e18
) + userStates[account].rewards;
}

Impact

This vulnerability in BaseGauge.sol introduces a critical flaw in reward distribution, leading to unfair token emissions and potential financial losses for the protocol.

** Misallocation of Rewards**

  • Users receive rewards for inactive time periods, meaning they earn rewards even when no emissions are active.

  • This leads to inflationary pressure on the reward token, depleting the reward pool faster than expected.

Severity: High

  • Potential fund loss for the protocol.

  • Excessive inflation of the reward token.

  • Exploitable by attackers to drain emissions funds.

Tools Used

Manual Review

Recommendations

Modify notifyRewardAmount to correctly reset the timestamp:

function notifyRewardAmount(uint256 amount) external override onlyController updateReward(address(0)) {
if (amount > periodState.emission) revert RewardCapExceeded();
// ✅ Fix: Reset `lastUpdateTime` to current block timestamp
lastUpdateTime = block.timestamp;
rewardRate = notifyReward(periodState, amount, periodState.emission, getPeriodDuration());
periodState.distributed += amount;
uint256 balance = rewardToken.balanceOf(address(this));
if (rewardRate * getPeriodDuration() > balance) {
revert InsufficientRewardBalance();
}
emit RewardNotified(amount);
}

Ensures rewards only start accumulating from the new distribution period.
Prevents inactive time from being included in calculations.

Updates

Lead Judging Commences

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

Give us feedback!