Core Contracts

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

Missing check if Treasury hold enough funds for allocations

Author Revealed upon completion

Summary

Scope: contracts/core/collectors/Treasury.sol

The allocateFunds function allows allocators to assign arbitrary fund amounts to recipients without verifying if the treasury holds sufficient tokens. This creates a risk of unrealistic allocations that cannot be fulfilled during actual withdrawals.

Vulnerability Details


Function: allocateFunds(address recipient, uint256 amount)

The allocation system tracks amounts in a generic _allocations mapping but does not:

  1. Check if the treasury holds enough tokens to honor allocations

  2. Reserve allocated funds to prevent double-spending

Impact

  • Creates systemic risk of unbacked allocations.

  • Recipients may act on false assumptions (e.g., initiate investments) based on invalid allocations.

  • Withdrawals tied to allocations will fail when treasury lacks sufficient funds, disrupting operations.

  • Undermines trust in the protocol's financial reporting.

Tools Used

  1. Manual code review (static analysis)

Recommendations

Verify sufficient balance of the contract when allocating funds + track allocations

// Add token-specific allocation tracking
mapping(address => uint256) private _totalAllocated; // Token => Total allocated amount
mapping(address => mapping(address => mapping(address => uint256))) private _allocations; // Token => Allocator => Recipient => Amount
// Update event to include token
event FundsAllocated(address indexed token, address indexed recipient, uint256 amount);
function allocateFunds(
address token,
address recipient,
uint256 amount
) external override onlyRole(ALLOCATOR_ROLE) {
if (token == address(0)) revert InvalidAddress();
if (recipient == address(0)) revert InvalidRecipient();
if (amount == 0) revert InvalidAmount();
// Get previous allocation for this token/allocator/recipient
uint256 previousAmount = _allocations[token][msg.sender][recipient];
// Calculate available balance (current balance - total allocated)
uint256 available = _balances[token] - _totalAllocated[token];
// Ensure new allocation doesn't exceed available + previous allocation
if (available + previousAmount < amount) {
revert InsufficientBalance();
}
// Update total allocated (remove old, add new)
_totalAllocated[token] = _totalAllocated[token] - previousAmount + amount;
// Store new allocation
_allocations[token][msg.sender][recipient] = amount;
emit FundsAllocated(token, recipient, amount);
}
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 (amount == 0) revert InvalidAmount();
// Check against available balance (total - allocated)
uint256 available = _balances[token] - _totalAllocated[token];
if (available < amount) revert InsufficientBalance();
_balances[token] -= amount;
_totalValue -= amount;
IERC20(token).transfer(recipient, amount);
emit Withdrawn(token, amount, recipient);
}
Updates

Lead Judging Commences

inallhonesty Lead Judge 8 days ago
Submission Judgement Published
Validated
Assigned finding tags:

Treasury::allocateFunds doesn't say what token you are actually allocating, doesn't check balances, or existing allocations to other recipients

Support

FAQs

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