Summary
The GaugeController
contract defines a MIN_VOTE_WEIGHT
constant set to 100 (1%), but fails to enforce this minimum threshold in the voting mechanism. This oversight allows users to bypass minimum vote constraint and cast votes with extremely small weights (as low as 1), leading to gauge weight fragmentation and inefficient vote distribution.
Vulnerability Details
The contract defines:
uint256 public constant MIN_VOTE_WEIGHT = 100;
But in the vote()
function, this minimum is never enforced:
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);
}
Impact
Large numbers of dust votes can fragment the voting power distribution, making it harder to achieve meaningful gauge weight changes
Tools Used
Manual Review
Recommendations
Enforce the MIN_VOTE_WEIGHT
constant in the vote()
function, while still allowing zero weights to enable vote removal:
function vote(address gauge, uint256 weight) external override whenNotPaused {
if (!isGauge(gauge)) revert GaugeNotFound();
if (weight > WEIGHT_PRECISION) revert InvalidWeight();
+ // Allow zero weight to remove votes, but enforce minimum for non-zero weights
+ if (weight != 0 && weight < MIN_VOTE_WEIGHT) revert WeightBelowMinimum();
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);
}