Core Contracts

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

Incorrect Reward Calculation Due to Misuse of Gauge Weight

Summary

The earned function is intended to calculate the rewards a user has accrued based on the amount of tokens they have staked. However, it relies on the getUserWeight function to determine the user's weight. Instead of reflecting the user's individual staked tokens, getUserWeight returns the gauge’s weight as provided by the GaugeController. This means rewards are calculated based on a gauge-wide value (influenced by external voting) rather than on the user's actual stake, leading to incorrect and unfair reward distribution.

Vulnerability Details

  • What Went Wrong:
    The earned function is defined as follows:

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

It uses getUserWeight(account) to determine the weight for reward calculation. However, getUserWeight is implemented as:

function getUserWeight(address account) public view virtual returns (uint256) {
uint256 baseWeight = _getBaseWeight(account);
return _applyBoost(account, baseWeight);
}

And _getBaseWeight returns:

function _getBaseWeight(address account) internal view virtual returns (uint256) {
return IGaugeController(controller).getGaugeWeight(address(this));
}

This chain of function calls retrieves the gauge’s weight from the GaugeController, rather than a weight calculated from the individual user’s staked tokens. As a result, rewards are not distributed based on each user's actual stake, but instead are tied to the overall gauge weight determined by external voting.
Why It Matters:
The intended design is to distribute rewards in proportion to the tokens staked by each user, potentially modified by a boost factor. By using the gauge’s weight—which is uniform for all users of that gauge—the reward calculation fails to differentiate between users with different staking amounts. This flaw undermines the fairness of the reward mechanism and can lead to significant discrepancies in reward distribution.

Impact

Unfair Reward Distribution:
Users receive rewards based on the gauge’s weight rather than their individual stake. This can result in users with small stakes earning rewards comparable to those with larger stakes, or rewards being determined entirely by external gauge voting.
Governance Manipulation Risk:
Since the gauge’s weight is influenced by voting in the GaugeController, malicious actors could manipulate gauge weights to unfairly affect reward distribution, regardless of the actual staked amounts.
Economic Imbalance:
The discrepancy between intended and actual reward allocation can lead to an economic imbalance, potentially reducing trust in the protocol and affecting user participation.

Tools Used

Manual Code Review: We thoroughly examined the reward calculation logic in the BaseGauge contract, focusing on how earned, getUserWeight, and _getBaseWeight are implemented and interact with the GaugeController.

Recommendations

Incorporate User-Specific Stake Data:
Modify the getUserWeight function (or introduce a new function) to calculate the user's weight based on their actual staked tokens (e.g., using data from the user's balance in the gauge) combined with any boost factors, rather than solely relying on the gauge's weight.
Revise Reward Calculation Logic:
Ensure that the earned function reflects rewards in direct proportion to the user’s individual stake. For example, use the user’s staking balance (from a mapping such as _balances) to compute their share of the rewards.
Comprehensive Testing:
Develop unit and integration tests that simulate different staking scenarios to verify that rewards are accurately and fairly distributed according to individual user stakes.

Updates

Lead Judging Commences

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