Core Contracts

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

Missing _checkpointState.writeCheckpoint Update in emergencyWithdraw Function

Summary

emergencyWithdraw allows a user to withdraw their locked tokens in an emergency scenario after a delay emergencyWithdrawDelay. It deletes the user’s lock _lockState.locks[msg.sender] and voting power state _votingState.points[msg.sender], burns their veTokens (currentPower), and transfers the locked amount back to the user. However, it does not call _checkpointState.writeCheckpoint(msg.sender, 0) to update the user’s checkpoint history, which tracks their voting power over time. This omission leaves the checkpoint state inconsistent with the user’s actual voting power (now zero) after the withdrawal.

Vulnerability Details

  • Omission of _checkpointState.writeCheckpoint: After burning the user’s veTokens (_burn(msg.sender, currentPower)), reducing their voting power to zero, the function does not record this change in the checkpoint state via _checkpointState.writeCheckpoint(msg.sender, 0).

  • Inconsistent State Management: Other functions like lock, increase, extend and withdraw call writeCheckpoint whenever voting power changes (e.g., minting or burning veTokens), but emergencyWithdraw skips this step despite nullifying the user’s power.

Impact

Outdated Checkpoint History:

  • The user’s last checkpoint (e.g., from a prior lock or extend) remains in _checkpointState.userCheckpoints[msg.sender], showing a non-zero voting power (e.g., currentPower) even though their actual power is now zero after _burn.

  • getPastVotingPower and getPastVotes rely on userCheckpoints to return historical voting power at a given blockNumber. Without an updated checkpoint at the withdrawal block, these functions return the previous power value for blocks after the withdrawal which could result to misinformation .

Tools Used

manual review

Recommendations

Add _checkpointState.writeCheckpoint:

  • Insert _checkpointState.writeCheckpoint(msg.sender, 0) after _burn to record the power dropping to zero:

    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);
    + _checkpointState.writeCheckpoint(msg.sender, 0); // Fix
    raacToken.safeTransfer(msg.sender, amount);
    emit EmergencyWithdrawn(msg.sender, amount);
    }
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!