Core Contracts

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

`withdraw` and `emergencyWithdraw` functions in veRAACToken contract don't update `_lockState.totalLocked`, breaking core accounting for locks.

Summary

withdraw and emergencyWithdraw functions in veRAACToken contract are defined as follows:

function emergencyWithdraw() external nonReentrant {
if (emergencyWithdrawDelay == 0 || block.timestamp < emergencyWithdrawDelay) {
revert EmergencyWithdrawNotEnabled();
}
LockManager.Lock memory userLock = _lockState.locks[msg.sender];
if (userLock.amount == 0) revert NoTokensLocked();
uint256 amount = userLock.amount;
uint256 currentPower = balanceOf(msg.sender);
delete _lockState.locks[msg.sender];
// @audit HIGH: should also reduce _lockState.totalLocked
delete _votingState.points[msg.sender];
_burn(msg.sender, currentPower);
raacToken.safeTransfer(msg.sender, amount);
emit EmergencyWithdrawn(msg.sender, amount);
}
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];
// @audit HIGH: should also reduce _lockState.totalLocked
delete _votingState.points[msg.sender];
// Update checkpoints
_checkpointState.writeCheckpoint(msg.sender, 0);
// Burn veTokens and transfer RAAC
_burn(msg.sender, currentPower);
raacToken.safeTransfer(msg.sender, amount);
emit Withdrawn(msg.sender, amount);
}

We can see that when a withdrawal occurs, the user's lock is deleted with delete _lockState.locks[msg.sender];. The problem arises because _lockState.totalLocked is not decremented by the withdrawn amount.

Vulnerability Details

totalLocked field value will be systematically over-estimated. The more time passes, the more it will be over-estimated.

Because totalLocked is never decremented during withdrawals, _boostState will be wrong: indeed, internal function _updateBoostState executes the following line:

_boostState.totalWeight = _lockState.totalLocked;

The will induce an incorrect computation of boosts.

Impact

The impact of this issue can be considered as high as it will lead to wrong calculation of boost for users. Also, core internal accounting for locks is broken.

Tools Used

Manual review.

Recommendations

Make sure to update _lockState.totalLocked in withdraw and emergencyWithdraw functions.

Updates

Lead Judging Commences

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

veRAACToken::withdraw / emergencyWithdraw doesn't substract the `_lockState.totalLocked`

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

veRAACToken::withdraw / emergencyWithdraw doesn't substract the `_lockState.totalLocked`

Support

FAQs

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