Core Contracts

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

Inconsistency in user desired burn amount and actual burn amount due to incorrect `RAACToken::burn` implementation

Summary

The RAACToken::burn function incorrectly calculates the amount to be burned when the feeCollector is set to the zero address. Instead of burning the entire specified amount, the function only burns amount - taxAmount, leading to unintended behavior and leaving tokens in the user's balance that should have been removed.

Vulnerability Details

  • The burn function applies a burn tax calculated as amount.percentMul(burnTaxRate).

  • The function then burns only amount - taxAmount and, if taxAmount > 0, attempts to transfer the tax amount to feeCollector.

  • If feeCollector is set to the zero address, the tax transfer does not occur, but the deducted taxAmount is also not burned, leaving an unintended residual balance in the user's account.

Below is a PoC demonstrating the issue, with added comments for clarity:

// SPDX-License-Identifier: MIT
pragma solidity 0.8.20;
import {Test, console} from "forge-std/Test.sol";
import "@openzeppelin/contracts/token/ERC20/ERC20.sol";
import "src/core/tokens/RAACToken.sol";
contract RAACTokenIncorrectBurnLogicTest is Test {
RAACToken internal raac;
address internal minter;
address internal user;
address internal feeCollector;
function setUp() public {
address initialOwner = address(this);
uint256 initialSwapTaxRate = 0;
uint256 initialBurnTaxRate = 0; // 0.5% for default
raac = new RAACToken(initialOwner, initialSwapTaxRate, initialBurnTaxRate);
minter = makeAddr("minter");
feeCollector = address(0); // Setting feeCollector to zero address
user = makeAddr("user");
raac.setMinter(minter);
raac.setFeeCollector(feeCollector);
// Mint tokens to the user
vm.prank(minter);
raac.mint(user, 100e18);
}
function testExploit() public {
// User attempts to burn 100 RAAC tokens
vm.prank(user);
raac.burn(100e18);
// Due to incorrect logic, only `amount - taxAmount` is burned,
// where taxAmount = 100e18 * 0.5% = 0.5e18.
// The user is left with 50e18 tokens instead of zero.
console.log("User's RAAC balance after burn:", raac.balanceOf(user)); // Expected: 0, Actual: 50e18
}
}

Code Reference: https://github.com/Cyfrin/2025-02-raac/blob/89ccb062e2b175374d40d824263a4c0b601bcb7f/contracts/core/tokens/RAACToken.sol#L80

Impact

Incorrect Token Burning: Users attempting to burn their entire balance may still have leftover tokens due to tax miscalculations.

Tools Used

Manual Review

Recommendations

To fix the issue, modify the burn function to ensure that all tokens are properly burned, even if the feeCollector is the zero address. The updated function should look like this:

This ensures:

  1. If feeCollector is not the zero address, the tax is transferred correctly.

  2. If feeCollector is the zero address, the entire amount is burned.

By implementing this fix, the contract will maintain its expected behavior and properly handle token burning in all cases.

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

Lead Judging Commences

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