Core Contracts

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

Fee distribution logic doesn't work properly in FeeCollector#_calculateDistribution

Summary

FeeCollector#_calculateDistribution calculates distribution shares for different stakeholders. But these distribution shares can be miscalculated due to precision loss.

Vulnerability Details

function _calculateDistribution(uint256 totalFees) internal view returns (uint256[4] memory shares) {
uint256 totalCollected;
for (uint8 i = 0; i < 8; i++) {
uint256 feeAmount = _getFeeAmountByType(i);
if (feeAmount == 0) continue;
FeeType memory feeType = feeTypes[i];
totalCollected += feeAmount;
uint256 weight = (feeAmount * BASIS_POINTS) / totalFees;
shares[0] += (weight * feeType.veRAACShare) / BASIS_POINTS;
shares[1] += (weight * feeType.burnShare) / BASIS_POINTS;
shares[2] += (weight * feeType.repairShare) / BASIS_POINTS;
shares[3] += (weight * feeType.treasuryShare) / BASIS_POINTS;
}
if (totalCollected != totalFees) revert InvalidFeeAmount();
shares[0] = (totalFees * shares[0]) / BASIS_POINTS;
shares[1] = (totalFees * shares[1]) / BASIS_POINTS;
shares[2] = (totalFees * shares[2]) / BASIS_POINTS;
shares[3] = (totalFees * shares[3]) / BASIS_POINTS;
uint256 remainder = totalFees - (shares[0] + shares[1] + shares[2] + shares[3]);
if (remainder > 0) shares[3] += remainder;
}

For each feeAmount, shares[] are added percent of totalFees, not pure shares. But this flaws can shares set 0 in some cases leading to distribution logic incorrect.

For example, assume: feeAmount = 1e18, totalFees = 100000e18

feeTypes[7] = FeeType({
veRAACShare: 50, // 0.5%
burnShare: 0,
repairShare: 100, // 1.0%
treasuryShare: 50 // 0.5%
});

In case of feeTypes[7], weight = 50 * 10000 / 100000 = 0. so in this case all of shares[] += 0 and at the end of this function this feeAmount is added to the treasuryShare. The shares except treasuryShare are miscalculated.

Impact

Distribution logic is broken.

veRAACShares can be 0 leading to stake holders receive fewer rewards.

Tools Used

manual

Recommendations

Calculates shares for each case instead of sum up total percentage of each shares.

function _calculateDistribution(uint256 totalFees) internal view returns (uint256[4] memory shares) {
uint256 totalCollected;
for (uint8 i = 0; i < 8; i++) {
uint256 feeAmount = _getFeeAmountByType(i);
if (feeAmount == 0) continue;
FeeType memory feeType = feeTypes[i];
totalCollected += feeAmount;
- uint256 weight = (feeAmount * BASIS_POINTS) / totalFees;
+ shares[0] += (feeAmount * feeType.veRAACShare) / BASIS_POINTS;
+ shares[1] += (feeAmount * feeType.burnShare) / BASIS_POINTS;
+ shares[2] += (feeAmount * feeType.repairShare) / BASIS_POINTS;
+ shares[3] += (feeAmount * feeType.treasuryShare) / BASIS_POINTS;
}
if (totalCollected != totalFees) revert InvalidFeeAmount();
- shares[0] = (totalFees * shares[0]) / BASIS_POINTS;
- shares[1] = (totalFees * shares[1]) / BASIS_POINTS;
- shares[2] = (totalFees * shares[2]) / BASIS_POINTS;
- shares[3] = (totalFees * shares[3]) / BASIS_POINTS;
uint256 remainder = totalFees - (shares[0] + shares[1] + shares[2] + shares[3]);
if (remainder > 0) shares[3] += remainder;
}
Updates

Lead Judging Commences

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

FeeCollector distributes too much to treasury when fee amounts are small relative to total due to precision loss in (feeAmount * BASIS_POINTS) / totalFees

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

FeeCollector distributes too much to treasury when fee amounts are small relative to total due to precision loss in (feeAmount * BASIS_POINTS) / totalFees

Support

FAQs

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