veToken Governance
Detects vulnerabilities in vote-escrowed token governance systems including flash-vote attacks, lock manipulation, and decay bypass.
veToken Governance
Overview
The veToken governance detector identifies vulnerabilities in vote-escrowed (ve) token systems where users lock tokens for a period to gain voting power. These systems, popularized by Curve’s veCRV, are susceptible to flash-vote attacks, decay manipulation, lock extension bypasses, and checkpoint gaming.
Why This Is an Issue
veToken governance depends on a time-weighted locking mechanism: longer locks yield more voting power, which decays linearly toward zero. Flaws in this model allow:
- Flash governance: Acquiring voting power just before a snapshot, voting, then exiting immediately.
- Decay bypass: Extending lock duration without resetting the decay curve, inflating voting power.
- Checkpoint manipulation: Triggering checkpoint writes at favorable times to capture inflated balances.
The Beanstalk governance attack ($182M, 2022) exploited flash-loaned governance tokens to pass a malicious proposal in a single transaction. While Beanstalk did not use veTokens specifically, the attack pattern applies directly to veToken systems with inadequate snapshot protection.
How to Resolve
// Before: Voting power based on current lock state -- flash-votable
function getVotingPower(address user) public view returns (uint256) {
Lock memory lock = locks[user];
if (lock.end <= block.timestamp) return 0;
return lock.amount * (lock.end - block.timestamp) / MAX_LOCK;
}
// After: Voting power based on historical checkpoint -- flash-resistant
function getVotingPower(address user, uint256 timestamp) public view returns (uint256) {
require(timestamp < block.timestamp, "Future lookup");
Checkpoint memory cp = _findCheckpoint(user, timestamp);
if (cp.lockEnd <= timestamp) return 0;
return cp.amount * (cp.lockEnd - timestamp) / MAX_LOCK;
}
Examples
Vulnerable Code
contract VulnerableVeToken {
struct Lock { uint256 amount; uint256 end; }
mapping(address => Lock) public locks;
function vote(uint256 proposalId) external {
// Vulnerable: reads current state, not historical checkpoint
uint256 power = getVotingPower(msg.sender);
require(power > 0, "No voting power");
_castVote(proposalId, msg.sender, power);
}
// Extend lock without proper decay reset
function extendLock(uint256 newEnd) external {
Lock storage lock = locks[msg.sender];
require(newEnd > lock.end, "Must extend");
lock.end = newEnd; // Does not reset decay -- inflated power
}
}
Fixed Code
contract SafeVeToken {
mapping(address => Checkpoint[]) public checkpoints;
function vote(uint256 proposalId) external {
// Use snapshot from proposal creation block -- immune to flash attacks
uint256 snapshotTime = proposals[proposalId].snapshotTimestamp;
uint256 power = getVotingPower(msg.sender, snapshotTime);
require(power > 0, "No voting power at snapshot");
_castVote(proposalId, msg.sender, power);
}
function extendLock(uint256 newEnd) external {
Lock storage lock = locks[msg.sender];
require(newEnd > lock.end, "Must extend");
_writeCheckpoint(msg.sender); // Record state before change
lock.end = newEnd;
}
}
Sample Sigvex Output
{
"detector_id": "vetoken-governance",
"severity": "high",
"confidence": 0.68,
"description": "Voting power function at offset 0x2a4 reads current lock state without checkpoint-based historical lookup. Flash-vote attacks can acquire and exercise governance power within a single transaction.",
"location": { "function": "vote(uint256)", "offset": 676 }
}
Detection Methodology
- Lock pattern identification: Detects time-locked balance structures (amount + end timestamp).
- Voting power analysis: Traces how voting power is calculated — current state vs. historical checkpoints.
- Decay curve validation: Checks whether lock extensions properly reset or adjust the decay function.
- Checkpoint coverage: Verifies that state-changing operations write checkpoints before mutations.
Limitations
- Cannot evaluate the economic viability of flash-vote attacks (requires off-chain market analysis).
- Governance systems using off-chain voting (Snapshot) are not analyzed.
- Complex multi-token governance with delegation chains may produce false negatives.
Related Detectors
- Governance Attacks — general governance manipulation patterns
- Flash Loan — flash loan attack vectors that may fund governance attacks
- Front Running — front-running of governance proposals