Flash Loan Attack: How They Work and How to Defend Against Them

N
Narrow

TL;DR

  1. Flash loans are not a vulnerabilit
  2. y — they are an execution primitive that removes capital constraints. When an attacker uses one, the real damage comes from a pre-existing flaw in the target protocol: a missing health check, a manipulable price oracle, or a governance system with no time delay. Fixing flash loan attacks means fixing protocol invariants, not restricting how loans work.

Introduction

Every major DeFi hack that involves a flash loan gets labeled a “flash loan attack” in the news cycle. The term is accurate but misleading: it names the funding mechanism, not the vulnerability.

A flash loan is an uncollateralized loan that must be borrowed and fully repaid within the same transaction. Providers like Aave make this possible without any upfront collateral because of a single guarantee: if the borrowed funds are not returned before the transaction ends, the entire transaction reverts automatically. Nothing is lost.

Their power comes from transaction atomicity. Either every operation in the sequence succeeds, and all state changes are committed, or any single failure causes the entire chain of operations to revert, as if nothing happened. This means a researcher or an attacker can access millions of dollars in liquidity for the duration of one block, execute complex multi-protocol logic, and either profit or walk away with no financial exposure.

Flash loans do not create protocol weaknesses. They remove capital constraints, which lets an attacker operate at a scale that was previously unavailable without upfront funds. In this article, we cover how flash loans work end-to-end, how they get integrated into exploit flows, and why the Euler Finance exploit is one of the clearest examples of where the actual root cause lives.

How Flash Loans Work

When a borrower calls a flash loan function on a lending provider, the sequence looks like this:

  1. The provider transfers the requested liquidity to the borrower’s contract.
  2. The provider immediately calls a callback function on the borrower’s contract.
  3. Inside the callback, the borrower has full control of the borrowed funds and can execute any arbitrary logic — swaps, arbitrage, collateral moves, liquidations, interactions with other protocols.
  4. Before the callback returns, the borrower must transfer back the original amount plus the provider’s fee.
  5. The provider checks that repayment conditions are met. If they are, the transaction is committed. If not, everything reverts.

The key design constraint is step 5. The flash loan provider does not need to trust the borrower because it has an atomic guarantee: if repayment fails, the loan never happened. This is what makes uncollateralized lending possible on-chain.

Pseudocode: the borrower side

contract FlashLoanReceiver {
    function startFlashLoan(address asset, uint256 amount) external {
        flashLoanProvider.flashLoan(address(this), asset, amount);
    }

    function onFlashLoan(address asset, uint256 amount, uint256 fee) external {
        // 1. Use the borrowed funds while this transaction is still executing.
        executeStrategy(asset, amount);

        // 2. Repay the loan before the callback finishes.
        IERC20(asset).transfer(msg.sender, amount + fee);
    }
}

executeStrategy() runs before repayment, but both are inside the same atomic transaction. A failed repayment does not leave the provider short — it erases the entire execution as if it never occurred.

How Flash Loans Enable Exploits

Most smart contract exploits require significant capital to become economically viable. Without enough liquidity, an attacker may not be able to move a price oracle far enough, meet a collateral threshold, or extract a profit large enough to cover gas and complexity.

Flash loans change this calculus. Any protocol flaw that becomes exploitable at scale is suddenly accessible to an attacker with no upfront capital, no borrowing history, and no counterparty relationship. The only cost is the gas fee and the loan fee, which are negligible compared to potential profits.

A typical flash-loan-assisted exploit follows this structure:

  1. Borrow large temporary liquidity from Aave, Uniswap v3, dYdX, or another provider
  2. Execute operations that interact with the vulnerable protocol — often across multiple contracts in one transaction
  3. Extract the value created by the manipulated state
  4. Repay the flash loan plus fee from the extracted funds
  5. Keep the remaining profit
Flowchart showing the four steps of a flash loan transaction — request loan, funds sent, execute logic, repay plus fee — with two outcome branches: a success path where repayment is verified and state changes commit, and a failure path where missing repayment causes the whole transaction to revert.

The important clarification

The exploit still depends on a pre-existing protocol flaw. If the protocol behaves correctly under arbitrarily large capital, a flash loan alone cannot create an attack. The loan is the amplifier, not the vulnerability.

Real-World Case Study: Euler Finance (2023)

The Euler Finance exploit is worth studying in detail because it is frequently described as a flash loan attack when the actual vulnerability had nothing to do with flash loan mechanics.

Euler used an internal accounting model built around two token types: eTokens representing deposited collateral, and dTokens representing outstanding debt. A user’s account health was determined by the ratio between these two balances.

Euler also exposed a mint() function that let users create synthetic leveraged positions. Instead of manually cycling through deposit-borrow-redeposit loops, mint() could inflate both the eToken collateral side and the dToken debt side simultaneously, effectively creating a leveraged position in one call.

This mechanism was not inherently unsafe. Leveraged positions can be valid as long as account health is verified after every state change.

The Root Cause: donateToReserves() Without a Health Check

Euler introduced a donateToReserves() function to allow users to voluntarily contribute eTokens to the protocol’s reserve pool. Mechanically, this function reduced the caller’s eToken balance, which meant it reduced the collateral side of their position.

The debt side — the dToken balance — was not reduced. Only collateral moved.

In any lending protocol, an operation that reduces collateral or increases debt must be followed by a health check. If the remaining collateral no longer covers the outstanding debt, the protocol must reject the operation or immediately initiate liquidation.

donateToReserves() skipped this check. It allowed the account’s collateral to fall below what was needed to cover its debt without any validation.

The vulnerable pattern

function donateToReserves(uint256 amount) external {
    // User gives up part of their collateral position.
    eTokenBalance[msg.sender] -= amount;
    reserves += amount;

    // Missing check:
    // require(isAccountHealthy(msg.sender), "account unhealthy");
}

The actual vulnerability

The dangerous part is not the donation itself. It is allowing collateral to decrease without verifying whether the remaining collateral still covers the outstanding debt. This violated a fundamental lending invariant: a borrower’s collateral must always remain sufficient to secure their debt.

The Exploit Flow, Step by Step

  1. Attacker borrowed approximately $30M in DAI using a flash loan from Aave.
  2. Deposited the borrowed DAI into Euler, receiving eDAI (collateral tokens).
  3. Called Euler’s mint() to synthetically amplify the position — creating far more eDAI and dDAI than the original deposit justified. This produced a large collateral balance and a correspondingly large debt balance.
  4. Called donateToReserves() to donate a significant portion of the eDAI collateral into protocol reserves. The collateral balance dropped sharply; the debt balance did not move.
  5. Because no health check was ran after the donation, the account was allowed to remain in an undercollateralized state.
  6. A second attacker-controlled contract identified the unhealthy account and liquidated it, capturing the liquidation discount as profit.
  7. The attacker repaid the Aave flash loan and retained the remaining extracted value.
Step-by-step diagram of the Euler Finance 2023 exploit. Seven numbered steps flow in a loop: flash loan provides temporary liquidity → funds deposited into Euler → mint() creates eTokens and dTokens → donateToReserves() reduces eToken collateral → no health check runs while debt remains → second contract captures the liquidation reward → flash loan is repaid and leftover value is profit. A callout at the bottom states the broken invariant: a borrower's collateral must remain sufficient to secure outstanding debt.

The flash loan funded the operation and made it scalable. But the protocol failed because donateToReserves() allowed a state transition that broke the core solvency invariant without any validation.

Flash Loan Attack Prevention: How Protocols Can Defend

Designing for flash loan resistance means designing around the assumption that capital is free and unlimited. Attackers using flash loans can access any amount of liquidity that exists in on-chain pools. Any protocol that can be broken with enough capital is a protocol with a design flaw, not a flash loan problem.

Below are the four main vectors and what effective flash loan attack prevention looks like in practice.

1. Lending Protocols: Enforce Health Checks After Every Risky State Change

Any operation that can reduce the collateral-to-debt ratio — including collateral withdrawal, donations, fee deductions, or collateral value adjustments — must trigger an account health evaluation before the transaction completes. This check cannot be optional or deferrable.

The Euler exploit is the clearest example of what happens when this rule is violated. The fix is not complicated: add a health check. But skipping it for gas optimization or convenience makes the protocol exploitable.

Rule

Every function that touches collateral or debt must end with a solvency assertion, not just the ones that obviously change borrowing capacity.

2. Oracle-Dependent Protocols: Do Not Trust Spot Prices From AMMs

Flash loans can move the price of an asset inside a low-liquidity AMM pool significantly within one transaction. If a protocol uses that spot price as its reference for collateral valuation, liquidation triggers, or interest rate calculations, an attacker can manipulate the price, exploit the downstream protocol behavior, and let the price revert, all atomically.

Defenses include: time-weighted average prices (TWAPs) over multiple blocks, decentralized oracle networks like Chainlink that source prices externally, liquidity depth checks before trusting an AMM price, and maximum deviation thresholds that reject outlier prices.

3. Swap and Vault Logic: Use Slippage Limits and Avoid Mutable Balance Assumptions

Contracts that calculate exchange ratios, vault share prices, or redemption amounts based on current token balances can be manipulated if those balances can be changed atomically inside the same transaction. An attacker can front-load a swap to distort the balance before the target function reads it.

Slippage limits and minimum output checks protect against this at the trade level. Vault contracts should calculate share prices against committed, immutable snapshots rather than live pool balances.

4. Governance Systems: Add Time Delays Between Voting and Execution

Governance protocols that allow immediate execution of passed proposals are vulnerable to flash loan governance attacks. An attacker can borrow a large token supply, acquire enough voting power to pass a proposal in a single block, execute it, and repay the loan — all atomically.

The standard defense is a time lock: require a delay between when a vote passes and when it can be executed. Because flash loans must be repaid within the same transaction, borrowed voting power cannot survive across blocks. A time lock of even a few hours eliminates this attack vector.

General rule for flash loan attack prevention

If a value can be manipulated within one transaction, never treat it as a stable source of truth in the same transaction. This includes prices, balances, voting power, and collateral ratios.

What This Means for Security Researchers

When reviewing a DeFi protocol for flash-loan-related vulnerabilities, the most productive question is not “can this be funded with a flash loan?” — almost anything can. The productive question is: “What invariant breaks if I have unlimited capital for one transaction?”

Concrete things to look for:

  • Health checks that are conditional, deferred, or skipped in specific code paths (donation functions, emergency withdrawals, fee mechanisms, reward distributions)
  • Price references that read from spot AMM balances rather than time-weighted or externally sourced values
  • Share price calculations in vaults that use live token balances are susceptible to donation attacks
  • Governance contracts without time locks or snapshot-based voting power
  • Reentrancy paths that allow an attacker to re-enter a function before a state update is committed — often combinable with flash loan capital to amplify the effect

Flash-loan-assisted exploits are not a distinct vulnerability class. They are existing vulnerability classes executed with temporarily unlimited capital. Understanding which invariant fails is the analytical work. The flash loan is just the delivery mechanism.

Conclusion

Flash loans are execution primitives, not vulnerabilities. Aave, Uniswap v3, and similar providers made uncollateralized on-chain lending possible by relying on transaction atomicity — if the loan is not repaid, the transaction reverts. That guarantee is the design. The protocol does exactly what it is supposed to do.

When a flash loan appears in an exploit, the underlying story is always about a protocol that allowed an invalid state transition under certain conditions, and where more capital made that transition more profitable or more likely to succeed. In Euler, it was a collateral reduction without a health check. In price manipulation attacks, it is a spot price read from a pool that can be moved. In governance attacks, it is voting power with no time-locked separation from execution.

Understanding flash loan attacks as a researcher means identifying which invariant the protocol relies on, determining whether that invariant holds under unlimited capital, and asking what happens when it does not.

The answer to that question — not the presence of a flash loan — is where the actual vulnerability lives.

Share article:

Read more on HackenProof Blog