Core Contracts

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

Fee Collector's Inaccurate Balance Tracking Creates Systemic Fund Distribution Issues

Summary and Impact

The FeeCollector contract fails to account for RAAC token's transfer taxes in its fee collection process, leading to a systemic discrepancy between recorded and actual token balances. When fees are collected, the contract records the pre-tax amount rather than the actual received amount, causing an accumulating deficit in the system.

This vulnerability directly impacts the protocol's ability to properly distribute collected fees, as the actual balance will always be lower than the recorded amount. According to the protocol documentation, the FeeCollector is responsible for managing and distributing protocol fees, lending fees, performance fees, and other critical revenue streams. When these distributions fail due to insufficient balances, it could lead to stuck funds and disrupt the protocol's revenue distribution mechanism.

Vulnerability Details

The core issue lies in the collectFee function of the FeeCollector contract:

function collectFee(uint256 amount, uint8 feeType) external override nonReentrant whenNotPaused returns (bool) {
if (amount == 0 || amount > MAX_FEE_AMOUNT) revert InvalidFeeAmount();
if (feeType > 7) revert InvalidFeeType();
// Transfer tokens from sender
raacToken.safeTransferFrom(msg.sender, address(this), amount);
// Update collected fees
_updateCollectedFees(amount, feeType);
emit FeeCollected(feeType, amount);
return true;
}

The RAAC token implements transfer taxes as shown in its contract:

function _update(
address from,
address to,
uint256 amount
) internal virtual override {
uint256 baseTax = swapTaxRate + burnTaxRate;
if (baseTax == 0 || from == address(0) || to == address(0) ||
whitelistAddress[from] || whitelistAddress[to] ||
feeCollector == address(0)) {
super._update(from, to, amount);
return;
}
uint256 totalTax = amount.percentMul(baseTax);
uint256 burnAmount = totalTax * burnTaxRate / baseTax;
super._update(from, feeCollector, totalTax - burnAmount);
super._update(from, address(0), burnAmount);
super._update(from, to, amount - totalTax);
}

When someone pays fees to the FeeCollector:

  1. They initiate a transfer of 100 RAAC tokens

  2. The RAAC token applies a 2% swap tax and 1% burn tax

  3. FeeCollector receives 97 tokens but records 100 in its accounting

  4. This 3-token difference accumulates with each fee collection

Later, when distributeCollectedFees is called:

function distributeCollectedFees() external override nonReentrant whenNotPaused {
if (!hasRole(DISTRIBUTOR_ROLE, msg.sender)) revert UnauthorizedCaller();
uint256 totalFees = _calculateTotalFees();
if (totalFees == 0) revert InsufficientBalance();
uint256[4] memory shares = _calculateDistribution(totalFees);
_processDistributions(totalFees, shares);
delete collectedFees;
emit FeeDistributed(shares[0], shares[1], shares[2], shares[3]);
}

The distribution attempts to distribute the recorded amount (100) when only 97 tokens are actually available, causing the transaction to revert and potentially leaving funds stuck in the contract.

Tools Used

  • Manual Review

  • Hardhat

Recommendations

Modify the collectFee function to track actual received tokens.

Updates

Lead Judging Commences

inallhonesty Lead Judge about 1 month ago
Submission Judgement Published
Invalidated
Reason: Non-acceptable severity
inallhonesty Lead Judge about 1 month ago
Submission Judgement Published
Invalidated
Reason: Non-acceptable severity

Support

FAQs

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