Core Contracts

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

Locked Funds in Treasury Due to Emergency Withdrawals in FeeCollector.sol

Summary

The FeeCollector.sol contract contains an emergencyWithdraw() function that transfers assets to the Treasury contract. However, the Treasury contract lacks a mechanism for withdrawing directly received funds, resulting in the risk of permanently locking funds within the treasury.

Vulnerability Details

The vulnerability arises from the following code in FeeCollector.sol:

function emergencyWithdraw(address token) external override whenPaused {
if (!hasRole(EMERGENCY_ROLE, msg.sender)) revert UnauthorizedCaller();
if (token == address(0)) revert InvalidAddress();
uint256 balance;
if (token == address(raacToken)) {
balance = raacToken.balanceOf(address(this));
raacToken.safeTransfer(treasury, balance);
} else {
balance = IERC20(token).balanceOf(address(this));
SafeERC20.safeTransfer(IERC20(token), treasury, balance);
}
emit EmergencyWithdrawal(token, balance);
}

The FeeCollector contract assumes that the Treasury contract has a method to retrieve these funds, but Treasury.sol does not provide a function to withdraw arbitrary ERC-20 tokens received via transfer. Instead, it tracks balances based on deposits made through its deposit() function, making it impossible to withdraw assets that were transferred directly.

Impact

This vulnerability can result in funds being permanently locked within the Treasury contract. Since the treasury contract does not provide a generic mechanism to withdraw directly received funds, any assets transferred via emergencyWithdraw() will be unrecoverable.

Tools Used

  • Manual Code Review

Recommendations

To mitigate this issue, consider the following approaches:

  1. Implement a rescueFunds() Function in Treasury

    • Introduce a function that allows an admin or manager to withdraw any ERC-20 token balance held by the treasury.

    function rescueFunds(address token, uint256 amount, address recipient) external onlyRole(MANAGER_ROLE) {
    require(recipient != address(0), "Invalid recipient");
    require(amount > 0, "Invalid amount");
    require(IERC20(token).balanceOf(address(this)) >= amount, "Insufficient balance");
    SafeERC20.safeTransfer(IERC20(token), recipient, amount);
    }
  2. Modify emergencyWithdraw() to Transfer to a Retrievable Address

    • Instead of transferring to the Treasury contract, send funds to a more flexible address (e.g., a multisig wallet) that has a withdrawal function.

  3. Modify Treasury to Track Transfers Accurately

    • Update the treasury’s balance tracking system to include tokens received via direct transfers and allow withdrawals accordingly.

By implementing these recommendations, the protocol can ensure that emergency withdrawals remain functional without the risk of funds becoming irretrievable.

Updates

Lead Judging Commences

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

FeeCollector::_processDistributions and emergencyWithdraw directly transfer funds to Treasury where they get permanently stuck

Support

FAQs

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