Core Contracts

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

_updateGaugeWeight Function Has an Issue with Weight Transfer

Summary

The function _updateGaugeWeight() is responsible for adjusting the gauge weight when users change their vote. However, the calculation does not handle the delegation change properly, leading to incorrect weight updates and potential reward misallocations.

/**
* @notice Updates a gauge's weight based on vote changes
* @dev Recalculates gauge weight using voting power
* @param gauge Address of the gauge
* @param oldWeight Previous vote weight
* @param newWeight New vote weight
* @param votingPower Voter's voting power
*/
function _updateGaugeWeight(
address gauge,
uint256 oldWeight,
uint256 newWeight,
uint256 votingPower
) internal {
Gauge storage g = gauges[gauge];
uint256 oldGaugeWeight = g.weight;
uint256 newGaugeWeight = oldGaugeWeight - (oldWeight * votingPower / WEIGHT_PRECISION)
+ (newWeight * votingPower / WEIGHT_PRECISION);
g.weight = newGaugeWeight;
g.lastUpdateTime = block.timestamp;
}

Vulnerability Details

Problematic Code:

uint256 newGaugeWeight = oldGaugeWeight - (oldWeight * votingPower / WEIGHT_PRECISION)
+ (newWeight * votingPower / WEIGHT_PRECISION);
  1. Incorrect Weight Removal

    • The function subtracts oldWeight * votingPower / WEIGHT_PRECISION, but only from the same gauge.

    • If the user is switching from Gauge A → Gauge B, their old weight in Gauge A should be removed first, but this function does not account for inter-gauge transfers.

    • This leads to inflated weights, where Gauge A still holds a part of the user's weight even after delegation to Gauge B.

  2. Lack of Explicit Delegation Clearing

    • The function assumes that weight updates are always within the same gauge and does not properly zero out old gauge votes when switching to a new gauge.

Severity Classification (Based on Common Vulnerability Rating Methodology - CVSS)

Criteria Severity
Financial Impact High - Direct reward inflation and emissions loss
Governance Manipulation High - Attackers can skew governance outcomes
Protocol Stability Risk Medium - Can cause long-term reward distribution issues
Exploit Complexity Medium - Requires voting power but no special privileges
Likelihood of Exploitation High - Straightforward delegation switching abuse

Impact

Rewards Manipulation:

  • A malicious user can delegate to multiple gauges without correctly removing their old votes, leading to double staking influence.

  • Example Exploit Scenario:

    • User1 votes 50% on Gauge A.

    • User1 then votes 100% on Gauge B.

    • Gauge A still holds part of the old weight, but Gauge B now has full voting power.

    • Result: Rewards are calculated incorrectly leading to inflated emissions.

Tools Used

Manual Review

Recommendations

Ensure Proper Weight Transfer

  1. Maintain Accurate Weight Calculations

    • The weight update should properly account for previous voting weights before applying the new weight.

    • Use safe math to prevent potential underflows or overflows.

  2. Track Total Delegated Voting Power Correctly

    • Ensure that when moving votes from one gauge to another, the previous gauge loses weight correctly before the new one gains it.

    • Introduce an explicit function to remove previous weight first, preventing unintended accumulation.

  3. Implement Atomic Update for Gauge Weight Transfer

    • The weight transfer must be fully processed within a single transaction to avoid edge cases where weight gets locked or miscalculated.

    • If the process fails, it should revert entirely to prevent inconsistent state changes.

function _updateGaugeWeight(
address oldGauge,
address newGauge,
uint256 oldWeight,
uint256 newWeight,
uint256 votingPower
) internal {
// Ensure weights are adjusted properly when transferring
if (oldGauge != address(0)) {
Gauge storage oldG = gauges[oldGauge];
require(oldG.weight >= (oldWeight * votingPower / WEIGHT_PRECISION), "Weight underflow");
oldG.weight -= (oldWeight * votingPower / WEIGHT_PRECISION);
oldG.lastUpdateTime = block.timestamp;
}
if (newGauge != address(0)) {
Gauge storage newG = gauges[newGauge];
require(newG.weight + (newWeight * votingPower / WEIGHT_PRECISION) >= newG.weight, "Weight overflow");
newG.weight += (newWeight * votingPower / WEIGHT_PRECISION);
newG.lastUpdateTime = block.timestamp;
}
emit GaugeWeightUpdated(oldGauge, newGauge, votingPower, oldWeight, newWeight);
}
Updates

Lead Judging Commences

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

GaugeController::vote lacks total weight tracking, allowing users to allocate 100% of voting power to multiple gauges simultaneously

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

GaugeController::vote lacks total weight tracking, allowing users to allocate 100% of voting power to multiple gauges simultaneously

Support

FAQs

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

Give us feedback!