Core Contracts

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

Locking new tokens overwrite previous user states

Summary

The lock function in the veRAACToken contract is designed to allow users to lock RAAC tokens and receive veRAAC tokens representing voting power. However, there is a critical issue where a user can call the lock function multiple times, overwriting their existing lock while still receiving new veRAAC tokens. This leads to a situation where users have veRAAC tokens but cannot withdraw their locked RAAC tokens.

Vulnerability Details

When a user calls lock, the function overwrites their existing lock state without checking if they already have an active lock. Since the previous lock is overwritten, userLock.amount will only reflect the most recent lock, effectively deleting the earlier locked amount.

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
});

Withdraw only checks locked amount not token balanca. Because of this even though the user has veRAAC tokens minted from multiple locks, they cannot withdraw their previously locked RAAC tokens, leading to a loss of funds.

function withdraw() external nonReentrant {
LockManager.Lock memory userLock = _lockState.locks[msg.sender];
if (userLock.amount == 0) revert LockNotFound();
if (block.timestamp < userLock.end) revert LockNotExpired();
uint256 amount = userLock.amount;
uint256 currentPower = balanceOf(msg.sender);
// Clear lock data
delete _lockState.locks[msg.sender];
delete _votingState.points[msg.sender];
// Burn veTokens and transfer RAAC
_burn(msg.sender, currentPower);
raacToken.safeTransfer(msg.sender, amount);
}

Impact

Users lose access to previously locked RAAC tokens if they lock new tokens before withdrawing their previous lock

Tools Used

Manual

Recommendations

Before processing a new lock, the function should check whether the user already has an active lock and revert if they do.

if (_lockState.locks[msg.sender].exists) revert ActiveLockExists();
Updates

Lead Judging Commences

inallhonesty Lead Judge about 1 month 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.