Core Contracts

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

If the deposit token in Treasury contract is RAACToken, the withdraw function may revert

Summary

The Treasury contract allows users to deposit any ERC20 token, including the RAACToken. However, the RAACToken contract imposes a swap tax and burn tax during token transfers, which reduces the actual amount received by the Treasury contract. This discrepancy between the recorded balance (_balances[token]) and the actual token balance can lead to insufficient funds when attempting to withdraw tokens. This vulnerability arises because the deposit function does not account for the tax mechanism of RAACToken, resulting in an overestimation of the treasury's token balance.

Vulnerability Details

The deposit function in the Treasury contract assumes that the full amount of tokens specified by the user will be transferred to the treasury.

function deposit(address token, uint256 amount) external override nonReentrant {
if (token == address(0)) revert InvalidAddress();
if (amount == 0) revert InvalidAmount();
IERC20(token).transferFrom(msg.sender, address(this), amount);
_balances[token] += amount;
_totalValue += amount;
emit Deposited(token, amount);
}

However, when depositing RAACToken, the _update function in the RAACToken contract applies a swap tax and burn tax, reducing the actual amount received by the treasury.

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

This creates a mismatch between the recorded balance (_balances[token]) and the actual token balance. When a manager attempts to withdraw tokens using the withdraw function, the contract may fail due to insufficient funds, even if the recorded balance (_balances[token]) suggests otherwise.

Impact

  • Incorrect Balance Tracking: The _balances[token] variable in the Treasury contract overestimates the actual token balance.

  • Withdrawal Failure: When a manager attempts to withdraw tokens using the withdraw function, the contract may fail due to insufficient funds, even if the recorded balance (_balances[token]) suggests otherwise.

  • Protocol Instability: This issue can lead to unexpected behavior in the protocol, such as failed transactions or incorrect accounting, undermining user trust.

The impact is Medium, the likelihood is Medium, so the severity is Medium.

Tools Used

Manual Review

Recommendations

Consider following fix:

function deposit(address token, uint256 amount) external override nonReentrant {
if (token == address(0)) revert InvalidAddress();
if (amount == 0) revert InvalidAmount();
uint256 balanceBefore = IERC20(token).balanceOf(address(this));
IERC20(token).transferFrom(msg.sender, address(this), amount);
uint256 balanceAfter = IERC20(token).balanceOf(address(this));
uint256 actualAmount = balanceAfter - balanceBefore;
_balances[token] += actualAmount;
_totalValue += actualAmount;
emit Deposited(token, actualAmount);
}
Updates

Lead Judging Commences

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

Treasury::deposit increments _balances[token] with amount, not taking FoT or rebasing into account

Support

FAQs

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