The cancel function in the Governance contract allows anyone to cancel a proposal if the proposer's voting power drops below the required threshold.
This creates a vulnerability where malicious actors can spam cancellations, potentially disrupting the governance process by canceling valid proposals whenever a proposer’s voting power temporarily dips below proposalThreshold
The cancel function allows cancellation if:
msg.sender == proposal.proposer (proposer can always cancel), OR
_veToken.getVotingPower(proposal.proposer) < proposalThreshold (proposer’s power drops below 100k veRAAC).
If msg.sender is not the proposer AND the proposer’s voting power is ≥ threshold, it reverts with InsufficientProposerVotes.
If msg.sender is not the proposer AND the proposer’s voting power is < threshold, it proceeds to cancel.
now
If msg.sender == proposal.proposer, the check is bypassed, and cancellation proceeds—intended behavior (Proposer cancellation).
If msg.sender != proposal.proposer, cancellation is allowed only when the proposer’s voting power drops below proposalThreshold(non proposer cl).
Exploit Scenario:
Proposal created by userA with 150k veRAAC (above 100k threshold).
userA’s lock partially expires or they withdraw some tokens, reducing power to 90k veRAAC.
Malicious userBcalls cancel(proposalId) → Succeeds because 90k < 100k, even if userA` intended to maintain the proposal.
userB could monitor proposers and spam cancellations whenever their power dips .
Malicious actors could exploit this behavior by monitoring proposals and canceling them when the proposer's voting power decreases.
Legitimate proposals could be canceled prematurely
Add a check in the cancel function to revert if state(proposalId) == ProposalState.Queued or state(proposalId) == ProposalState.Executed.
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.