Core Contracts

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

Users can bypass the Max total Locked amount

Summary

Each users have a Max locked amount of RAAC tokens But also the entire contract has a MAX locked amount for all users the individual max locked are enforced but the maxlocked amount is not checked hence users can locked more RAAC tokens above the max lock

Vulnerability Details

This Bypass is possible in the Lock function and the Increase Function

/**
* @notice Creates a new lock position for RAAC tokens
* @dev Locks RAAC tokens for a specified duration and mints veRAAC tokens representing voting power
* @param amount The amount of RAAC tokens to lock
* @param duration The duration to lock tokens for, in seconds
*/
function lock(uint256 amount, uint256 duration) external nonReentrant whenNotPaused {
if (amount == 0) revert InvalidAmount();
@audit>> only users max >> if (amount > MAX_LOCK_AMOUNT) revert AmountExceedsLimit();
@audit >> we are check max vetooken HERE NOT MAXLOCKED>> if (totalSupply() + amount > MAX_TOTAL_SUPPLY) revert TotalSupplyLimitExceeded(); /// Bug wrong check total supply is not the same as amount // bug no check for global lock
if (duration < MIN_LOCK_DURATION || duration > MAX_LOCK_DURATION)
revert InvalidLockDuration();
// Do the transfer first - this will revert with ERC20InsufficientBalance if user doesn't have enough tokens
raacToken.safeTransferFrom(msg.sender, address(this), amount);
// Calculate unlock time
uint256 unlockTime = block.timestamp + duration;
// Create lock position
_lockState.createLock(msg.sender, amount, duration);
_updateBoostState(msg.sender, amount);
// Calculate initial voting power
(int128 bias, int128 slope) = _votingState.calculateAndUpdatePower(
msg.sender,
amount,
unlockTime
);
// Update checkpoints
uint256 newPower = uint256(uint128(bias));
_checkpointState.writeCheckpoint(msg.sender, newPower);
// Mint veTokens
_mint(msg.sender, newPower); // minting new power. add this plus total supply........Note bug
emit LockCreated(msg.sender, amount, unlockTime);
}
/**
* @notice Increases the amount of locked RAAC tokens
* @dev Adds more tokens to an existing lock without changing the unlock time
* @param amount The additional amount of RAAC tokens to lock
*/
function increase(uint256 amount) external nonReentrant whenNotPaused { // bug note total supply can be bypassed // note by pass max lock globally
// Increase lock using LockManager
_lockState.increaseLock(msg.sender, amount); // amount incremented and saved so // create max lock per psoition no one is checking global
_updateBoostState(msg.sender, locks[msg.sender].amount);
// Update voting power
LockManager.Lock memory userLock = _lockState.locks[msg.sender];
(int128 newBias, int128 newSlope) = _votingState.calculateAndUpdatePower(
msg.sender,
userLock.amount + amount, // bug amount already incremented
userLock.end
);
// Update checkpoints
uint256 newPower = uint256(uint128(newBias));
_checkpointState.writeCheckpoint(msg.sender, newPower);
// Transfer additional tokens and mint veTokens
raacToken.safeTransferFrom(msg.sender, address(this), amount);
_mint(msg.sender, newPower - balanceOf(msg.sender)); // minting more power than user actually has....... bug
emit LockIncreased(msg.sender, amount);
}

Even though a user doesn't bypass the max locked per user the Max locked for everyone can still be bypass as there are no checks to prevent this

/**
* @notice Initializes lock parameters
* @dev Sets up initial parameters for lock calculations
*/
function _initializeLockParameters() internal {
_lockState.minLockDuration = MIN_LOCK_DURATION; // 365 days
_lockState.maxLockDuration = MAX_LOCK_DURATION; // 1460 days (4 years)
@audit >> max for user
>> _lockState.maxLockAmount = MAX_LOCK_AMOUNT; // 10M
@audit >> max for contract >> _lockState.maxTotalLocked = MAX_TOTAL_LOCKED_AMOUNT; // 1B
}
/**
* @notice Maximum amount that can be locked in a single position
*/
uint256 private constant MAX_LOCK_AMOUNT = 10_000_000e18; // 10M
/**
* @notice Maximum total amount that can be locked globally
*/
uint256 public constant MAX_TOTAL_LOCKED_AMOUNT = 1_000_000_000e18; // 1B // note max lock globally

From the above if we have 1B locked already and user B calls to lock

The contract will accept his deposits instead of reverting.

Checking the locked manager also this is not enforced

/**
* @notice Creates a new lock position
* @dev Validates duration and amount, creates lock entry
* @param state The lock state storage
* @param user Address creating the lock
* @param amount Amount of tokens to lock
* @param duration Duration of the lock in seconds
* @return end The timestamp when the lock expires
*/
function createLock(
LockState storage state,
address user,
uint256 amount,
uint256 duration
) internal returns (uint256 end) {
// Validation logic remains the same
if (state.minLockDuration != 0 && state.maxLockDuration != 0) {
if (duration < state.minLockDuration || duration > state.maxLockDuration)
revert InvalidLockDuration();
}
if (amount == 0) revert InvalidLockAmount();
end = block.timestamp + duration;
state.locks[user] = Lock({
amount: amount,
end: end,
exists: true
});
state.totalLocked += amount;
emit LockCreated(user, amount, end);
return end;
}
/**
* @notice Increases the amount in an existing lock
* @dev Adds tokens to existing lock without changing duration
* @param state The lock state storage
* @param user Address increasing their lock
* @param additionalAmount Additional amount to lock
*/
function increaseLock(
LockState storage state,
address user,
uint256 additionalAmount
) internal {
Lock storage lock = state.locks[user];
if (!lock.exists) revert LockNotFound();
if (lock.end <= block.timestamp) revert LockExpired();
// Maximum lock amount
@audit>> only users max>> if (lock.amount + additionalAmount > state.maxLockAmount) revert AmountExceedsLimit();
// Maximum total locked amount
@audit>> commented out>> // if (state.totalLocked + additionalAmount > state.maxTotalLocked) revert AmountExceedsLimit();
lock.amount += additionalAmount; // seee bug updated the lock amount
state.totalLocked += additionalAmount;
emit LockIncreased(user, additionalAmount);
}

Impact

Bypass of the max locked amount for the contract

Tools Used

Manual Review

Recommendations

Nest a check in the Lock and increase lock to prevent this.

Updates

Lead Judging Commences

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