The governance quorum calculation in the protocol uses real-time total voting power rather than a snapshot at proposal creation. An attacker can manipulate the quorum requirements after a proposal's voting period has ended but before execution, effectively blocking the execution of passed proposals.
The vulnerability exists because the quorum calculation in the Governance contract is based on the current total voting power rather than the voting power between the time the proposal was created and voting ended. This allows malicious actors to manipulate the quorum requirement by significantly increasing the total voting power after a vote has concluded but before execution, so the proposal results in a defeated state.
The attack flow:
A proposal is created and receives sufficient votes to pass during the voting period
After the voting period ends, but before execution:
A whale deposits a large amount of tokens, dramatically increasing the total voting power (because the getTotalVotingPower() returns the totalSupply()). It's worth to mention here that he needs to lock RaacTokens in order to get veRaac for at least 1 year which reduces the likelihood of this attack but it's still possible.
This increases the required quorum retroactively
The previously "passed" proposal can no longer be executed due to insufficient quorum
The governance contract calculates quorum using current total voting power:
The veRAACToken getTotalVotingPower() function returns the totalSupply of the token:
The quorum() function is then used to determine if the currentQuorum of a proposal mets the threshold to pass or not :
In order to run the test you need to:
Run foundryup to get the latest version of Foundry
Install hardhat-foundry: npm install --save-dev @nomicfoundation/hardhat-foundry
Import it in your Hardhat config: require("@nomicfoundation/hardhat-foundry");
Make sure you've set the BASE_RPC_URL in the .env file or comment out the forking option in the hardhat config.
Run npx hardhat init-foundry
There is one file in the test folder that will throw an error during compilation so rename the file in test/unit/libraries/ReserveLibraryMock.sol to => ReserveLibraryMock.sol_broken so it doesn't get compiled anymore (we don't need it anyways).
Create a new folder test/foundry
Paste the below code into a new test file i.e.: FoundryTest.t.sol
Run the test: forge test --mc FoundryTest -vvvv
Proposals that legitimately passed voting can be blocked from execution
Governance system can be rendered ineffective through quorum manipulation
Foundry
Manual Review
The PowerCheckpointlibrary which is already used in the veRAACToken contract has a function to get the total voting power at a specific block:
The protocol should implement this carefully into the veRAACToken contract and then safe the block number when the proposal got created to query the totalSupply at that point during the quorum() function.
The contest is live. Earn rewards by submitting a finding.
This is your time to appeal against judgements on your submissions.
Appeals are being carefully reviewed by our judges.