Core Contracts

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

Incorrect reward rate calculation due to mismatch between boosted weights and total supply

Relevant Context

The BaseGauge contract implements a staking mechanism where users' reward weights are boosted based on their veToken holdings. The getRewardPerToken function is crucial for calculating the rate at which rewards are distributed.

Finding Description

In BaseGauge#getRewardPerToken, the reward rate calculation uses totalSupply() as the denominator, which represents the raw sum of staked tokens. However, the actual reward distribution is based on boosted weights from getUserWeight. This creates a mismatch between the reward rate calculation and the actual distribution of rewards.

The reward rate formula:

rewardRate * timePeriod * 1e18 / totalSupply()

assumes that rewards should be distributed proportionally to staked amounts. However, since rewards are actually distributed based on boosted weights, this formula becomes incorrect when users have different boost multipliers.

Impact Explanation

High. This discrepancy leads to:

  1. Incorrect reward rate calculations

  2. Potential over-distribution of rewards when total boosted weight exceeds total supply

  3. Unfair reward distribution that doesn't properly account for the boost system

Likelihood Explanation

High. This issue affects every reward distribution and becomes more severe as the disparity between users' boost multipliers increases.

Proof of Concept

Consider this scenario:

  1. Total staked supply is 1000 tokens

  2. User A stakes 500 tokens with 2x boost (effective weight 1000)

  3. User B stakes 500 tokens with 1x boost (effective weight 500)

  4. Total effective weight is 1500, but totalSupply() returns 1000

  5. This causes reward rate to be calculated using 1000 as denominator instead of 1500

  6. Results in 50% higher reward rate than intended

Recommendation

Maintain a separate total for boosted weights and use it in reward calculations:

contract BaseGauge {
uint256 public totalBoostedSupply;
function getRewardPerToken() public view returns (uint256) {
if (totalBoostedSupply == 0) {
return rewardPerTokenStored;
}
return rewardPerTokenStored + (
(lastTimeRewardApplicable() - lastUpdateTime) * rewardRate * 1e18 / totalBoostedSupply
);
}
// Update totalBoostedSupply when weights change
function _updateUserBoost(address user, uint256 oldWeight, uint256 newWeight) internal {
totalBoostedSupply = totalBoostedSupply + newWeight - oldWeight;
}
}

This ensures reward rates accurately reflect the boosted weight system.

Updates

Lead Judging Commences

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

BaseGauge::earned calculates rewards using getUserWeight instead of staked balances, potentially allowing users to claim rewards by gaining weight without proper reward checkpoint updates

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

BaseGauge::earned calculates rewards using getUserWeight instead of staked balances, potentially allowing users to claim rewards by gaining weight without proper reward checkpoint updates

Support

FAQs

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