Core Contracts

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

[H] Overriding Existing Lock in `lock` Function in `veRAACToken`

Summary

The lock function in the veRAACToken contract does not check if the user already has an existing lock. It calls the LockManager contract to create a new lock, which overrides the user's previous lock state with the new state.

Vulnerability Details

The current lock function in the veRAACToken contract is:

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();
...
_lockState.createLock(msg.sender, amount, duration);
_updateBoostState(msg.sender, amount);
...
// Mint veTokens
_mint(msg.sender, newPower);
emit LockCreated(msg.sender, amount, unlockTime);
}

The function does not check if the user already has an existing lock, leading to the user's previous lock state being overridden. Because of that there is no protocol breakdown but when the user withdraws or votes the voting power is determined by the user.lockAmount and totalPower is the TotalSupply which affects the voting and at withdrawls the user will burn all his tokens for the lastCreated LockState amount.

Links:

  1. https://github.com/Cyfrin/2025-02-raac/blob/89ccb062e2b175374d40d824263a4c0b601bcb7f/contracts/core/tokens/veRAACToken.sol#L226

Impact

This bug can lead to the loss of the user's previous lock state, potentially causing issues with token accounting and user balances within the protocol.

Tools Used

Manual code review.

Recommendations

Update the lock function to check if the user already has an existing lock before creating a new one. The corrected function should be:

function lock(uint256 amount, uint256 duration) external nonReentrant whenNotPaused {
if (amount == 0) revert InvalidAmount();
// @audit why are we not using lockState constraints here
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();
// Check if the user already has an existing lock
if (_lockState.locks[msg.sender].exists) revert LockAlreadyExists();
...
_lockState.createLock(msg.sender, amount, duration);
_updateBoostState(msg.sender, amount);
...
// Mint veTokens
_mint(msg.sender, newPower);
emit LockCreated(msg.sender, amount, unlockTime);
}

This ensures that the user's previous lock state is not overridden, leading to accurate token accounting and user balances.

Updates

Lead Judging Commences

inallhonesty Lead Judge 4 months ago
Submission Judgement Published
Validated
Assigned finding tags:

veRAACToken::lock called multiple times, by the same user, leads to loss of funds

Support

FAQs

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