The governance system uses decayed voting power for individual votes but total supply for quorum calculations, creating a possibility for governance manipulation under specific lock expiry conditions.
The governance contract has an inconsistency in how it handles voting power between individual votes and quorum calculations:
in castVote(), individual votes use decayed voting power
While in the quorum() function, totalVotingPower uses total supply without decay.
in state() function check, compares decayed votes against undecayed quorum for it to fail
Sample Scenario:
Initial State:
Total Supply: 1,000,000 RAAC
Current Locked: 950, 000
Quorum: 4% (38,000)
950,000 RAAC locked with 50 days remaining
Decayed voting power calculation:
950,000 (50/1460) ≈ 32,534 veRAAC
Attack Path:
Attacker acquires and locks 50,000 RAAC (5%) for 4 years
Gets full voting power = 50,000 veRAAC
New Quorum: 4% (40,000)
Proposes malicious governance action
Votes YES with 50,000 power
Even with 50 days remaining, if all others vote NO:
Meets quorum (50,000 > 40,000)
More YES than NO votes (50,000 > 32,534 )
Proposal still passes despite 95% opposition
A malicious actor with a even with a minority token position could potentially gain disproportionate control over protocol governance by exploiting the mismatch between decayed voting power calculations and quorum requirements. This could lead to a governance attack allowing malicious proposals to pass (eg. proposals to redirect protocol treasury funds, modify critical protocol parameters, or upgrade contracts to malicious implementations)
Implement a "voting power snapshot" system:
Take snapshots of voting power at proposal creation
Use consistent power calculations for entire vote duration
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.