Skip to main content
Sigvex

veToken Governance Remediation

How to protect vote-escrow systems from flash-vote, decay manipulation, and checkpoint gaming.

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.

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.

References