Core Contracts

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

Truncation of shares possible in FeeCollector.sol when calculating fees

Summary

When calculating fees in FeeCollector.sol, distributeCollectedFees() is called by the DISTRIBUTOR_ROLE which calls _calculateDistribution()

In _calculateDistribution(), the weight and feeType.xShare may be very low, which result in a truncation when dividing by BASIS_POINTS

Vulnerability Details

In _calculateDistribution(), feeAmount is calculated per feeType which is then divided by totalFees to get weight. The weight is then multiplied by the share and divided by BASIS_POINTS which is 10000.

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;
}

Looking at an example, let's say total fees is 40e18, which is distributed as such: (1e18,10e18,5e18,4e18,0e18,3e18,7e18,10e18)

For feeAmount(0) which is 1e18, the weight is 1e18 * 10000 / 40e18 which is 250. The calculation here has no problems, assuming initial feetype:

feeTypes[0] = FeeType({
veRAACShare: 8000, // 80%
burnShare: 0,
repairShare: 0,
treasuryShare: 2000 // 20%
});
shares[0] += (250 * 8000 / 10000) = 200
shares[1] += (250 * 0 / 10000) = 0
shares[2] += (250 * 0 / 10000) = 0
shares[3] += (250 * 2000 / 10000) = 50

Now, if we look at feeType(6), weight = 7e18 * 10000 / 40e18 = 1750. Note the truncation of 87.5

feeTypes[6] = FeeType({
veRAACShare: 500, // 0.5%
burnShare: 500, // 0.5%
repairShare: 1000, // 1.0%
treasuryShare: 0
});
shares[0] += (1750 * 500 / 10000) = 87.5 (truncated to 87)
shares[1] += (1750 * 500 / 10000) = 87.5 (truncated to 87)
shares[2] += (1750* 1000 / 10000) = 175
shares[3] += (1750* 0 / 10000) = 0

This also accounts for the fact that the weight is quite high already, if the weight is lowered, the truncation can be zero.

Impact

Fees is not counted properly, leading to loss fees

Tools Used

Manual Review

Recommendations

Recommend improving the precision of the shares calculation to prevent truncation down or even to zero.

Updates

Lead Judging Commences

inallhonesty Lead Judge 4 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 4 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.