Core Contracts

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

Incorrect Scaling in `BaseGauge::earned` Results in Significantly Reduced Rewards

Summary

BaseGauge::earned incorrectly scales reward calculations by dividing by 1e18 when multiplying a BPS value with an e18 value, resulting in users receiving significantly fewer rewards than intended.

Vulnerability Details

[](https://github.com/Cyfrin/2025-02-raac/blob/89ccb062e2b175374d40d824263a4c0b601bcb7f/contracts/core/governance/gauges/BaseGauge.sol#L585)

In BaseGauge::earned, the final reward calculation improperly scales the rewards by dividing a BPS-scaled value by 1e18:

function earned(address account) public view returns (uint256) {
return (getUserWeight(account) *
(getRewardPerToken() - userStates[account].rewardPerTokenPaid) / 1e18
) + userStates[account].rewards;
}

Example:

  1. Initial Values:

getUserWeight(account) = 10000 (in BPS)
getRewardPerToken() = 1428571428571428192 (in e18)
userStates[account].rewardPerTokenPaid = 0
userStates[account].rewards = 0
  1. Step by Step Calculation:

// First calculate reward token difference
rewardDiff = getRewardPerToken() - rewardPerTokenPaid
= 1428571428571428192 - 0
= 1428571428571428192 (in e18)
// Then multiply by user weight (in BPS)
weightedReward = 10000 * 1428571428571428192
= 14285714285714281920000
// Divide by 1e18 (this is where scaling breaks)
scaledReward = 14285714285714281920000 / 1e18
= 14285 // Result is now a tiny non-e18 number!
// Add existing rewards
finalReward = 14285 + 0
= 14285 // Should be ~14285e18

Impact

  • Users receive significantly reduced rewards (by a factor of 1e18)

  • Core gauge reward mechanism is effectively broken

  • All reward calculations are orders of magnitude smaller than intended

Tools Used

Foundry

Recommendations

Modify the earned calculation to maintain proper e18 scaling throughout:

function earned(address account) public view returns (uint256) {
return (getUserWeight(account) *
- (getRewardPerToken() - userStates[account].rewardPerTokenPaid) / 1e18
+ (getRewardPerToken() - userStates[account].rewardPerTokenPaid) / 10000 // divide by BPS
) + userStates[account].rewards;
}
Updates

Lead Judging Commences

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

BaseGauge reward calculations divide by 1e18 despite using 1e4 precision weights, causing all user weights to round down to zero and preventing reward distribution

Support

FAQs

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