Core Contracts

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

Wrong `burn` logic in RAACToken contract leads to impossibility for FeeCollector to correctly execute its burn.

Summary

The burn function in RAACToken contract is defined as follows:

function burn(uint256 amount) external {
uint256 taxAmount = amount.percentMul(burnTaxRate);
_burn(msg.sender, amount - taxAmount);
if (taxAmount > 0 && feeCollector != address(0)) {
_transfer(msg.sender, feeCollector, taxAmount);
}
}

This function might be used by users to burn RAAC tokens, or by the FeeCollector contract that uses it as part of its process distribution mechanism:

function _processDistributions(uint256 totalFees, uint256[4] memory shares) internal {
uint256 contractBalance = raacToken.balanceOf(address(this));
if (contractBalance < totalFees) revert InsufficientBalance();
if (shares[0] > 0) {
uint256 totalVeRAACSupply = veRAACToken.getTotalVotingPower();
if (totalVeRAACSupply > 0) {
TimeWeightedAverage.createPeriod(
distributionPeriod, block.timestamp + 1, 7 days, shares[0], totalVeRAACSupply
);
totalDistributed += shares[0];
} else {
shares[3] += shares[0]; // Add to treasury if no veRAAC holders
}
}
// @audit: burn shares[1] amount of tokens.
if (shares[1] > 0) raacToken.burn(shares[1]);
if (shares[2] > 0) raacToken.safeTransfer(repairFund, shares[2]);
if (shares[3] > 0) raacToken.safeTransfer(treasury, shares[3]);
}

The problem arises because:

  • The FeeCollector contract has its own mechanism to handle fee distribution, with 4 shareholders: veRAAC share, burn share, repair fund share, treasury share

  • burn function itself has a way to handle tax payment, which includes burning and transfer to fee collector

Vulnerability Details

When the FeeCollector wants to process the distribution of the fees it has collected since last distribution, we expect shares[1]to be burned entirely, as it is the burn share.

The problem is that raacToken.burn(shares[1])call will indeed burn amount - taxAmount, but taxAmount will be transferred to the feeCollector itself with _transfer(msg.sender, feeCollector, taxAmount);.

The burn implementation should be modified to take into account that when the Fee Collector is msg.sender, the entire amount should be burned.

Impact

The impact of this issue is medium is it prevents the FeeCollector contract to correctly burn shares[1]amount of tokens.

Tools Used

Manual review

Recommendations

Make sure to correctly handle the specific case we described to make sure the full amount is burned:

function burn(uint256 amount) external {
uint256 taxAmount = amount.percentMul(burnTaxRate);
if (taxAmount > 0 && feeCollector != address(0) && msg.sender != feeCollector) {
_burn(msg.sender, amount - taxAmount);
_transfer(msg.sender, feeCollector, taxAmount);
} else {
_burn(msg.sender, amount);
}
}

This way, we ensure that if feeCollector is the sender, the entire amount is burned as expected.

Updates

Lead Judging Commences

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

RAACToken::burn incorrectly deducts tax amount but doesn't burn or transfer it when feeCollector is address(0), preventing complete token burns

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

RAACToken::burn incorrectly deducts tax amount but doesn't burn or transfer it when feeCollector is address(0), preventing complete token burns

Support

FAQs

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

Give us feedback!