Core Contracts

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

`FeeCollector::updateFeeType` wrong fee share validation leads to impossible update for some fee types

Summary

The FeeCollector::updateFeeType updates parameters for a specific fee type. Each fee type has shares that determine how the collected fees are distributed among different stakeholders (veRAACShare, burnShare, repairShare, treasuryShare). The updateFeeType function validates that these shares total to 100% (10000 basis points). For Buy/Sell Swap Tax (type 6) and NFT Royalty fees (type 7), the inizialized total shares add up to 2000 basis points. This means it's impossible to update Buy/Sell Swap Tax and/or NFT Royalty fees or other types if the share total is less than 10000 through the intended update function (FeeCollector::updateFeeType).

Vulnerability Details

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

Impact

Add Foundry to the project following this procedure

Create a file named FeeCollector.t.sol and copy/paste this:

// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.0;
import {Test, console2} from "forge-std/Test.sol";
import {FeeCollector} from "../contracts/core/collectors/FeeCollector.sol";
import {RAACToken} from "../contracts/core/tokens/RAACToken.sol";
import {veRAACToken} from "../contracts/core/tokens/veRAACToken.sol";
import {IFeeCollector} from "../contracts/interfaces/core/collectors/IFeeCollector.sol";
contract FeeCollectorTest is Test {
FeeCollector public feeCollector;
RAACToken public raacToken;
veRAACToken public veRaacToken;
address owner = makeAddr("owner");
address treasury = makeAddr("treasury");
address repairFund = makeAddr("repairFund");
uint256 SWAP_TAX_RATE = 100; // 1%
uint256 BURN_TAX_RATE = 50; // 0.5%
function setUp() public {
raacToken = new RAACToken(owner, SWAP_TAX_RATE, BURN_TAX_RATE);
veRaacToken = new veRAACToken(address(raacToken));
feeCollector = new FeeCollector(address(raacToken), address(veRaacToken), treasury, repairFund, owner);
console2.log("raacToken: ", address(raacToken));
console2.log("veRaacToken: ", address(veRaacToken));
console2.log("feeCollector: ", address(feeCollector));
}
function test_cantChangeParamThatDosntMatch100() public {
vm.startPrank(owner);
IFeeCollector.FeeType memory swapFeeType = feeCollector.getFeeType(6);
assertEq(swapFeeType.veRAACShare, 500);
assertEq(swapFeeType.burnShare, 500);
assertEq(swapFeeType.repairShare, 1000);
assertEq(swapFeeType.treasuryShare, 0);
IFeeCollector.FeeType memory swapFeeTypeUpdate =
IFeeCollector.FeeType({veRAACShare: 700, burnShare: 700, repairShare: 500, treasuryShare: 100});
vm.expectRevert();
feeCollector.updateFeeType(6, swapFeeTypeUpdate);
IFeeCollector.FeeType memory swapFeeTypeAfter = feeCollector.getFeeType(6);
assertNotEq(swapFeeTypeAfter.veRAACShare, 700);
assertNotEq(swapFeeTypeAfter.burnShare, 700);
assertNotEq(swapFeeTypeAfter.repairShare, 500);
assertNotEq(swapFeeType.veRAACShare, 100);
assertEq(swapFeeType.veRAACShare, 500);
assertEq(swapFeeType.burnShare, 500);
assertEq(swapFeeType.repairShare, 1000);
assertEq(swapFeeType.treasuryShare, 0);
vm.stopPrank();
}
}

Run forge test --match-test test_cantChangeParamThatDosntMatch100 -vv

Logs:
raacToken: 0x5615dEB798BB3E4dFa0139dFa1b3D433Cc23b72f
veRaacToken: 0x2e234DAe75C793f67A35089C9d99245E1C58470b
feeCollector: 0xF62849F9A0B5Bf2913b396098F7c7019b51A820a
Suite result: ok. 1 passed; 0 failed; 0 skipped; finished in 7.94ms (735.50µs CPU time)

The test shows that if we try to updateFeeType for Buy/Sell Swap Tax (type 6) with a total of the param of 2000 (=20%) < 10000 (100%), the function reverts and the params can't be changed.

Tools Used

Manual review

Recommendations

Modify the validation in updateFeeType to accommodate different total shares for different fee types.

Updates

Lead Judging Commences

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

Give us feedback!