Core Contracts

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

Users can update gauge weights as often as they like, manipulating rewards distribution

Summary

In the GaugeController.sol, user can move their weights between gauges as often as they like, with no vote delay at all, allowing them to manipulate reward distribution.

Vulnerability Details

The GaugeController implements a Curve-style gauge voting and reward distribution system, where users vote with their veRAACToken to allocate weights to gauges. These weights determine the emission rates for each gauge. The functionality is implemented in the vote function of the GaugeController.sol contract as follows:

/**
* @notice Core voting functionality for gauge weights
* @dev Updates gauge weights based on user's veToken balance
* @param gauge Address of gauge to vote for
* @param weight New weight value in basis points (0-10000)
*/
//@audit-issue Missing minimum vote delay to prevent manipulation
function vote(address gauge, uint256 weight) external override whenNotPaused {
if (!isGauge(gauge)) revert GaugeNotFound();
if (weight > WEIGHT_PRECISION) revert InvalidWeight();
uint256 votingPower = veRAACToken.balanceOf(msg.sender);
if (votingPower == 0) revert NoVotingPower();
uint256 oldWeight = userGaugeVotes[msg.sender][gauge];
userGaugeVotes[msg.sender][gauge] = weight;
_updateGaugeWeight(gauge, oldWeight, weight, votingPower);
emit WeightUpdated(gauge, oldWeight, weight);
}

As we can see, users can assign a certain weight to a specific gauge. Currently, there is no delay in how often users can reassign their votes. This oversight allows them to manipulate the reward distribution by assigning their weights to a specific pool right before the distribution, gaining a larger share of rewards. After the rewards are distributed, they can switch the votes back to another pool and repeat the process.

In Curve there is a delay of 10 days where user can change their voting weight. By looking at the contract GaugeController.sol, among others, there are following constants and variable defined:

/// @notice Last vote timestamp for each user
mapping(address => uint256) public lastVoteTime;
/// @notice Required delay between votes
uint256 public constant VOTE_DELAY = 10 days;
/// @notice Minimum allowed vote delay
uint256 public constant MIN_VOTE_DELAY = 1 days;
/// @notice Maximum allowed vote delay
uint256 public constant MAX_VOTE_DELAY = 10 days;
/// @notice Minimum vote weight allowed
uint256 public constant MIN_VOTE_WEIGHT = 100;

However, they are never used to check whether the period since the last vote has passed.

Impact

Medium – Although the likelihood is high, the impact is medium since it involves leveraging the ability to switch weights to gain more rewards (i.e., manipulating the reward distribution mechanism to one's advantage)

Tools Used

Manual review

Recommendations

Utilize the lastVoteTime mapping to store the time of the vote and check it against the VOTE_DELAY when calling the vote function

function vote(address gauge, uint256 weight) external override whenNotPaused {
if (!isGauge(gauge)) revert GaugeNotFound();
if (weight > WEIGHT_PRECISION) revert InvalidWeight();
// check if the delay has passed since the last vote
if (block.timestamp < lastVoteTime[msg.sender] + VOTE_DELAY) revert VoteDelayNotPassed();
uint256 votingPower = veRAACToken.balanceOf(msg.sender);
if (votingPower == 0) revert NoVotingPower();
uint256 oldWeight = userGaugeVotes[msg.sender][gauge];
userGaugeVotes[msg.sender][gauge] = weight;
// Update the last vote time to the current timestamp
lastVoteTime[msg.sender] = block.timestamp;
_updateGaugeWeight(gauge, oldWeight, weight, votingPower);
emit WeightUpdated(gauge, oldWeight, weight);
}
Updates

Lead Judging Commences

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

GaugeController::vote never enforces VOTE_DELAY or updates lastVoteTime, allowing users to spam votes and manipulate gauge weights without waiting

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

GaugeController::vote never enforces VOTE_DELAY or updates lastVoteTime, allowing users to spam votes and manipulate gauge weights without waiting

Support

FAQs

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