All articles
Security FundamentalsJune 14, 20269 min read

Flash Loans and Security: What Changes When Capital Is Free

Flash loans amplify oracle manipulation, governance attacks, and accounting drift. Real exploits, defensive patterns, and what to check when auditing a protocol that allows them.

By Carlos (Bloqarl)

TL;DR

  • A flash loan is a loan that has to be repaid within the same transaction. The protocol issuing the loan checks at the end of the transaction whether the loan was repaid; if not, the entire transaction reverts.
  • This makes capital effectively free for the duration of one block. An attacker with $0 in their wallet can briefly control billions of dollars to execute multi-step attacks.
  • Three attack classes amplified by flash loans: oracle manipulation (bZx 2020 ~$1M, the first flash-loan exploit), governance attacks (Beanstalk $182M, 2022, flash-loaned 67% of governance), accounting drift (Cream's various exploits, $130M cumulative).
  • Defenses: TWAP oracles instead of spot prices, snapshot-based governance instead of execution-time vote weight, atomic invariant checks at the end of every state-changing function.
  • If you're auditing a protocol, every spot-price read, every governance vote, every reserve-based calculation is a flash-loan attack surface. The audit checklist is here.

Why this matters

Before flash loans, smart contract security implicitly assumed attackers had limited capital. A vulnerability that required $100M to exploit was theoretically possible but practically irrelevant: who has $100M in cash and wants to risk it on a smart contract bug?

After flash loans, that assumption is wrong. Anyone with $0 and a modicum of Solidity skill can briefly control $100M. They have one transaction to use it, and if they fail, they pay nothing (because the entire flash-loan transaction reverts). The risk-reward is asymmetric: huge upside on success, zero downside on failure.

This changes the threat model for almost every DeFi protocol. Spot-price oracles that were "fine" in 2018 became unsafe in 2020. Governance vehicles that assumed "no one has 67% of the supply" became unsafe in 2022. Accounting tricks that relied on reserve invariants became unsafe whenever a protocol exposes a reserve-based callback.

If you're building or auditing DeFi, you have to model the flash-loan-enabled attacker. Failing to do so is the most common vulnerability class outside of plain reentrancy.

What a flash loan actually is

The mechanism is simple. A protocol (Aave, dYdX, Balancer, Maker) holds a large pool of capital. The protocol exposes a function:

function flashLoan(uint amount, bytes calldata data) external {
    uint balanceBefore = token.balanceOf(address(this));
    
    token.transfer(msg.sender, amount);  // give attacker the money
    
    IFlashBorrower(msg.sender).onFlashLoan(amount, data);  // attacker's logic runs here
    
    uint balanceAfter = token.balanceOf(address(this));
    require(balanceAfter >= balanceBefore + fee, "FLASH_LOAN_NOT_REPAID");
}

Three steps:

  1. Loan issued: tokens move to the borrower.
  2. Borrower's logic runs: any code the borrower wants to execute, including swapping, depositing into other protocols, manipulating prices, voting in governance, etc.
  3. Loan repaid: the protocol checks its balance increased by at least amount + fee. If not, the entire transaction reverts.

The "transaction reverts" mechanism is what makes the loan free of credit risk for the protocol. If the borrower can't repay, the loan never happened. If the borrower can repay, the protocol earned a small fee.

Aave's flash loan fee is 0.09% of the borrowed amount. dYdX historically charged $1 per loan regardless of size. The fee is small enough to make most attack scenarios still profitable; it's not a meaningful deterrent.

Attack class 1: oracle manipulation

The first published flash-loan exploit was bZx in February 2020. Approximately $1M loss. The attack:

  1. Flash-loan a large amount of WETH (say 10,000 WETH).
  2. Use the WETH to manipulate the price of a small token on a DEX (e.g., sUSD on Kyber).
  3. The bZx margin trading system reads the manipulated price as its oracle.
  4. Take a position based on the manipulated price, profitable to the attacker because the position is sized for "real" prices but executed at "fake" prices.
  5. Reverse the price manipulation (sell back the WETH).
  6. Repay the flash loan.
  7. Walk away with profit.

The pattern: any protocol that reads spot prices from a small DEX is vulnerable. Spot prices are by definition the current state of liquidity, which can be moved by sufficient capital.

After bZx, this pattern repeated multiple times:

  • Mango Markets ($114M, 2022): similar attack on MNGO's spot price.
  • Inverse Finance ($15.6M, 2022): spot-price oracle manipulated via flash-loaned position.
  • Bonq ($120M, 2023): spot-price manipulation on a small DEX feeding a stablecoin's collateral check.

The defense: TWAP oracles. A time-weighted average price integrates over a window (typically 30 minutes to a day), making single-block manipulation ineffective. Manipulating the TWAP requires sustained capital deployment over the window, which is impossible with a flash loan.

We covered this design choice in the Compound V2 oracle article and the dual-arithmetic article. Production protocols use Chainlink (which itself uses TWAP-like aggregation) or Uniswap V2/V3's TWAP accumulators.

If your protocol reads pair.getReserves() and computes a price from that, you have a spot-price oracle. Flash-loanable. Move to TWAP.

Attack class 2: governance attacks

The Beanstalk attack of April 2022 was the largest flash-loan governance exploit on record. $182M lost. The attack:

  1. Wait until Beanstalk had a low-friction governance proposal mechanism (24-hour delay between proposal and execution).
  2. Submit a malicious proposal: "transfer all protocol funds to attacker".
  3. Wait 24 hours. The proposal is valid; nobody has voted against it because it has 0 support.
  4. Flash-loan ~$1B in governance tokens (BEAN, BEAN3CRV, BEANLUSD).
  5. Vote those tokens in favor of the proposal. Achieve 67%+ of total supply voting yes.
  6. Execute the proposal. Funds transfer to attacker.
  7. Repay the flash loan.

The timing was the key: Beanstalk's governance had a delay between proposal and execution, but the vote weight was checked at execution time, not at proposal time. The attacker proposed early (waited the delay) and voted late (using flash-loaned tokens at execution time).

The defense: snapshot-based governance. Vote weight is locked at the proposal's submission, not at execution. Even if an attacker flash-loans tokens at execution time, those tokens have no vote weight (the snapshot was taken before they existed in the attacker's wallet).

OpenZeppelin's Governor contract implements snapshot-based voting by default. Compound's GovernorBravo does too. Beanstalk's bug was using a custom governance contract that didn't.

After Beanstalk, snapshot-based governance is the default expectation. If a protocol you're auditing uses execution-time vote weight, that's a flash-loan governance attack waiting to happen.

Attack class 3: accounting drift

Flash loans amplify any accounting bug where the protocol's invariants don't hold mid-transaction. The classic example is multi-step lending interactions where a flash-loaned position changes a global state variable mid-flight.

Cream Finance lost over $130M across two major exploits. One was a cross-contract reentrancy (covered in the reentrancy article). Another involved flash-loan-amplified accounting:

  1. Flash-loan a large amount of asset X.
  2. Deposit X into Cream as collateral. This grants borrow capacity.
  3. Borrow asset Y against the temporary collateral.
  4. Use Y to manipulate price/state somewhere (often via flash-loaning more from a second protocol).
  5. Repay the original flash loan, withdrawing the collateral.
  6. Walk away with the borrowed Y, plus profit from the manipulation.

The fix is multi-fold but anchored on atomic invariant checks: at the end of every state-changing function, assert that the protocol's invariants still hold globally. If a function temporarily violates an invariant during execution, the function must restore it before returning.

For lending: invariant is "total collateral value >= total debt value, at protocol-wide level". Check this at function exit. If a flash-loan-enabled flow leaves the protocol violating this, revert.

This is harder to implement correctly than it sounds. The invariant has to be efficient (re-computing collateral values across all markets is expensive) and complete (it has to capture all the relevant interactions).

Defensive patterns and audit checklist

When auditing a protocol that allows flash loans (either as the protocol issuing them or as a protocol that interacts with them), check these in order:

1. Oracle source

For every price read in the codebase:

  • Is the source a TWAP, Chainlink, or other manipulation-resistant oracle?
  • If the source is a DEX spot price, what's the depth of liquidity? Spot prices on small pools are flash-loanable.
  • Does the price feed have multiple sources with sanity checks (e.g., median of three)?

If any single price read uses unprotected spot pricing, that's a finding.

2. Governance vote weight

For every governance vote:

  • Is vote weight snapshotted at proposal time or execution time?
  • If snapshotted, what's the snapshot block? (Should be at proposal submission.)
  • Does the governance contract use OpenZeppelin's Governor or a custom implementation? Custom implementations need extra scrutiny.

3. Invariant checks

For every external function that changes state:

  • What are the protocol's invariants?
  • Are they checked at function exit?
  • Does the check cover all relevant state, not just the function's local view?

For lending protocols: the invariant is "no account has negative net worth". For DEXes: "K = x*y is non-decreasing". For derivatives: "total positions sum to zero" (pair contracts) or "PnL is conserved" (perp protocols).

4. Cross-protocol interactions

For every external call to another protocol:

  • Could the call involve a flash-loan callback?
  • If yes, what's the state of this protocol when the callback executes?
  • Could the callback exploit a temporary inconsistency in this protocol's state?

This is the hardest class to audit because it requires modeling interactions between protocols, including ones that didn't exist when the audit started.

5. Deposit/withdraw share math

For protocols that issue shares (cTokens, LP tokens, vault shares):

  • Can a flash-loan deposit + withdraw round trip yield more shares than deposited?
  • Are the math operations symmetric (deposit math is the inverse of withdraw math)?
  • Is the share math protected against the donation attack?

We covered the donation attack pattern in the Compound V2 fork article.

Related questions

Should I prevent flash loans of my token? Usually no. Flash loans are a tool, not a vulnerability. The vulnerability is the protocol that doesn't model flash-loan-enabled attackers. Preventing flash loans of your token (e.g., by adding transfer restrictions) breaks legitimate use cases like arbitrage and liquidations. Fix the protocol instead.

What about flash loans across multiple protocols? The attacker can compose flash loans (borrow from Aave, deposit into Compound, borrow against the deposit from Compound, etc.). All atomic, all reverted if any step fails. Modern attacks routinely use 5-10 protocols in one transaction.

Are there protocols where flash loans are net positive? Yes. Flash loans enable:

  • Arbitrage that closes price gaps (welfare-positive).
  • Liquidations of underwater positions (welfare-positive).
  • Refinancing across lenders (welfare-positive).
  • Collateral swaps (welfare-positive).

The welfare-negative use cases are the exploits. Net effect on the ecosystem is debated, but the consensus is positive.

Does the flash loan fee matter? For Aave: 0.09%. On a $100M loan, that's $90K. Significant for legitimate arbitrage (who profit margins are small) but trivial for exploits (who profit in the millions). The fee is a cost of doing business, not a deterrent.

What's the difference between flash loan and flash mint? Flash loan: borrow existing tokens. Flash mint: temporarily mint new tokens (e.g., DAI's flash mint). Same atomicity guarantee, slightly different mechanism. Same security implications.

Can my protocol detect that it's being called within a flash loan? No reliably. The transaction looks identical to a non-flash-loan transaction from the protocol's perspective. You have to assume any call could be flash-loan-enabled and design accordingly.

Where to see this in Academy

The Shadow Arena targets include several real protocols where flash-loan attacks were documented. Auditing them lets you practice the audit checklist above against real production-like code.

The Uniswap V2 reconstruction (covered in the dual-arithmetic article) implements the TWAP oracle, which is the primary defense against flash-loan oracle manipulation. The test suite verifies the TWAP correctly resists single-block manipulation.

The eMBA pillar (Module 3 specifically) covers flash-loan-aware governance design as part of the broader security-from-day-one curriculum. If you're building a protocol with governance, the lessons there cover snapshot voting, timelock mechanics, and the Beanstalk failure mode in detail.

Tagged

Smart Contract SecurityFlash LoansDeFi