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 {
uint256 currentPower = balanceOf(msg.sender);
delete _lockState.locks[msg.sender];
@> delete _votingState.points[msg.sender];
@>
_burn(msg.sender, currentPower);
raacToken.safeTransfer(msg.sender, amount);
}
function getPastVotes(address account, uint256 blockNumber) public view returns (uint256) {
@> return _checkpointState.getValueAt(account, blockNumber);
}
Example of inconsistency:
User has 1000 locked tokens:
_votingState.points[user] = 1000
_checkpointState for user = 1000
emergencyWithdraw():
_votingState.points[user] = 0
_checkpointState for user = 1000
getPastVotes(user, block) = 1000
Risk
Likelihood: Low
Only occurs during emergency withdrawals
Emergency scenarios are rare
Requires specific query of past votes
Impact: Low
Recommended Mitigation
This ensures that all voting power tracking mechanisms are properly updated during emergency withdrawals.
function emergencyWithdraw() external nonReentrant {
uint256 currentPower = balanceOf(msg.sender);
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);
}