Core Contracts

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

gauge revenue distribution will almost always fail silently

Summary

In revenue distribution, when shares are calculated, it is calculated in a way that it will almost always round down to zero, and will silently fail.

Details

GaugeController::distributeRevenue is called by EMERGENCY_ADMIN to distribute revenue to gauges. It calls _distributeToGauges function, which loops through gauges (twice) to calculate the rewards.

/**
* @notice Distributes rewards to gauges of a specific type
* @dev Internal function to handle gauge reward distribution
* @param gaugeType Type of gauges to distribute to
* @param amount Total amount to distribute
*/
function _distributeToGauges(GaugeType gaugeType, uint256 amount) internal {
uint256 totalTypeWeight = 0;
uint256[] memory gaugeWeights = new uint256[](_gaugeList.length);
uint256 activeGaugeCount = 0;
// First pass: calculate total weight and store gauge weights
for (uint256 i = 0; i < _gaugeList.length; i++) {
address gauge = _gaugeList[i];
if (
gauges[gauge].isActive && gauges[gauge].gaugeType == gaugeType
) {
gaugeWeights[i] = gauges[gauge].weight;
@> totalTypeWeight += gaugeWeights[i];
activeGaugeCount++;
}
}
if (totalTypeWeight == 0 || activeGaugeCount == 0) return;
// Second pass: distribute rewards
for (uint256 i = 0; i < _gaugeList.length; i++) {
address gauge = _gaugeList[i];
if (
gauges[gauge].isActive && gauges[gauge].gaugeType == gaugeType
) {
@> uint256 gaugeShare = (amount * gaugeWeights[i]) /
totalTypeWeight; //@audit this will round down to 0
if (gaugeShare > 0) {
IGauge(gauge).notifyRewardAmount(gaugeShare);
}
}
}
}

In the first loop, totalTypeWeight is calculated as:

totalTypeWeight += gaugeWeights[i];

Then, on the second loop gaugeShare is calculated as:

uint256 gaugeShare = (amount * gaugeWeights[i]) /
totalTypeWeight; //@audit this will round down to 0
if (gaugeShare > 0) {
IGauge(gauge).notifyRewardAmount(gaugeShare);
}
}

Now, totalTypeWeight is the sum of each gauge’s gaugeWeights value, so it is necessarily bigger than the individual gauge’s weight.

When amount multiplied with the current gauge’s weight is smaller than totalTypeWeight, the division will round down to zero, and since the next line only calls notifyRewardAmount only if gaugeShare > 0 it will just fail silently.

Impact

Revenue distribution will fail in many cases, breaking the distribution logic and making the dApp more cumbersome. The admin can just call the function with a different/higher amount, yes, but that also means that the admin cannot distribute the revenue with the correct, intended value, and has to either wait for amount accumulation, or opt for a different amount than it should be.

Recommendation

Consider using a decimal scaling like 1e18 in calculations including division.

Updates

Lead Judging Commences

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

GaugeController's _distributeToGauges function leaves revenue undistributed due to integer division truncation when calculating gauge shares

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

GaugeController's _distributeToGauges function leaves revenue undistributed due to integer division truncation when calculating gauge shares

Support

FAQs

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