Core Contracts

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

Missing Checkpoint Update in emergencyWithdraw Enables Voting Power Manipulation

Summary

The emergencyWithdraw function in veRAACToken fails to update the user's checkpoint when tokens are withdrawn, allowing users to retain their voting power in governance decisions even after withdrawing their tokens. This creates a potential for vote manipulation through a double-voting scenario.

Vulnerability Details

The veRAACToken contract uses a checkpoint system to track users' voting power over time, which is critical for governance decisions. The emergencyWithdraw function currently has these operations:

function emergencyWithdraw() external nonReentrant {
// ... emergency delay checks ...
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];
//@audit-issue Missing: _checkpointState.writeCheckpoint(msg.sender, 0);
_burn(msg.sender, currentPower);
raacToken.safeTransfer(msg.sender, amount);
emit EmergencyWithdrawn(msg.sender, amount);
}

The function correctly:

  1. Deletes the user's lock state

  2. Deletes the voting power points

  3. Burns the user's veRAACToken balance

  4. Returns the original RAAC tokens

However, it fails to update the user's checkpoint through _checkpointState.writeCheckpoint(msg.sender, 0). This is problematic because the Governance contract uses getPastVotes() (currently, it doesn't, but this is a bug reported in another submission) which reads from these checkpoints to determine voting power at specific blocks.

Impact

  • Users can perform an emergency withdrawal and still maintain voting power in governance decisions through their last checkpoint

  • A malicious user could execute an emergency withdrawal, vote on a proposal, then deposit again to effectively double their voting influence

Tools Used

Manual Review

Recommendations

Add the missing checkpoint update in the emergencyWithdraw function:

function emergencyWithdraw() external nonReentrant {
// ... existing code ...
delete _lockState.locks[msg.sender];
delete _votingState.points[msg.sender];
+ _checkpointState.writeCheckpoint(msg.sender, 0);
_burn(msg.sender, currentPower);
raacToken.safeTransfer(msg.sender, amount);
emit EmergencyWithdrawn(msg.sender, amount);
}

This ensures that the user's voting power is properly zeroed out in the checkpoint history when they withdraw their tokens, preventing any potential voting power manipulation.

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!