Flash Loan Attack
How attackers use uncollateralized flash loans to manipulate prices, exploit protocol logic, and drain funds within a single transaction.
Overview
Flash loans are a DeFi primitive that allows anyone to borrow virtually unlimited capital — hundreds of millions of dollars — with zero collateral, provided the loan is repaid within the same transaction. If repayment fails, the entire transaction reverts as if nothing happened. This atomic guarantee, while innovative, has become one of the most powerful weapons in an attacker’s arsenal.
Attackers exploit flash loans to temporarily acquire massive capital, use it to manipulate on-chain state (most commonly prices on decentralized exchanges), and then exploit protocols that rely on that manipulated state. The borrowed capital amplifies the attack far beyond what the attacker could achieve with their own funds. Total historical losses from flash loan attacks exceed $90M, and the attack vector remains active.
Flash loan attacks almost always combine with oracle manipulation — specifically targeting protocols that read spot prices from DEX reserves rather than using time-weighted or off-chain oracle feeds. The pattern is devastatingly effective because it requires zero upfront capital, carries no liquidation risk, and completes in a single atomic transaction.
How This Attack Works
From the attacker’s perspective, a flash loan attack follows a precise sequence that executes atomically within one transaction:
Step 1: Identify a vulnerable protocol. The attacker scans for protocols that rely on spot prices from DEX pools, use single-block price readings, or lack circuit breakers on price-sensitive operations. Any protocol where a large capital injection can shift an internal price or state variable is a candidate.
Step 2: Flash borrow large capital. The attacker borrows millions in tokens from a flash loan provider (Aave, dYdX, Balancer, or others). The only cost is a small fee (typically 0.09%), paid only if the attack succeeds. If the attack fails, the transaction simply reverts.
Step 3: Manipulate the target. Using the borrowed capital, the attacker performs large swaps on a DEX to shift the spot price of a target token. For example, dumping 10,000 ETH into a Uniswap pool will dramatically shift the ETH/token ratio in that pool.
Step 4: Exploit the manipulated state. With prices now distorted, the attacker interacts with the vulnerable protocol. This might mean depositing artificially inflated collateral to borrow at favorable rates, minting governance tokens at a manipulated exchange rate, or withdrawing vault shares at a distorted NAV.
Step 5: Reverse the manipulation or exit. The attacker unwinds the price manipulation by swapping back, or simply exits the position. The target protocol is left holding the loss.
Step 6: Repay the flash loan. The attacker repays the borrowed amount plus the fee. Everything remaining is pure profit.
Step 7: All of this happens atomically. If any step fails — if the profit doesn’t cover the loan plus fee — the entire transaction reverts. The attacker loses nothing except gas fees for the failed attempt.
Attack Contract Structure
The following Solidity example illustrates the general structure of a flash loan attack contract:
contract FlashLoanAttack {
IFlashLoanProvider public lender;
IVulnerableProtocol public target;
IERC20 public token;
function executeAttack() external {
// Step 1: Borrow massive capital
lender.flashLoan(address(this), address(token), 10_000_000e18, "");
}
function onFlashLoan(
address initiator,
address tokenAddr,
uint256 amount,
uint256 fee,
bytes calldata
) external returns (bytes32) {
// Step 2: Manipulate price on DEX
token.approve(address(dex), amount);
dex.swap(address(token), address(targetToken), amount);
// Step 3: Exploit manipulated price
target.deposit(targetToken.balanceOf(address(this)));
target.borrow(token, inflatedAmount);
// Step 4: Reverse manipulation
dex.swap(address(targetToken), address(token), targetToken.balanceOf(address(this)));
// Step 5: Repay loan + fee, profit remains
token.approve(address(lender), amount + fee);
return keccak256("ERC3156FlashBorrower.onFlashLoan");
}
}
The key insight is that the entire attack — borrow, manipulate, exploit, unwind, repay — occurs inside the onFlashLoan callback. The flash loan provider calls this function after transferring the borrowed tokens, and expects repayment by the time it returns.
Historical Exploits
bZx Protocol ($350K, February 2020)
The bZx attack in February 2020 was the first major flash loan exploit and established the template that subsequent attackers would follow for years.
The attacker flash borrowed 10,000 ETH from dYdX. They then split this capital: 5,500 ETH was swapped for sUSD on Uniswap, which dramatically moved the sUSD/ETH spot price on that pool. The remaining ETH was used as collateral on bZx’s Fulcrum platform. Because bZx referenced Uniswap’s manipulated spot price to value the sUSD collateral, the attacker was able to borrow at an extremely favorable rate — effectively borrowing more than their collateral was worth at true market value.
The attacker profited approximately $350K from this single transaction. More importantly, this attack proved the concept that flash loans could amplify price manipulation attacks to devastating effect, and it opened the floodgates for a wave of copycat exploits.
Harvest Finance ($34M, October 2020)
- Contract:
0xbc2431bbd2c0e7f67f8239afa1e0f7bba35d9a4a
The Harvest Finance exploit was a masterclass in repetitive flash loan cycling. The attacker flash borrowed 50M USDC and 11.4M USDT, then used these funds to manipulate the virtual price in a Curve Finance pool.
The attack sequence was elegant in its simplicity: the attacker swapped large amounts in the Curve pool to shift the pool’s internal price calculation, then deposited into the Harvest vault while the price was inflated (receiving more vault shares than deserved). They then swapped back to reverse the price manipulation and withdrew from the vault at the deflated price (receiving more underlying tokens than the shares were worth). This deposit-at-high/withdraw-at-low cycle was repeated 32 times within a single transaction, draining approximately $34M from the protocol.
After the attack, the attacker returned $2.5M to the Harvest deployer address, suggesting either partial ethical concerns or an attempt to reduce the likelihood of aggressive pursuit.
PancakeBunny ($45M, May 2021)
- Network: Binance Smart Chain (BSC)
The PancakeBunny attack exploited the protocol’s on-chain price calculation for minting its BUNNY governance token. The attacker flash borrowed a massive amount of BNB from PancakeSwap, used it to manipulate the spot price of the BUNNY token via large swaps, and then called PancakeBunny’s minting function.
Because PancakeBunny’s smart contracts used the manipulated spot price to determine how many BUNNY tokens to mint as rewards, the attacker received a vastly inflated amount of BUNNY tokens. They immediately sold these tokens on the open market, crashing the BUNNY price by 96%. The token and protocol never recovered to their pre-attack valuations.
This attack highlighted a critical lesson: governance token minting functions that rely on spot prices are extremely dangerous. The attacker needed zero BUNNY tokens to start and walked away with $45M.
Yearn Finance yDAI ($11M, July 2020)
The Yearn Finance yDAI vault was exploited through flash loan manipulation of Balancer pool prices. The attacker leveraged the flash-borrowed capital to shift the price that the yDAI vault used for its internal accounting, allowing them to extract $11M in value from the vault. This attack reinforced that any yield vault using on-chain spot prices for share pricing is vulnerable to flash loan manipulation.
Detection Patterns
The following on-chain signals indicate a potential flash loan attack in progress or a protocol vulnerable to one:
-
Large single-transaction volume. Any transaction moving millions of dollars in value — particularly involving multiple DEX swaps and protocol interactions — warrants investigation. Legitimate users rarely execute $10M+ multi-step transactions.
-
Price changes exceeding 10% within a single block. Spot prices on DEX pools that shift dramatically within one block are a strong indicator of manipulation. Normal market activity rarely causes double-digit price swings in a single block.
-
Spot price oracle usage without TWAP. Protocols that read
getReserves()directly from a Uniswap pair or equivalent — rather than using a time-weighted average price (TWAP) — are structurally vulnerable. -
Missing price bounds or circuit breakers. If a protocol accepts any price without checking whether it deviates significantly from a known baseline, it is likely exploitable.
-
No minimum liquidity requirements for price feeds. Protocols that trust prices from low-liquidity pools are especially vulnerable, as less capital is needed to move the price.
-
Same-block deposit and withdrawal patterns. Flash loan attacks require borrowing, exploiting, and repaying within a single transaction. Detecting deposit-then-withdraw patterns in the same block can flag attacks in progress.
Mitigation Strategies
1. Use TWAP Oracles
Time-Weighted Average Price (TWAP) oracles aggregate prices over a window of time, making them resistant to single-transaction manipulation. An attacker would need to sustain the price manipulation across many blocks, which is economically prohibitive.
// Vulnerable: Spot price
function getPrice() public view returns (uint) {
return uniswapPair.getReserves(); // Manipulable in one tx!
}
// Secure: Time-Weighted Average Price
function getPrice() public view returns (uint) {
return oracleLibrary.consult(
pair, token, amount, 1800 // 30-minute TWAP
);
}
A TWAP window of 30 minutes is generally considered safe. Shorter windows (under 10 minutes) still leave some attack surface, particularly on low-liquidity pairs.
2. Chainlink Price Feeds
Off-chain oracle networks like Chainlink aggregate prices from multiple sources and are not influenced by on-chain DEX state. They provide a robust defense against flash loan-driven price manipulation.
import "@chainlink/contracts/src/v0.8/interfaces/AggregatorV3Interface.sol";
function getPrice() public view returns (uint256) {
(, int256 price,, uint256 updatedAt,) = priceFeed.latestRoundData();
require(block.timestamp - updatedAt < 3600, "Stale price");
require(price > 0, "Invalid price");
return uint256(price);
}
Always validate that the price feed data is fresh (check updatedAt) and that the returned price is positive. Stale or zero prices can indicate oracle failure and should trigger a circuit breaker.
3. Price Deviation Bounds
Implement circuit breakers that reject transactions when the price deviates beyond a threshold from a known reference. This limits the damage even if an attacker can manipulate one price source.
function validatePrice(uint256 newPrice) internal view {
uint256 lastPrice = getLastPrice();
uint256 deviation = newPrice > lastPrice
? ((newPrice - lastPrice) * 10000) / lastPrice
: ((lastPrice - newPrice) * 10000) / lastPrice;
require(deviation <= 1000, "Price deviation > 10%"); // Circuit breaker
}
A 10% deviation threshold is a reasonable starting point, but the appropriate value depends on the asset’s normal volatility. Highly volatile assets may need a wider band, while stablecoins should use a much tighter bound (1-2%).
4. Deposit/Withdraw Delays
Flash loan attacks fundamentally require that the borrow, exploit, and repay all happen within a single transaction. By introducing a minimum delay between deposits and withdrawals — even a single block — protocols can break this atomic requirement.
This can be implemented by recording the block number at deposit time and requiring that withdrawals only occur in a subsequent block:
- Record
block.numberwhen a user deposits. - Reject withdrawals where
block.number == depositBlock. - This forces attackers to hold positions across blocks, exposing them to arbitrage and eliminating the risk-free nature of flash loan attacks.
The tradeoff is a slight reduction in user experience for legitimate users who must wait one block before withdrawing. For most protocols, this is an acceptable cost given the protection it provides.