Description
The GaugeController::vote
function calculates voting power using veRAACToken.balanceOf()
which represents the locked token balance without accounting for time-based decay. This allows users to maintain voting influence proportional to their original locked amount even after their actual voting power should have decayed over time. The system should instead use veRAACToken.getVotingPower()
which properly accounts for time-based decay in the voting power calculation.
Proof of Concept
Add this test case in GaugeController.test.js
:
it("demonstrates non-decaying voting power impact", async () => {
await veRAACToken.mint(user1.address, ethers.parseEther("1000"));
await gaugeController
.connect(user1)
.vote(await rwaGauge.getAddress(), WEIGHT_PRECISION);
const initialRWAWeight = await gaugeController.getGaugeWeight(
await rwaGauge.getAddress()
);
await time.increase(15 * 24 * 3600);
await network.provider.send("evm_mine");
await gaugeController
.connect(user1)
.vote(await raacGauge.getAddress(), WEIGHT_PRECISION);
const finalRWAWeight = await gaugeController.getGaugeWeight(
await rwaGauge.getAddress()
);
const raacWeight = await gaugeController.getGaugeWeight(
await raacGauge.getAddress()
);
const totalWeight = await gaugeController.getTotalWeight();
expect(totalWeight).to.equal(ethers.parseEther("2000"));
expect(finalRWAWeight).to.equal(ethers.parseEther("1000"));
expect(raacWeight).to.equal(ethers.parseEther("1000"));
});
Impact
Medium Severity - Creates unfair gauge weight allocations where users with expired or decaying locks maintain disproportionate voting power. Distorts reward distribution and protocol incentives over time.
Recommendation
Use time-decayed voting power:
contracts/core/governance/gauges/GaugeController.sol
function vote(address gauge, uint256 weight) external {
- uint256 votingPower = veRAACToken.balanceOf(msg.sender);
+ uint256 votingPower = veRAACToken.getVotingPower(msg.sender);
}
mapping(address => uint256) public votingPowerSnapshots;
function vote(address gauge, uint256 weight) external {
uint256 votingPower = veRAACToken.getVotingPower(msg.sender);
votingPowerSnapshots[msg.sender] = votingPower;
}