Core Contracts

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

Incorrect Tax Deduction Logic in `RAACToken::burn` Function

Summary

The burn function in the RAACToken contract incorrectly deducts the tax fee during the transfer to the fee collector, triggering an additional tax deduction. As a result, the actual fee transferred to the feeCollector is less than expected, causing a loss in protocol revenue.

Vulnerability Details

The issue arises in the RAACToken::burn function, where the tax fee is calculated and transferred to the feeCollector. The tax fee transfer triggers a secondary tax deduction in the _transfer function. This behavior leads to the feeCollector receiving less than the intended tax amount.

/**
* @dev Burns tokens from the caller's balance
* @param amount The amount of tokens to burn
*/
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);
}
}

The core issue is that when the burn function is called, the tax fee is calculated and transferred to the feeCollector. However, the _transfer function calls the _update function, which contains additional tax logic:

/**
* @dev Internal function to update balances and apply taxes (overrides ERC20's _update)
* @param from The address to transfer from
* @param to The address to transfer to
* @param amount The amount to transfer
*/
function _update(
address from,
address to,
uint256 amount
) internal virtual override {
uint256 baseTax = swapTaxRate + burnTaxRate;
// Skip tax for whitelisted addresses or when fee collector disabled
if (
baseTax == 0 ||
from == address(0) ||
to == address(0) ||
whitelistAddress[from] ||
whitelistAddress[to] ||
feeCollector == address(0)
) {
super._update(from, to, amount);
return;
}
// All other cases where tax is applied
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);
}

In this logic, part of the tax amount (the burnAmount) is transferred to address(0) for burning, which reduces the actual fee amount that is transferred to the feeCollector.

Mathematical Example

Let’s assume the following:

  • burnTaxRate = 50 (0.5%)

  • swapTaxRate = 100 (1%)

  • User burns 1000 RAAC tokens

The tax calculation would be:

taxAmount = 1000 * 0.5% = 5 RAAC
Actual transfer amount = 5 - (5 * 0.5%) = 4.975 RAAC
Protocol loss = 0.025 RAAC (which is 0.5% of the expected tax amount)

Impact

Direct loss: Every burn operation results in a tax revenue reduction of 0.5%.

Tools Used

Manual

Updates

Lead Judging Commences

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

RAACToken::burn applies burn tax twice when transferring to feeCollector, causing excess tokens to be burned and reduced fees to be collected

This is by design, sponsor's words: Yes, burnt amount, done by whitelisted contract or not always occur the tax. The feeCollector is intended to always be whitelisted and the address(0) is included in the _transfer as a bypass of the tax amount, so upon burn->_burn->_update it would have not applied (and would also do another burn...). For this reason, to always apply such tax, the burn function include the calculation (the 2 lines that applies) and a direct transfer to feeCollector a little bit later. This is done purposefully

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

RAACToken::burn applies burn tax twice when transferring to feeCollector, causing excess tokens to be burned and reduced fees to be collected

This is by design, sponsor's words: Yes, burnt amount, done by whitelisted contract or not always occur the tax. The feeCollector is intended to always be whitelisted and the address(0) is included in the _transfer as a bypass of the tax amount, so upon burn->_burn->_update it would have not applied (and would also do another burn...). For this reason, to always apply such tax, the burn function include the calculation (the 2 lines that applies) and a direct transfer to feeCollector a little bit later. This is done purposefully

Appeal created

inallhonesty Lead Judge 6 months ago
Submission Judgement Published
Invalidated
Reason: Non-acceptable severity
Assigned finding tags:

RAACToken::burn applies burn tax twice when transferring to feeCollector, causing excess tokens to be burned and reduced fees to be collected

This is by design, sponsor's words: Yes, burnt amount, done by whitelisted contract or not always occur the tax. The feeCollector is intended to always be whitelisted and the address(0) is included in the _transfer as a bypass of the tax amount, so upon burn->_burn->_update it would have not applied (and would also do another burn...). For this reason, to always apply such tax, the burn function include the calculation (the 2 lines that applies) and a direct transfer to feeCollector a little bit later. This is done purposefully

Support

FAQs

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

Give us feedback!