Core Contracts

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

Over-rewarding when the fees are calculated

Summary

When the FeeCollector contract is deployed, the different fee types are initialized by invoking _initializeFeeTypes(). The problem is that the swap fees and NFT royalty fees are initialized with the wrong amount which will inflate the received shares.

Vulnerability Details

function _initializeFeeTypes() internal {
...
// Buy/Sell Swap Tax (2% total)
feeTypes[6] = FeeType({
veRAACShare: 500, // 0.5%
burnShare: 500, // 0.5%
repairShare: 1000, // 1.0%
treasuryShare: 0
});
// NFT Royalty Fees (2% total)
feeTypes[7] = FeeType({
veRAACShare: 500, // 0.5%
burnShare: 0,
repairShare: 1000, // 1.0%
treasuryShare: 500 // 0.5%
});
}

As can be seen the swap fee must be in total 2% along with the NFT royalty fee (denominated in BPS, i.e 10000 = 100%).

  • 10000 = 100%

  • 1000 = 10%

  • 100 = 1%

  • 10 = 0.1%

The problem is that 500 equals 5% and 1000 equal 10%. That means the total collected fees will be 20% with 18% more than intended.

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

A very brief example would be:

  1. feeAmount = 0.01 ETH

  2. totalFees = 1e18

  3. weight = 0.01 * 10000 / 1e18 = 100

  4. for example let's take the repair share, the formula would be:

    • 100 (weight) * 1000 (the % set upon deployment) / 10000 (BPS) = 10 shares

  5. but actually the correct formula should be:

    • 100 * 100 (instead of 1000) / 10000 = 1 shares

The shares will be inflated and the collector will over-reward the recipients.

Impact

Over-rewarding the fee recipients, although there is an update function:

function updateFeeType(uint8 feeType, FeeType calldata newFee) external override {
if (!hasRole(FEE_MANAGER_ROLE, msg.sender)) revert UnauthorizedCaller();
if (feeType > 7) revert InvalidFeeType();
// Validate fee shares total to 100%
if (newFee.veRAACShare + newFee.burnShare + newFee.repairShare + newFee.treasuryShare != BASIS_POINTS) {
revert InvalidDistributionParams();
}
feeTypes[feeType] = newFee;
emit FeeTypeUpdated(feeType, newFee);
}

Here the logic requires new total of fees to be 100% otherwise the call reverts, but as can be seen from the code the intention for swap fees and NFT fee is to be only 2%. Overall this is not intended behaviour and cannot be fixed until the devs decide to allocate 100% by calling the update function it will inflate the fees. For this reason i consider this as Low severity.

Tools Used

Manual Review

Recommendations

function _initializeFeeTypes() internal {
...
// Buy/Sell Swap Tax (2% total)
feeTypes[6] = FeeType({
- veRAACShare: 500, // 0.5%
- burnShare: 500, // 0.5%
- repairShare: 1000, // 1.0%
+ veRAACShare: 50, // 0.5%
+ burnShare: 50, // 0.5%
+ repairShare: 100, // 1.0%
treasuryShare: 0
});
// NFT Royalty Fees (2% total)
feeTypes[7] = FeeType({
- veRAACShare: 500, // 0.5%
+ veRAACShare: 50, // 0.5%
burnShare: 0,
- repairShare: 1000, // 1.0%
+ repairShare: 100, // 1.0%
- treasuryShare: 500 // 0.5%
+ treasuryShare: 50 // 0.5%
});
}
Updates

Lead Judging Commences

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

Fee shares for fee type 6 and 7 inside FeeCollector do not total up to the expected 10000 basis points, this leads to update problems, moreover they are 10x the specifications

Support

FAQs

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