Core Contracts

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

Less gauge rewards are distributed due to incorrectly using `getTotalWeight` instead of `total weight of all GaugeType`

##Summary

Protocol has 2 types of gauges: RWA and RAAC gauges. Each type has its own reward amount emission.
GaugeController calculates and distribute the rewards as a percent of total weight of all active gauges. Instead it should calculate the rewards as a percent of the total weight of all active gauges of the same type as the gauge for which rewards are being distributed.

Vulnerability Details

GaugeController can have 2 types of gauges: RWA gauges and RAAC gauges. Each type has it's own reward emission schedule( defined by amount and duration)

function addGauge(
address gauge,
GaugeType gaugeType,
uint256 initialWeight
) external onlyGaugeAdmin {
if (gauges[gauge].lastUpdateTime != 0) revert GaugeAlreadyExists();
@> if (gaugeType != GaugeType.RWA && gaugeType != GaugeType.RAAC) {
revert InvalidGaugeType();
}

GaugeController::distributeRewards can be called to distribute the rewards to the specified gauge.
The reward amount is calculated by _calculateReward:

function _calculateReward(address g.gaugeType) internal view returns (uint256) {
Gauge storage g = gauges[gauge];
@> uint256 totalWeight = getTotalWeight();//@audit: wrong total weight; get weight of all *g.gaugeType* instead
if (totalWeight == 0) return 0;
@> uint256 gaugeShare = (g.weight * WEIGHT_PRECISION) / totalWeight;
uint256 typeShare = (typeWeights[g.gaugeType] * WEIGHT_PRECISION) / MAX_TYPE_WEIGHT;
// Calculate period emissions based on gauge type
uint256 periodEmission = g.gaugeType == GaugeType.RWA ? _calculateRWAEmission() : _calculateRAACEmission();
return (periodEmission * gaugeShare * typeShare) / (WEIGHT_PRECISION * WEIGHT_PRECISION);
}
  • gaugeShare is the percentage of gauge's weight relative to the total weight of all active gauges

  • typeShare which is based on typeWeights admin set multiplier

  • periodEmission is the amount to be distributed to all gauges of the same type as gauge's type (g.gaugeType);

The reward is calculated as a percentage of the gauge's weight relative to the sum of all active gauges. This is wrong because each gauge type has it's own amount of rewards configured and the gaugeShare is applied to that amount.

The gaugeShare should be calculated as a % of the total weights allocated to all gauges of of the same type.

Impact

Gauge rewards are incorrectly calculated. Gauges receive less rewards.

Tools Used

Implement a new function similarly to getTotalWeight(). It should return the weight of all active gauges for the given GaugeType.
Use this function to calculate the correct gaugesShare, passing the gauge's type as argument .

function _calculateReward(address gauge) internal view returns (uint256) {
Gauge storage g = gauges[gauge];
- uint256 totalWeight = getTotalWeight();
+ uint256 totalGaugeTypeWeight = getTotalWeightOfType(g.gaugeType);
...
}

Additionally, the typeWeights admin set mapping has a simmilar purpose as _calculateRWAEmission and _calculateRAACEmission functions. To avoid confusion and reward configuration errors, consider removing the typeWeights mapping.

Recommendations

Updates

Lead Judging Commences

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

GaugeController::_calculateReward uses combined weight of all gauge types instead of type-specific weights, causing incorrect reward distribution between RWA and RAAC gauges

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

GaugeController::_calculateReward uses combined weight of all gauge types instead of type-specific weights, causing incorrect reward distribution between RWA and RAAC gauges

Support

FAQs

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

Give us feedback!