Core Contracts

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

Missing Total Locked Amount Validation in veRAACToken

Author Revealed upon completion

Missing Total Locked Amount Validation in veRAACToken

Description

The veRAACToken contract has a critical oversight in its lock() function where it fails to validate that the new lock amount won't exceed MAX_TOTAL_LOCKED_AMOUNT. While the contract correctly initializes this limit in _initializeLockParameters() as 1B tokens, the lock() function only checks the individual lock amount limit (MAX_LOCK_AMOUNT) but not the total locked amount limit.

function lock(uint256 amount, uint256 duration) external nonReentrant whenNotPaused {
if (amount == 0) revert InvalidAmount();
if (amount > MAX_LOCK_AMOUNT) revert AmountExceedsLimit();
if (totalSupply() + amount > MAX_TOTAL_SUPPLY) revert TotalSupplyLimitExceeded();
if (duration < MIN_LOCK_DURATION || duration > MAX_LOCK_DURATION)
revert InvalidLockDuration();
// MISSING CHECK: Should verify _lockState.totalLocked + amount <= MAX_TOTAL_LOCKED_AMOUNT
raacToken.safeTransferFrom(msg.sender, address(this), amount);

Impact

  • Protocol's total lock limit can be exceeded

  • Economic model assumptions about maximum locked tokens could be violated

  • Could lead to unexpected behavior in boost calculations if total exceeds expected maximum

  • Protocol governance could be affected if voting power calculations assume a maximum total locked amount

Proof of Concept

The following test demonstrates how the total locked amount limit can be exceeded:

function testExceedTotalLockedLimit() public {
uint256 MAX_TOTAL_LOCKED = 1_000_000_000e18; // 1B tokens
// Setup multiple users with large token amounts
address[] memory users = new address[](3);
for(uint i = 0; i < 3; i++) {
users[i] = address(uint160(i + 1));
deal(address(raacToken), users[i], 400_000_000e18);
vm.startPrank(users[i]);
raacToken.approve(address(veRAACToken), 400_000_000e18);
// Each user locks 400M tokens
veRAACToken.lock(400_000_000e18, 365 days);
vm.stopPrank();
}
// Total locked is now 1.2B, exceeding MAX_TOTAL_LOCKED_AMOUNT of 1B
assertEq(_lockState.totalLocked, 1_200_000_000e18);
// This should fail: assert(_lockState.totalLocked <= MAX_TOTAL_LOCKED);
}
function testBoostCalculationWithExceededTotal() public {
// Setup state where total locked exceeds max
testExceedTotalLockedLimit();
// New user tries to get boost calculation
address newUser = address(0x999);
deal(address(raacToken), newUser, 1000e18);
vm.startPrank(newUser);
raacToken.approve(address(veRAACToken), 1000e18);
veRAACToken.lock(1000e18, 365 days);
// Boost calculations may be incorrect due to exceeded total
(uint256 boost, uint256 boostedAmount) = veRAACToken.calculateBoost(newUser, 1000e18);
// Boost might be unexpectedly low due to inflated total
vm.stopPrank();
}

Fix Recommendation

Add total locked amount validation in the lock function:

function lock(uint256 amount, uint256 duration) external nonReentrant whenNotPaused {
if (amount == 0) revert InvalidAmount();
if (amount > MAX_LOCK_AMOUNT) revert AmountExceedsLimit();
if (totalSupply() + amount > MAX_TOTAL_SUPPLY) revert TotalSupplyLimitExceeded();
if (duration < MIN_LOCK_DURATION || duration > MAX_LOCK_DURATION)
revert InvalidLockDuration();
// Add check for total locked amount
if (_lockState.totalLocked + amount > MAX_TOTAL_LOCKED_AMOUNT)
revert TotalLockedExceedsLimit();
raacToken.safeTransferFrom(msg.sender, address(this), amount);
// Rest of the function...
}
// Add new error
error TotalLockedExceedsLimit();

The fix should also be applied to the increase() function since it also increases the total locked amount:

function increase(uint256 amount) external nonReentrant whenNotPaused {
// Add check for total locked amount
if (_lockState.totalLocked + amount > MAX_TOTAL_LOCKED_AMOUNT)
revert TotalLockedExceedsLimit();
// Rest of function...
}

Tools Used

  • Manual Review

  • Foundry Testing Framework

  • Static Analysis

Updates

Lead Judging Commences

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

veRAACToken::increase doesn't check the maximum total locked amount

`veRAACToken::lock` function doesn't check MAX_TOTAL_LOCKED_AMOUNT

Support

FAQs

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