Skip to content
#10 largest Governance 2022

The Beanstalk Farms hack — $181M lost

Loss$181M
Date17 Apr 2022
ChainEthereum
Failure classGovernance
In assetsvoted, passed and executed in one transaction
Targetalgorithmic stablecoin
1

What happened

Beanstalk's governance measured voting power from a wallet's current token balance, and offered an emergencyCommit path that executes a proposal 24 hours after nomination if it holds a two-thirds supermajority. The attacker submitted BIP-18 (transfer everything to me) and BIP-19 (a $250k donation to Ukraine, as cover), waited out the 24 hours, then flash-loaned roughly $1 billion from Aave, Uniswap and SushiSwap, converted it into Beanstalk LP tokens, voted with it, committed, repaid the loan and left — all inside a single block.

2

How the attack ran

  1. Submit BIP-18“Transfer the protocol’s assets to me”
  2. Wait 24 hoursThe emergency-commit window opens
  3. Votes = current balanceFlash-loan ~$1B, vote, commit — all in one transaction
  4. Executed in a single block$181M out, the loan repaid before the block closed
3

The code

Governance.sol — a flash loan is a balance
function emergencyCommit(uint32 bip) external {
    require(isNominated(bip),  "Governance: Not nominated.");
    require(block.timestamp >= timestamp(bip).add(C.getGovernanceEmergencyPeriod()),
                              "Governance: Too early.");
    require(isActive(bip),     "Governance: Ended.");
    require(canPropose(msg.sender), "Governance: Not enough Stalk.");
    // ❌ voting power read from the CURRENT balance, this block
    require(bipVotePercent(bip).greaterThanOrEqualTo(C.getSuperMajority()),
                              "Governance: Must have supermajority.");
    _execute(msg.sender, bip, false, true);
}

// ✅ the fix is one word: snapshot.
//    votes = balanceOfAt(voter, proposal.snapshotBlock)
//    A flash loan cannot rewrite a block that already happened.
4

What would have caught it

What an audit looks for: any privilege priced by a spot balance is for rent. Voting power, collateral weight, fee tiers, reward multipliers — if it reads balanceOf() at execution time, assume an attacker holds an unlimited amount of it for one transaction.
6

Sources

Every figure on this page comes from the post-mortems above, not from us. Losses are US dollars at the time of the incident.

Check your own contract for this

Governance is one of the 203 classes the SaferICO scanner checks for. It will not review your signing process — but it will read your Solidity.

Run the scanner See how it is attacked Read the docs