veToken Governance Remediation
Overview
Related Detector: veToken Governance
Vote-escrowed (ve) systems grant voting power for locking tokens over time. They are vulnerable to flash-vote attacks (acquire power, vote, release in one transaction), decay manipulation, lock-extension bypasses, and checkpoint gaming. The remediation is to make voting power a function of past, time-locked state rather than instantaneous balance.
Recommended Fix
Before (Vulnerable)
function vote(uint256 proposalId, bool support) external {
// Uses live balance — flash-loaned or just-locked tokens vote immediately.
uint256 weight = veToken.balanceOf(msg.sender);
_castVote(proposalId, support, weight);
}
After (Fixed — snapshot power at proposal creation)
function vote(uint256 proposalId, bool support) external {
uint256 snapshotBlock = proposals[proposalId].snapshotBlock;
// Voting power is read at a block fixed when the proposal was created,
// so power acquired afterward (or flash-loaned) cannot influence the vote.
uint256 weight = veToken.balanceOfAt(msg.sender, snapshotBlock);
_castVote(proposalId, support, weight);
}
Combine snapshot-based power with a genuine lock duration (power should require tokens locked well before the snapshot) so a lock taken to game a specific vote carries no weight.
Common Mistakes
- Reading live voting power instead of a snapshot taken at proposal creation, enabling flash-loan governance attacks.
- Letting a lock created after the snapshot count, or letting an extension retroactively boost past power.
- Checkpointing power lazily so an attacker can insert a favorable checkpoint at the moment they vote.