Core Contracts

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

Loss of User RAAC Tokens Through Lock Position Overwrite in veRAACToken::lock function

Description

The veRAACToken contract allows users to lock RAAC tokens to receive voting power. When a user who already has a locked position calls veRAACToken::lock function again, their previous position is overwritten without returning their tokens, leading to permanent loss of funds.

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();
// 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);
emit LockCreated(msg.sender, amount, unlockTime);
}

Furthermore LockManager::createLock function called by veRAACToken::lock has no check for existing lock position:

function createLock(
LockState storage state,
address user,
uint256 amount,
uint256 duration
) internal returns (uint256 end) {
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;
@> // Completely overwrites any existing lock
@> state.locks[user] = Lock({
@> amount: amount, // New amount overwrites old amount
@> end: end, // New end overwrites old end
@> exists: true
@> });
@> state.totalLocked += amount; // Adds to total without subtracting old amount
emit LockCreated(user, amount, end);
return end;
}

Risk

Likelihood: Medium

  • Users might attempt to create multiple locks

Impact: High

  • Users permanently lose their locked RAAC tokens

  • Protocol's totalLocked accounting becomes incorrect

  • Voting power calculations are corrupted

Recommended Mitigation

The protocol should add a separate function for increasing existing locks and preventing overwriting existing locks in veRAACToken::lock function

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.