Core Contracts

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

Issues with allocation

Summary

Issue with allocation

Vulnerability Details

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);
}
/**
* @notice Allocates funds to a recipient
* @dev Only callable by accounts with ALLOCATOR_ROLE
* Records allocation without transferring tokens
* @param recipient Address to allocate funds to
* @param amount Amount of funds to allocate
*/
function allocateFunds(
address recipient,
uint256 amount
) external override onlyRole(ALLOCATOR_ROLE) {
if (recipient == address(0)) revert InvalidRecipient();
if (amount == 0) revert InvalidAmount();
_allocations[msg.sender][recipient] = amount;
emit FundsAllocated(recipient, amount);
}

function allocateFunds(address recipient, uint256 amount) {

_allocations[msg.sender][recipient] = amount;

}

function withdraw(address token, uint256 amount, address recipient) {

if (_balances[token] < amount) revert InsufficientBalance();
// No check against allocations

}

// Example:

allocateFunds(recipient1, 1000); // Allocator A

allocateFunds(recipient2, 1000); // Allocator B

// Total allocated: 2000

// Actual balance might be: 500

// System allows overallocation

// State variables
mapping(address => uint256) private _balances; // Token balances
mapping(address => mapping(address => uint256)) private _allocations; // Allocator => recipient => amount
uint256 private _totalValue; // Total value across all tokens

Example:

// Treasury has 1000 USDC
_balances[USDC] = 1000

// No allocations yet

_allocations[allocatorA][recipient1] = 0

_allocations[allocatorB][recipient2] = 0

// Allocator A promises 800 USDC to recipient1
allocateFunds(recipient1, 800);
_allocations[allocatorA][recipient1] = 800

// Problem #1: Function doesn't specify which token is being allocated

// Problem #2: No check if Treasury has 800 of any token available

// Allocator B promises 500 USDC to recipient2
allocateFunds(recipient2, 500);
_allocations[allocatorB][recipient2] = 500

// Total allocated: 800 + 500 = 1300
// Actual balance: 1000 USDC

// Problem #3: System allowed allocations exceeding available funds

// Recipient1 tries to withdraw their 800 allocation
withdraw(USDC, 800, recipient1);

// Only checks balance, not allocations
if (_balances[USDC] = totalAllocated + amount, "Insufficient funds");
_allocations[msg.sender][recipient] = amount;
}

// Current withdraw function

function withdraw(address token, uint256 amount, address recipient) {

if (_balances[token] = amount, "Exceeds allocation");

_allocations[msg.sender][recipient] -= amount;

}

Impact

Multiple allocators can exceed available funds. Allocations don't specify token type. No validation against available token balances.

Tools Used

Foundry

Recommendations

This has been expressed above

Updates

Lead Judging Commences

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