Core Contracts

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

`veRAACToken::_updateBoostState` Uses Outdated `totalSupply` and `_votingState`, Causing Incorrect Boost Calculations

Summary

The _updateBoostState function in the veRAACToken contract retrieves the total token supply and voting power before they are updated. Since _updateBoostState is called before _mint(msg.sender, newPower), it uses an outdated value for totalSupply(). Additionally, _boostState.votingPower is calculated using _votingState.calculatePowerAtTimestamp(user, block.timestamp), but _votingState is only updated later in the lock function, leading to incorrect calculations for boost weight and voting power distribution.

Vulnerability Details

The function _updateBoostState updates the _boostState values using totalSupply() and _votingState before they are properly updated:

function lock(uint256 amount, uint256 duration) external nonReentrant whenNotPaused {
if (amount == 0) revert InvalidAmount();
if (amount > MAX_LOCK_AMOUNT) revert AmountExceedsLimit();
if (totalSupply() + amount > MAX_TOTAL_SUPPLY) revert TotalSupplyLimitExceeded();
if (duration < MIN_LOCK_DURATION || duration > MAX_LOCK_DURATION)
revert InvalidLockDuration();
// Do the transfer first - this will revert with ERC20InsufficientBalance if user doesn't have enough tokens
raacToken.safeTransferFrom(msg.sender, address(this), amount);
// Calculate unlock time
uint256 unlockTime = block.timestamp + duration;
// Create lock position
_lockState.createLock(msg.sender, amount, duration);
@> _updateBoostState(msg.sender, amount); // Called too early
// Calculate initial voting power
@> (int128 bias, int128 slope) = _votingState.calculateAndUpdatePower(
msg.sender,
amount,
unlockTime
);
// Update checkpoints
uint256 newPower = uint256(uint128(bias));
_checkpointState.writeCheckpoint(msg.sender, newPower);
// Mint veTokens
@> _mint(msg.sender, newPower); // Should happen before _updateBoostState
emit LockCreated(msg.sender, amount, unlockTime);
}
function _updateBoostState(address user, uint256 newAmount) internal {
// Update boost calculator state
@> _boostState.votingPower = _votingState.calculatePowerAtTimestamp(user, block.timestamp);
// @audit-issue Is _votingState updated at this point? Or is it the old value?
@> _boostState.totalVotingPower = totalSupply();
// @audit-issue Has totalSupply been updated yet? Or is it outdated?
_boostState.totalWeight = _lockState.totalLocked;
_boostState.updateBoostPeriod();
}

At this point:

  • totalSupply() does not yet reflect the new voting power minted in the lock function because _mint(msg.sender, newPower); is executed after _updateBoostState.

  • _votingState.calculatePowerAtTimestamp(user, block.timestamp) retrieves outdated voting power since _votingState.calculateAndUpdatePower has not yet been called.

This results in miscalculations in _boostState, leading to incorrect weight distributions.

Impact

The use of outdated totalSupply() and _votingState values causes incorrect calculations for boost weight and voting power distribution. This can lead to governance discrepancies, where some token holders might receive inaccurate voting power allocations. Over time, this could impact governance decisions by misrepresenting token holders' influence.

Tools Used

Manual review of the contract’s logic.

Recommendations

Move the _updateBoostState call after _mint(msg.sender, newPower); and ensure _votingState is updated before it is used in _updateBoostState. This will ensure that both totalSupply() and _votingState reflect the correct values before being used in boost calculations.

Updates

Lead Judging Commences

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