Core Contracts

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

Stale Voting Power After Emergency Withdrawal Due to Uncleaned Checkpoints in veRAACToken Contract

Description

The veRAACToken::emergencyWithdraw function cleans voting state but fails to update checkpoints, which are used by veRAACToken::getPastVotes.
This can lead to stale voting power being reported after emergency withdrawals.

function emergencyWithdraw() external nonReentrant {
// ... validation checks ...
uint256 currentPower = balanceOf(msg.sender);
delete _lockState.locks[msg.sender];
@> delete _votingState.points[msg.sender]; // Cleans voting state
@> // Missing: _checkpointState.writeCheckpoint(msg.sender, 0);
_burn(msg.sender, currentPower);
raacToken.safeTransfer(msg.sender, amount);
}
function getPastVotes(address account, uint256 blockNumber) public view returns (uint256) {
@> return _checkpointState.getValueAt(account, blockNumber); // Uses stale checkpoint
}

Example of inconsistency:

// Initial state
User has 1000 locked tokens:
_votingState.points[user] = 1000
_checkpointState for user = 1000
// User performs emergency withdrawal
emergencyWithdraw():
_votingState.points[user] = 0 // Cleaned
_checkpointState for user = 1000 // Still shows old value!
// Query past votes
getPastVotes(user, block) = 1000 // Returns stale voting power

Risk

Likelihood: Low

  • Only occurs during emergency withdrawals

  • Emergency scenarios are rare

  • Requires specific query of past votes

Impact: Low

  • Stale voting power can be queried

  • Limited to past votes queries

Recommended Mitigation

This ensures that all voting power tracking mechanisms are properly updated during emergency withdrawals.

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

Lead Judging Commences

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