Core Contracts

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

veRAACToken Lock function lacks locking different locks

Summary

The lock() function in veRAACToken.sol allows users to lock their RAAC tokens to receive veTokens. However, the contract lacks proper handling of multiple locks for the same user. Instead of maintaining multiple lock records, each new lock overwrites the previous one, leading to unintended state collisions.

Vulnerability Details

When a user locks their tokens, the contract stores the lock state in _lockState. If the user locks additional tokens with a different duration, the contract does not preserve the previous lock details. Instead, the new lock entry overwrites the previous one, effectively erasing the original lock state.

This issue arises due to the createLock() function:

state.locks[user] = Lock({
amount: amount,
end: end,
exists: true
});

Since state.locks[user] is a mapping that does not support multiple entries per user, each subsequent lock replaces the previous one.

If user wants to Lock his tokens twice. for example he locked once and now has some duration. and now he wants to lock again with different amount to get a new boost and different duration. Contract lacks proper saving of states for multiple locks it saves only 1 lock if user calls lock once more the second lock rewrtites the first lock and now if user wants to increaselock amount he can call only the second one and not the first one.

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);
//Logic lacks for handling different logic amounts which leads to collision
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(msg.sender, newPower);
// Mint veTokens
emit LockCreated(msg.sender, amount, unlockTime);
}

Impact

Collision of lockstate info which may rewrite the first lockstate with the second resulting in User not being able to manipulate his first lockstate.

  1. Loss of Previous Lock State: Users who lock tokens multiple times will only retain the latest lock, losing prior locked amounts and durations.

  2. Boost Manipulation Issues: Users may be unable to properly manage boosts since previous locks are lost.

  3. Incorrect Lock Management: If a user tries to increase their lock amount later, only the most recent lock will be considered, leading to inconsistencies in locked amounts and durations.

Tools Used

Manual Review

Recommendations

To fix this issue, the contract should allow users to maintain multiple locks by implementing a nonce-based tracking system. A potential solution is:

  1. Modify the lock mapping to track multiple entries:

mapping(address => mapping(uint256 => Lock)) public locks; +
mapping(address => uint256) public lockNonce; -
//Add this
uint256 nonce = lockNonce[user]++;
state.locks[user][nonce] = Lock({
amount: amount,
end: end,
exists: true
});
Updates

Lead Judging Commences

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

Give us feedback!