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:
Check if the treasury holds enough tokens to honor allocations
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
Manual code review (static analysis)
Recommendations
Verify sufficient balance of the contract when allocating funds + track allocations
mapping(address => uint256) private _totalAllocated;
mapping(address => mapping(address => mapping(address => uint256))) private _allocations;
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();
uint256 previousAmount = _allocations[token][msg.sender][recipient];
uint256 available = _balances[token] - _totalAllocated[token];
if (available + previousAmount < amount) {
revert InsufficientBalance();
}
_totalAllocated[token] = _totalAllocated[token] - previousAmount + amount;
_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();
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);
}