Core Contracts

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

`GaugeController::_updateGaugeWeight()` does not account for voting power changes, leading to DOS or bad calculations

Summary

At GaugeController::_updateGaugeWeight() the weight math used to compute the new weight is incorrect. This is because this math makes sense if a user's voting power does not change, yet it can in multiple ways. Like the user locking more tokens.

Vulnerability Details

Here is the math logic for it.

It first subtracts X% of the previous votingPower then adds the new X% of the votingPower. This will be correct if votingPower is the same.

Proof Of Concept

Good scenario:

  • A user allocates 50% of his voting power to a gauge. Lets say the voting power is 100. Total allocated weight is 50.

  • The user re-allocates and decides to increase to 75% of his power. He keeps having the same voting power, so the new weight should be 75.

The code will:

// oldWeight = 50
// newWeight = 75
// votingPower = 100
// 75 = 50 - (50% of 100) + (75% of 100) = 50 - 50 + 75
uint256 newGaugeWeight = oldGaugeWeight - (oldWeight * votingPower / WEIGHT_PRECISION)
+ (newWeight * votingPower / WEIGHT_PRECISION);

Bad scenario:

If the user between those 2 updates locks more tokens, or the voting power naturally decays as it is supposed to do, the math will be incorrect. Let's showcase the locking more because it is simpler:

  • A user allocates 50% of his voting power to a gauge. Lets say the voting power is 100. Total allocated weight is 50.

  • User locks more tokens and now has 200 voting power.

  • The user re-allocates and decides to increase to 75% of his power. The new weight should be 75% of 200 = 150.

// oldWeight = 50
// newWeight = 75
// votingPower = 200
// 50 - (50% of 200) + (75% of 200) = 50 - 100 + 150 // 🔴 REVERT due to UNDERFLOW!!
// Let's briefly think that the voting power has linearly decayed over time to 50.
// 50 - (50% of 50) + (75% of 50) = 50 - 25 + 37.5 = 62.5 // 🔴 ADDING more WEIGHT to the gauge even with less power
uint256 newGaugeWeight = oldGaugeWeight - (oldWeight * votingPower / WEIGHT_PRECISION)
+ (newWeight * votingPower / WEIGHT_PRECISION);

Impact

If users increase their voting power, by locking more tokens for example, they will be unable to vote on gauge controller calling GaugeController::vote(). As this function is the one using _updateGaugeWeight(), see here.

They will be unable to use it probably at least for a year, the minimum lock period to get veTokens. And, if for some reason the linear decay allows them not to revert the function, they will have a greater influence that they should in voting in the gauge.

Recommendations

Account for voting power changes every time the weight is updated on GaugeController::vote(). For this you will probably need to track the last voting power of the user when he last called vote on the controller, and then subtract the respecting amount correctly derived from that and the oldWeight percentage.

Updates

Lead Judging Commences

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

GaugeController::_updateGaugeWeight uses current voting power for both old and new vote calculations, causing underflows when voting power increases and incorrect gauge weights

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

GaugeController::_updateGaugeWeight uses current voting power for both old and new vote calculations, causing underflows when voting power increases and incorrect gauge weights

Support

FAQs

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

Give us feedback!