Core Contracts

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

The `slope` is missing from veRaac checkpoints. The protocol is unable to calculate the correct voting power from checkpoints and voting will be unfair

Summary

VeRaac creates checkpoints each time a user calls lock, increase or extend. Only the timestamp and bias is stored. Since the slope is missing from these checkpoints, protocol is unable to retrieve the votingPower for a given timestamp from them.

Vulnerability Details

Each time lock/increase/extend is called, calculateAndUpdatePower is resposible to save the latest timestamp, bias, and slope in _votingState.points[user] variable.

function calculateAndUpdatePower(
VotingPowerState storage state,
address user,
uint256 amount,
uint256 unlockTime
) internal returns (int128 bias, int128 slope) {
...
// Calculate initial voting power that will decay linearly to 0 at unlock time
uint256 duration = unlockTime - block.timestamp;
uint256 initialPower = (amount * duration) / MAX_LOCK_DURATION; // Normalize by max duration
bias = int128(int256(initialPower));
slope = int128(int256(initialPower / duration)); // Power per second decay
uint256 oldPower = getCurrentPower(state, user, block.timestamp);
@> state.points[user] = RAACVoting.Point({
bias: bias,
slope: slope,
timestamp: block.timestamp
});
_updateSlopeChanges(state, unlockTime, 0, slope);
emit VotingPowerUpdated(user, oldPower, uint256(uint128(bias)));
return (bias, slope);
}

The bias and slope can be defined as:

  • Bias: The initial voting power at the time of locking.

  • Slope: The rate at which voting power decreases per second.

    The voting power for a voting-escrow system decreases linearly and can be defined by :

    votingPower = bias − slope × timePassed

The lock/ increase/ extend functions are also creating a checkpoint, saving only the bias and the corresponding timestamp for the user.

To know the true voting power an user has you need all 3. Protocol is unable to compute the voting power at a given time using the checkpoints.
The checkpoints works as a snapshot, allowing protocol to interogate a user's votingPower at any given time.

For example this is needed when users must vote for a governance proposal. Users must vote with their voting power at a given time (eg. proposal time creation) to ensure fairness and that proposal results can't be manipulated.

Note: there is another issue reported in a separate submission related to the fact protocol doesn't read votingPower from the snapshots.

Impact

The voting power at a given timestamp can't be used.

The governance and gauge rewards allocation are susceptible to manipulations.

Tools Used

Recommendations

When a checkpoint is created save the slope too beside bias and timestamp.

Ensure that getPastVotes function calculates correctly the voting power using the formulla :
votingPower = bias − slope × timePassed.

Updates

Lead Judging Commences

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

veRAACToken checkpoints store only bias without slope, preventing accurate historical voting power calculation and making governance proposals vulnerable to manipulation

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

veRAACToken checkpoints store only bias without slope, preventing accurate historical voting power calculation and making governance proposals vulnerable to manipulation

Appeal created

anonymousjoe Auditor
3 months ago
inallhonesty Lead Judge
3 months ago
inallhonesty Lead Judge 3 months ago
Submission Judgement Published
Validated
Assigned finding tags:

Governance.castVote uses current voting power instead of proposal creation snapshot, enabling vote manipulation through token transfers and potential double-voting

Support

FAQs

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