Core Contracts

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

GaugeController::_calculateReward will return zero in some cases, resulting in 0 rewards for gauge even when there should be rewards

Summary

In reward calculation, if total weight of all gauges exceed the weight of a single gauge in a considerable way, that gauge’s rewards will be calculated as zero.

Details

GaugeController::distributeRewards calls _calculateReward with the specified gauge address.

_calculateReward calls getTotalWeight , which loops through all the gauges in the _gaugeList and sums their weights.

/**
* @notice Gets total weight of all active gauges
* @return Total weight across all gauges
*/
function getTotalWeight() public view override returns (uint256) {
uint256 total = 0;
// This could be optimized by maintaining a running total
for (uint256 i = 0; i < _gaugeList.length; i++) {
if (gauges[_gaugeList[i]].isActive) {
total += gauges[_gaugeList[i]].weight;
}
}
return total;
}

Then, back in _calculateReward function, the shares for that specific gauge is calculated with the following formula: uint256 gaugeShare = (g.weight * WEIGHT_PRECISION) / totalWeight;

WEIGHT_PRECISION is hardcoded to 10000.

Which means, if totalWeight surpasses the multiplication of the weight of the gauge in question with 10000, gaugeShare will round down to zero.

When gaugeShare gets calculated as 0, the return statement will also be 0 because here:

return (periodEmission * gaugeShare * typeShare) / (WEIGHT_PRECISION * WEIGHT_PRECISION);

0 multiplied by anything else is 0, and when you divide 0 with anything, the result is again 0. So, even though the gauge has weight, the calculation returns 0 rewards.

/**
* @notice Calculates reward amount for a gauge
* @dev Uses gauge weight and type weight to determine share
* @param gauge Address of gauge to calculate reward for
* @return Calculated reward amount
*/
function _calculateReward(address gauge) internal view returns (uint256) {
Gauge storage g = gauges[gauge];
uint256 totalWeight = getTotalWeight();
if (totalWeight == 0) return 0;
uint256 gaugeShare = (g.weight * WEIGHT_PRECISION) / totalWeight; // @audit will round down to zero
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);
}

Impact

Gauge rewards calculated wrongly and return 0 value.

Recommendation

You could consider a more precise weight precision like 1e18.

Updates

Lead Judging Commences

inallhonesty Lead Judge 4 months ago
Submission Judgement Published
Invalidated
Reason: Non-acceptable severity

Appeal created

0xgondar Submitter
3 months ago
inallhonesty Lead Judge
3 months ago
inallhonesty Lead Judge 3 months ago
Submission Judgement Published
Invalidated
Reason: Non-acceptable severity

Support

FAQs

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