Core Contracts

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

Treasury allocations are tracked but never enforced, allowing unlimited withdrawals by managers

Relevant GitHub Links

https://github.com/Cyfrin/2025-02-raac/blob/89ccb062e2b175374d40d824263a4c0b601bcb7f/contracts/core/collectors/Treasury.sol#L64-L77

https://github.com/Cyfrin/2025-02-raac/blob/89ccb062e2b175374d40d824263a4c0b601bcb7f/contracts/core/collectors/Treasury.sol#L87-L95

Summary

The Treasury contract tracks allocation limits for managers but never enforces them during withdrawals, allowing managers to withdraw unlimited amounts regardless of their allocation.

Vulnerability Details

The Treasury contract implements an allocation system through allocateFunds() that sets spending limits for managers. However, these allocations are never checked during withdrawals:

// Treasury.sol
function withdraw(
address token,
uint256 amount,
address recipient
) external override nonReentrant onlyRole(MANAGER_ROLE) {
if (token == address(0)) revert InvalidAddress();
if (recipient == address(0)) revert InvalidRecipient();
if (_balances[token] < amount) revert InsufficientBalance();
_balances[token] -= amount;
_totalValue -= amount;
IERC20(token).transfer(recipient, amount);
emit Withdrawn(token, amount, recipient);
}

A manager with MANAGER_ROLE can withdraw any amount up to the total balance, bypassing their allocation limit entirely.

Impact

High severity as:

  • Managers can withdraw unlimited funds beyond their intended allocation limits

  • Complete bypass of the allocation system intended to limit withdrawals

  • Direct risk to protocol funds as single manager can drain treasury

Tools Used

Manual review

Recommendations

Add allocation enforcement in the withdraw function:

function withdraw(
address token,
uint256 amount,
address recipient
) external override nonReentrant onlyRole(MANAGER_ROLE) {
if (token == address(0)) revert InvalidAddress();
if (recipient == address(0)) revert InvalidRecipient();
if (_balances[token] < amount) revert InsufficientBalance();
if (_allocations[msg.sender][recipient] < amount) revert ExceedsAllocation();
_allocations[msg.sender][recipient] -= amount;
_balances[token] -= amount;
_totalValue -= amount;
IERC20(token).transfer(recipient, amount);
emit Withdrawn(token, amount, recipient);
}
Updates

Lead Judging Commences

inallhonesty Lead Judge 4 months ago
Submission Judgement Published
Invalidated
Reason: Incorrect statement

Appeal created

0xterrah Submitter
4 months ago
inallhonesty Lead Judge
4 months ago
inallhonesty Lead Judge 3 months ago
Submission Judgement Published
Invalidated
Reason: Incorrect statement

Support

FAQs

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