Core Contracts

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

Incorrect checkpoint placement in function flow when locking

Summary

Due to the position of a function in the flow of execution, users' boost states will not be updated correctly.

Vulnerability Details

Users lock RAAC tokens for veRAAC tokens which give them voting power and affect their boost state. But if we break down the lock function, we will see that the update to boost state is placed in an incorrect position.

Take notice that the _updateBoostState() call is made before the _votingState.calculateAndUpdatePower() call:

function lock(uint256 amount, uint256 duration) external nonReentrant whenNotPaused {
...
// Create lock position
_lockState.createLock(msg.sender, amount, duration);
@> _updateBoostState(msg.sender, amount);
// Calculate initial voting power
@> (int128 bias, int128 slope) = _votingState.calculateAndUpdatePower(
msg.sender,
amount,
unlockTime
);
...
}

The _updateBoostState() function calculates the voting power from the user's voting state calculation at a given timestamp:

function _updateBoostState(address user, uint256 newAmount) internal {
// Update boost calculator state
@> _boostState.votingPower = _votingState.calculatePowerAtTimestamp(user, block.timestamp);
_boostState.totalVotingPower = totalSupply();
_boostState.totalWeight = _lockState.totalLocked;
_boostState.updateBoostPeriod();
}

But in order to make that calculation, the user must have a checkpoint created in the _votingState:

function calculatePowerAtTimestamp(
VotingPowerState storage state,
address account,
uint256 timestamp
) internal view returns (uint256) {
@> RAACVoting.Point memory point = state.points[account];
@> if (point.timestamp == 0) return 0; // @audit point accessed is not yet created
if (timestamp < point.timestamp) {
return 0;
}
uint256 timeDelta = timestamp - point.timestamp;
int128 adjustedBias = point.bias - (point.slope * int128(int256(timeDelta)));
return adjustedBias > 0 ? uint256(uint128(adjustedBias)) : 0;
}

In order to calculate the power at timestamp and update it in _updateBoostState, the user must have a checkpoint created in the _votingState, but in the original lock() function flow, that point is created after the update to boost state, inside _votingState.calculateAndUpdatePower():

function calculateAndUpdatePower(
VotingPowerState storage state,
address user,
uint256 amount,
uint256 unlockTime
) internal returns (int128 bias, int128 slope) {
...
@> 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);
}

Due to this incorrect placement of functions inside lock(), whenever users lock tokens the boost state will be updated incorrectly. Since the at the time of updating the boost state the user will have no checkpoint created, the boost state will be updated with amount 0 which is the default to return if a checkpoint does not exist yet. The same is the case when useres increase() their lock, where the update to boost state is also put in place before the checkpoint creation.

Impact

Since locks are meant to be long term and users lock tokens for up to years at a single time, the boost state will return 0 instead of the correct amount for years on end. Due to this, the boost state will return incorrect amounts when calculating rewards minting.

Tools Used

Manual Review

Recommendations

Make sure the _updateBoostState() call is placed after the _votingState.calculateAndUpdatePower() call so that the checkpoint is already created and the boost state is stored correctly.

Updates

Lead Judging Commences

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

veRAACToken::_updateBoostState should be called later inside lock/increase

Support

FAQs

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

Give us feedback!