Decentralized governance is supposed to give power to the community. In practice, it's given power to whoever can accumulate the most tokens in a single transaction. Beanstalk Farms lost $182M to a flash loan governance attack — the attacker borrowed enough tokens to pass a malicious proposal, drained the protocol, and repaid the loan, all in one transaction. They're not alone.
How Governance Attacks Work
Most DAO governance systems use token-weighted voting: more tokens = more votes. This creates several attack vectors.
Flash Loan Governance
function attack() external {
// 1. Flash loan governance tokens
aave.flashLoan(address(this), BEAN, 1_000_000e18, "");
}
function executeOperation(...) external returns (bool) {
// 2. Create and vote on malicious proposal
dao.propose(maliciousProposal);
dao.vote(proposalId, 1_000_000e18);
// 3. If quorum met + no timelock, execute immediately
dao.execute(proposalId);
// Proposal drains treasury to attacker
// 4. Repay flash loan
IERC20(BEAN).approve(address(aave), amount + fee);
return true;
}
Governance Attack Variants
1. Flash Loan Voting
Borrow tokens → vote → repay. Only works if voting uses current balance rather than historical snapshots.
2. Proposal Spam
Create many proposals to confuse voters and sneak through a malicious one during voter fatigue.
3. Social Engineering
Disguise a malicious proposal as a routine parameter change through confusing naming or complex execution logic.
4. Vote Buying
Off-chain markets for governance votes (dark DAOs) that undermine the integrity of on-chain voting.
| Protocol | Year | Loss | Attack Vector |
|---|---|---|---|
| Beanstalk | 2022 | $182M | Flash loan governance |
| Build Finance DAO | 2022 | $470K | Hostile takeover via token accumulation |
| Tornado Cash | 2023 | Governance | Malicious proposal with hidden logic |
| Audius | 2022 | $6M | Governance contract vulnerability |
Building Secure Governance
// Snapshot-based voting: immune to flash loans
function propose(bytes memory proposal) external {
require(
token.getPastVotes(msg.sender, block.number - 1) >= proposalThreshold,
"Need tokens BEFORE proposing"
);
// Snapshot is taken at proposal creation block
}
// Timelock: gives users time to exit
function execute(uint256 proposalId) external {
require(
block.timestamp >= proposals[proposalId].eta,
"Timelock not expired" // 48-hour minimum delay
);
}
- ✅ Use snapshot-based voting (OpenZeppelin Governor) — tokens must be held before proposal
- ✅ Implement timelocks (24-72 hours) on all governance actions
- ✅ Set realistic quorum (10-20% of circulating supply)
- ✅ Add voting delay (proposal → voting starts takes 1+ day)
- ✅ Multi-step proposals for critical changes (propose → review → execute)
How Vultbase Detects Governance Issues
- Pattern DB — 16 governance attack patterns including flash loan voting and proposal manipulation
- Challenge-Based Testing — Simulates flash loan governance scenarios
- Access Control Analysis — Verifies timelock and threshold configurations
Your governance is your protocol's constitution. Audit it before someone rewrites it with a flash loan.