Core Contracts

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

Reward distribution function fails to transfer actual rewards in GaugeController

Summary

The distributeRewards function is designed to calculate and transfer rewards to a gauge based on its weight. However, while the function correctly computes the reward amount and calls the gauge’s notifyRewardAmount method, it does not perform any token transfer. As a result, gauges receive state updates that imply rewards have been distributed, but no actual reward tokens are transferred from the BaseGauge contract, leading to a discrepancy between documented behavior and on-chain state.

Vulnerability Details

  1. Lack of Actual Token Transfer:

    The function calculates the reward:

    uint256 reward = _calculateReward(gauge);

    and then calls:

    IGauge(gauge).notifyRewardAmount(reward);

    However, no transfer of reward tokens (using, for example, rewardToken.safeTransfer(...)) occurs. This means that while the gauge’s internal state is updated to reflect a new reward amount, the reward tokens remain in the BaseGauge contract.

  2. Misleading Documentation:

    The function’s comments state that it "Calculates and transfers rewards based on gauge weight," yet the implementation only updates the gauge's state via notifyRewardAmount.

Impact

  • False Reward Allocation:
    Gauges may display an updated reward rate internally, suggesting that rewards have been allocated, while the reward tokens remain in the BaseGauge contract. This misalignment can result in a situation where users expect rewards that have not been made available.

Proof-of-Concept (PoC) Example

  1. Scenario Setup:

    • Deploy the BaseGauge contract along with its dependencies, including the reward token and a gauge contract implementing the IGauge interface.

    • Ensure the gauge is registered (i.e., isGauge(gauge) returns true) and marked as active.

  2. Exploit Steps:

    • Step 1: Call the distributeRewards function with the gauge’s address.

    • Step 2: The function computes a non-zero reward value using _calculateReward(gauge).

    • Step 3: It then calls IGauge(gauge).notifyRewardAmount(reward) which updates the gauge's reward state.

    • Step 4: Verify that no reward tokens are transferred from the BaseGauge contract to the gauge.

  3. Observation:

    • The gauge’s internal state indicates that it has been notified of a reward amount.

    • However, a check of token balances confirms that the reward tokens remain in the BaseGauge contract, demonstrating that the actual token transfer did not occur.

Tools Used

Manual review

Recommendations

  1. Implement Actual Token Transfer:

    Update the distributeRewards function to perform an explicit transfer of reward tokens to the gauge before calling the notification function:

    function distributeRewards(address gauge) external override nonReentrant whenNotPaused {
    if (!isGauge(gauge)) revert GaugeNotFound();
    if (!gauges[gauge].isActive) revert GaugeNotActive();
    uint256 reward = _calculateReward(gauge);
    if (reward == 0) return;
    // Transfer reward tokens to the gauge
    rewardToken.safeTransfer(gauge, reward);
    // Notify the gauge of the transferred reward
    IGauge(gauge).notifyRewardAmount(reward);
    emit RewardDistributed(gauge, msg.sender, reward);
    }

    This ensures that the gauge receives the actual tokens corresponding to the calculated reward.

Updates

Lead Judging Commences

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

GaugeController notifies gauges of rewards without transferring tokens in both distributeRewards and _distributeToGauges functions, breaking reward distribution

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

GaugeController notifies gauges of rewards without transferring tokens in both distributeRewards and _distributeToGauges functions, breaking reward distribution

Support

FAQs

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