The thing you are actually defending: the treasury's keys
Picture a bank vault with no human guard. The only way to open it is to gather a majority of numbered voting chips and drop them in a slot; if enough chips agree, the door swings open and does whatever the winning slip of paper says — including 'transfer everything to address 0xATTACKER'. That is on-chain governance. A DAO is not a vibe or a Discord server; it is a smart contract that literally holds the keys to the treasury, and a passed proposal is an arbitrary transaction the contract will execute on its own authority. Winning a vote is not 'having a say' — it is signing as the protocol.
That framing is the whole security problem. Earlier in this rung you saw the honest path: propose, vote, queue, execute. A governance attack is the same machinery, driven by someone whose goal is to drain the vault, not improve the protocol. And because the rules are 'whoever holds the most governance tokens decides', every attack below is really one question: how cheaply can the attacker control enough voting weight, for just long enough, to pass one malicious proposal?
Renting a majority for one block: the Beanstalk flash-loan takeover
On 17 April 2022 the stablecoin protocol Beanstalk lost about $182 million in roughly 13 seconds — the span of a single Ethereum transaction. The attacker did not break the cryptography or find a reentrancy bug. They simply voted. The weapon was a flash loan: an uncollateralized loan that must be borrowed and repaid in the same transaction, which lets anyone wield a billion dollars of capital for a few hundred milliseconds. Beanstalk made the fatal mistake of counting live token balances as voting power, so capital that exists for one block could vote for one block.
Two days earlier the attacker had quietly submitted a malicious proposal (BIP-18) whose payload, dressed up with a donation to Ukraine to look benign, actually executed an arbitrary call that swept the protocol's assets to themselves. Beanstalk had an `emergencyCommit` function that would execute any proposal immediately — bypassing the normal 7-day wait — the moment it reached a two-thirds supermajority of the Stalk governance token. That instant execution was the second fatal flaw: even an honest timelock is useless if there is a back door that skips it.
- Borrow ~$1 billion in stablecoins and ETH via flash loans from Aave, Uniswap, and SushiSwap — all inside one transaction, zero collateral.
- Convert the loan into Beanstalk LP tokens and deposit them into the protocol's Silo, instantly minting a supermajority (~67%) of all Stalk voting power.
- Call emergencyCommit on the pre-submitted BIP-18. The contract sees a two-thirds majority and executes the proposal's payload right there.
- The payload transfers the protocol's entire treasury to the attacker. Withdraw, unwind the positions, repay all flash loans, and keep the rest.
- Net profit: roughly $76 million walked away, ~$182 million in protocol value destroyed — and the borrowed billion never had to persist beyond one atomic transaction.
The fix in code: snapshot the past, delay the future
The flash-loan class of attack dies the instant voting power is read from a block in the past rather than the live balance. If a proposal's snapshot block is fixed when the proposal is created, then tokens borrowed afterward have zero historical weight — there is no block in the past where the attacker held them. This is exactly why OpenZeppelin and Compound governors are built on a checkpointed token (`ERC20Votes`) and read `getPastVotes`, and why snapshot voting is the single most important defense in this guide. Here is the vulnerable pattern Beanstalk effectively had:
// VULNERABLE: voting weight read from the CURRENT balance.
// A flash loan can borrow millions of tokens, vote, and repay
// in the SAME transaction -- the balance only needs to exist for
// one block, and that is exactly how long it needs to vote.
contract NaiveGovernor {
IGovToken public immutable gov;
function castVote(uint256 proposalId, bool support) external {
uint256 weight = gov.balanceOf(msg.sender); // <-- LIVE balance
_tally(proposalId, support, weight);
}
// Worse: instant execution with no timelock back door.
function emergencyCommit(uint256 proposalId) external {
require(_forVotes(proposalId) * 3 >= gov.totalSupply() * 2,
"no 2/3 supermajority");
_execute(proposalId); // arbitrary call -> drains the treasury NOW
}
}// SAFE: weight comes from a PAST block fixed when the proposal
// was created. Tokens acquired after that block have ZERO power,
// so a flash loan within the voting tx cannot help.
function _getVotes(address account, uint256 snapshotBlock)
internal view returns (uint256)
{
// ERC20Votes stores per-block checkpoints; you must delegate
// to yourself (even self-delegation) for a balance to count.
return token.getPastVotes(account, snapshotBlock); // historical
}
// AND every approved proposal flows through a Timelock: even a
// passed proposal must wait (e.g. 2 days) before it can touch
// the treasury, giving honest users time to see it and exit/fork.
function execute(uint256 proposalId) external {
require(state(proposalId) == ProposalState.Queued, "not queued");
require(block.timestamp >= eta[proposalId], "timelock not elapsed");
_execute(proposalId);
}When nobody is voting: whale capture and the hostile takeover
Not every attack needs a flash loan. The quieter danger is one-token-one-vote plus voter apathy. In most DAOs, only a few percent of the token supply ever shows up to vote. That turns the real quorum into a small, capturable number: if 5% of tokens decide outcomes, an attacker who accumulates a bit over 5% — patiently, on the open market, looking like an ordinary buyer — can pass proposals against the wishes of the silent 95%. This is plutocracy by default: wealth is power, and low participation makes wealth cheap.
Build Finance DAO (February 2022) is the textbook case. An attacker bought up the cheap, thinly-held BUILD governance token until they could pass a proposal granting themselves the contract's minting and control keys. The first hostile proposal failed; a second one passed because almost nobody was watching. They then minted a flood of new tokens and drained roughly $470,000 — a complete, legitimate-looking governance takeover, with no exploit at all. The protocol's own rules handed over the keys.
Buying the votes outright: bribery markets and self-serving proposals
If votes are transferable tokens, then voting power has a price — and where there is a price, a market appears. The most famous example is the 'Curve wars'. Curve directs its CRV emissions to liquidity pools according to votes from vote-escrowed CRV holders, so protocols that want emissions to their pool simply bribe those voters. Whole marketplaces (Votium, Hidden Hand, bribe.crv) grew up to let anyone pay token holders to vote a certain way. This 'vote-buying' is mostly legal and out in the open, but it reveals the uncomfortable truth: token-weighted governance can route a protocol's decisions to the highest bidder rather than its long-term health.
A darker cousin is using stolen or borrowed tokens to vote yourself out of trouble. In the Mango Markets exploit (October 2022), the attacker first used oracle manipulation to pump the price of MNGO, borrowed ~$114 million against the inflated collateral, and drained the protocol. Then came the governance twist: holding the MNGO tokens bought with the loot, the attacker submitted and voted for their own proposal — one that would let them keep roughly $47 million as a 'bounty' and waive criminal liability. The proposal failed at execution, but it shows governance being used as the getaway car, not the break-in.
A defense-in-depth checklist for DAO designers
No single control stops every governance attack; you layer them so that an attacker must defeat all of them at once. Read the list as a sequence of independent gates between 'attacker has tokens' and 'attacker has your treasury':
- Snapshot voting power at a past block fixed at proposal creation — the non-negotiable defeat for flash-loan and same-block vote-buying attacks.
- Route every approved proposal through a mandatory timelock (e.g. 2-7 days) with NO emergency bypass — reaction time is your last line of defense.
- Set a real quorum so a passive majority cannot be overruled by a tiny engaged minority, and a proposal threshold high enough to deter spam proposals.
- Encourage delegation so engaged, accountable delegates concentrate honest voting power against passive whales who never show up.
- Add a guardian or security council (often a multisig) that can VETO or cancel a queued proposal, but deliberately cannot itself spend the treasury.
- Separate the value-bearing token from the voting token, or lock voting tokens for long periods, so rented or borrowed influence carries real cost and time risk.
Finally, treat governance like any other high-value contract surface: read the post-mortems of Beanstalk, Build Finance, and Mango, and ask of your own system, 'what is the cheapest way to control a majority for one block, and what happens in the seconds after a malicious proposal passes?' If the honest answer is 'instant, total loss', you have not built governance — you have built a vault whose combination is for sale. The art is making an attack cost more, and move slower, than the treasury is worth.