Core Contracts

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

Emergency withdrawals don't delete all user states

Summary

If the admins determine an emergency is happening, they allow users to withdraw their locked veTokens before the lock expires, but not all user states get deleted like during normal withdrawals.

Vulnerability Details

If there's an emergency and the admins allow an emergency withdrawal, users are allowed to redeem their veRAAC tokens for RAAC tokens, before the lock expires:

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];
delete _votingState.points[msg.sender];
_burn(msg.sender, currentPower);
raacToken.safeTransfer(msg.sender, amount);
emit EmergencyWithdrawn(msg.sender, amount);
}

The issue is that although the function deletes the user's _lockState and _votingState, their _checkpointState is left untouched. The checkpoints state keeps track of each user's voting power, and during normal withdrawals, it is reset:

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

Essentially, if users emergency withdraw their tokens, the function will not set a new checkpoint with 0 power and they will keep their old checkpoint with full power even though the tokens have been burned and redeemed for RAAC tokens. To give more context, veRAAC tokens are minted when users lock RAAC tokens for long periods of time. The user's _checkpointState is then updated to reflect their new power.

Impact

Users will keep their last checkpoint and ve power even during emergency withdrawals when their veRAAC gets burned.

Tools Used

Manual Review

Recommendations

During emergency withdrawals, write a new checkpoint with 0 power just like it is done (and should be done) in normal withdrawals.

Updates

Lead Judging Commences

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

veRAACToken::emergencyWithdraw doesn't update checkpoint - innacurate historical voting power, inconsistent state

Support

FAQs

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

Give us feedback!