Core Contracts

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

DoS or incorrect accounting if user votes second time after his voting power has changed.

Bug description

Using GaugeController users can vote for gauges increasing their gauge weight. This can be done via vote() function. User can specify a weight or a percentage of their voting power they wisht to delegate.

GaugeController.sol#L194-L200

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);

_updateGaugeWeight() will subtract user's old vote and add the new one.

GaugeController.sol#L221-L223

uint256 oldGaugeWeight = g.weight;
uint256 newGaugeWeight = oldGaugeWeight -
((oldWeight * votingPower) / WEIGHT_PRECISION) +
((newWeight * votingPower) / WEIGHT_PRECISION);

The problem arises when users vote again after their voting power has changed. Assume a scenario where user has 100 voting power and votes with 100% of it for one gauge. Now gauge's weight is 100. User's voting power increases to 200, and now if he wishes to withdraw his vote or add additional weight to the gauge, the _updageGaugeWeight() will revert. This will happen because the function will subtract (oldWeight) 100% * 200 (user's current voting power), while gauge weight is only equal 100. If the gauge has other people voting for it, this action will subtract from other peoples votes.

Impact

DoS of the vote function or incorrect accounting of gauge's weight.

Proof of Concept

Please add this test to GaugeController.test.js and run it with npx hardhat test --grep "DoS or incorrect accounting".

describe("sl1", () => {
it("DoS or incorrect accounting", async () => {
await veRAACToken.mint(user1.address, ethers.parseEther("1000"));
// User has voted with all of his weight
await gaugeController
.connect(user1)
.vote(await rwaGauge.getAddress(), 10000);
const weight = await gaugeController.getGaugeWeight(
await rwaGauge.getAddress()
);
expect(weight).to.equal(await veRAACToken.balanceOf(user1));
// User balance has increased
await veRAACToken.mint(user1.address, ethers.parseEther("1000"));
// He wishse to vote with his additional tokens, but faces underflow revert
await expect(
gaugeController.connect(user1).vote(await rwaGauge.getAddress(), 10000)
).to.be.revertedWithPanic("0x11");
// Reducing to 0 is also not possible
await expect(
gaugeController.connect(user1).vote(await rwaGauge.getAddress(), 0)
).to.be.revertedWithPanic("0x11");
// And if is possible, that would mean that it would subtract other people's votes
});
}

Recommended Mitigation

When voting for a gauge apart from storing user's old weight, store the voting power he has voted with, so when _updateGaugeWeight() is called, it will subtract oldWeight using original voting power.

Updates

Lead Judging Commences

inallhonesty Lead Judge 4 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 4 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.