Core Contracts

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

User will loose all his locked raacTokens if he increases locked amount via lock() instead of increase()

Description

when a user calls veRAACToken.lock(), a brand new LockState.locks is created for a user via _lockState.createLock()

// https://github.com/Cyfrin/2025-02-raac/blob/89ccb062e2b175374d40d824263a4c0b601bcb7f/contracts/core/tokens/veRAACToken.sol#L226
function lock(uint256 amount, uint256 duration) external nonReentrant whenNotPaused {
...
_lockState.createLock(msg.sender, amount, duration);
...
}
// https://github.com/Cyfrin/2025-02-raac/blob/89ccb062e2b175374d40d824263a4c0b601bcb7f/contracts/libraries/governance/LockManager.sol#L133-L137
function createLock(&args) internal returns (uint256 end) {
state.locks[user] = Lock({
amount: amount,
end: end,
exists: true
});
}

The problem arises when a user had previously locked huge amounts of raacTokens and tries to increase lockamounts via lock() itself rather than increase(). By doing so. all his previously locked raacTokens are overriden with the brand new lock data and there is no way to retrieve previous raacTokens

Imagine this scenario:

  • User locks 1 Million raacTokens by calling lock()

  • now _lockState.locks[user].amount == 1000000 * decimals

  • User increases 1 more raacToken, not by increase() but by lock() itself

  • A new call to _lockState.createLock() resets the previous user lockState

  • now _lockState.locks[user].amount == 1 * decimals

  • RESULT => USER LOST 1 MILLION RAAC TOKENS AND CAN'T RECOVER A DIME

Recommendations

function lock(uint256 amount, uint256 duration) external nonReentrant whenNotPaused {
+ if (_lockState.locks[msg.sender].amount > 0 ) {
+ increase(amount);
+ } else {
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
...
...
...
+ }
}
- function increase(uint256 amount) external nonReentrant whenNotPaused {
+ function increase(uint256 amount) public nonReentrant whenNotPaused {
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!