Core Contracts

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

Reward calculation vulnerability allows reward claims without staking in `BaseGauge`

Summary

The BaseGauge contract’s reward distribution mechanism is critically flawed. The reward calculation fails to incorporate a user’s staked balance due to an incorrect implementation in the weight computation logic. Specifically, the _getBaseWeight function erroneously uses the contract’s own gauge weight (via address(this)) instead of the user’s stake. This oversight enables any user—even those with zero staked tokens—to claim rewards, thereby bypassing the intended economic commitment required for earning rewards.

Vulnerability Details

  1. Incorrect Base Weight Derivation:

    The earned function, which computes the rewards for an account, is defined as:

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

    This function calls getUserWeight(account), which in turn depends on:

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

    Instead of using the account parameter to derive a user-specific base weight, the function incorrectly calls getGaugeWeight with address(this). As a result, every user is assigned the same base weight—one that is based solely on the gauge weight of the contract itself.

  2. Lack of Staked Balance Consideration:

    In a properly functioning staking system, the rewards should scale with the staked tokens, typically involving a calculation along the lines of:

    earned = _balances[account] * (rewardPerToken - userStates[account].rewardPerTokenPaid) + userStates[account].rewards;

    However, by not incorporating the staked balance (tracked in _balances), the reward distribution is decoupled from user contributions. Consequently, a user with no staked tokens can still claim rewards, since the gauge weight used is a property of the contract rather than the user’s participation.

  3. Bypassing Economic Incentives:

    Due to the error, users are incentivized to exploit the system by repeatedly calling the getReward function—even if they have never staked any tokens—thereby draining the reward pool without any actual commitment or risk.

Proof-of-Concept (PoC) Example

  1. Scenario Setup:

    • Deploy the BaseGauge contract along with its dependencies (staking token, reward token, veToken, and GaugeController).

    • Ensure multiple user accounts are available.

  2. Exploit Steps:

    • Step 1: From Account A, do not call the stake function (thus, _balances[Account A] remains zero).

    • Step 2: Allow the contract to accumulate rewards (or simulate a reward accrual period).

    • Step 3: From Account A, call the getReward function.

  3. Observation:

    • Despite having no staked tokens, Account A successfully claims rewards.

    • This happens because the reward calculation uses a base weight derived from address(this) (the contract) instead of the user’s own stake.

  4. Result:

    • Account A receives rewards proportionate to the gauge weight of the contract, illustrating that staking is effectively bypassed

Impact

  • Unfair Reward Distribution:
    Users can claim rewards without staking any tokens, meaning that rewards are not allocated based on actual economic participation. This undermines the fundamental design of the staking mechanism.

  • Economic Exploitation:
    Malicious actors can abuse the flaw to continuously claim rewards, depleting the reward pool and potentially destabilizing the protocol's incentive structure. This results in an erosion of the value proposition for genuine stakers.

  • Loss of Protocol Integrity:
    The integrity of the staking mechanism is compromised, leading to inaccurate reward calculations. Over time, such discrepancies can diminish stakeholder confidence, reduce user engagement, and negatively affect the long-term viability of the system.

  • Revenue Drain:
    As rewards are distributed without proper staking contributions, the protocol may incur unexpected financial losses, thereby affecting its sustainability and market reputation.

Tools Used

Manual review

Recommendations

  1. Correct the Base Weight Calculation:

    Modify the _getBaseWeight function to incorporate the user’s staked balance. For instance, if the design intends for the base weight to reflect the staked amount, the function should be updated as follows:

    function _getBaseWeight(address account) internal view virtual returns (uint256) {
    // Use the user’s staked balance as the base weight.
    return _balances[account];
    }

    Alternatively, if the design mandates that gauge weight is a factor, the function should correctly reference the user’s address:

    function _getBaseWeight(address account) internal view virtual returns (uint256) {
    return IGaugeController(controller).getGaugeWeight(account);
    }
  2. Integrate Staked Balance into User Weight Calculation:

    Adjust the getUserWeight function so that it properly factors in the staked balance. For example:

    function getUserWeight(address account) public view virtual returns (uint256) {
    uint256 userStake = _balances[account];
    // Optionally, retrieve a gauge multiplier if necessary.
    uint256 gaugeMultiplier = IGaugeController(controller).getGaugeWeight(account);
    // Apply any boost multiplier if required.
    return _applyBoost(account, userStake * gaugeMultiplier);
    }

    This ensures that rewards are proportional to the actual staked tokens and any additional multipliers.

Updates

Lead Judging Commences

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