Flash loan–facilitated attacks describe exploits where an attacker uses uncollateralized, same-transaction borrowing (flash loans) to amplify an underlying vulnerability into a profitable, protocol-draining attack. Flash loans are not inherently vulnerable—they are a legitimate DeFi primitive—but they grant attackers arbitrarily large, transient capital within a single transaction. Any protocol that makes trust assumptions based on capital at risk or historical position size is exposed when an attacker can temporarily hold massive balances without putting their own funds at risk.
This affects all contract types that assume economic constraints or proportional exposure: lending protocols (governance vote weight, liquidation thresholds, collateral ratios), AMMs (share minting, arbitrage, oracle skew), yield vaults (deposit/withdraw/share accounting), governance (vote buying, flash-loan governance attacks), NFT and token valuation (floor price, collateral valuation), and cross-protocol composability where one contract’s state is influenced by another’s liquidity. On non-EVM chains, similar concepts exist (e.g., transient large positions within a single block or batch).
Few areas to focus on:
Attackers construct batched transactions that:
When underlying weaknesses in business logic (SC02), oracles (SC03), arithmetic (SC07), or access control (SC01) exist, flash loans act as a force multiplier, turning small bugs into catastrophic exploits.
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
interface IFlashLender {
function flashLoan(
address receiver,
uint256 amount,
bytes calldata data
) external;
}
contract VulnerablePool {
uint256 public totalShares;
uint256 public totalAssets;
mapping(address => uint256) public sharesOf;
// Vulnerable: naive share minting with truncation benefit to sender
function deposit(uint256 assets) external {
uint256 shares;
if (totalShares == 0) {
shares = assets;
} else {
shares = (assets * totalShares) / totalAssets;
}
totalAssets += assets;
totalShares += shares;
sharesOf[msg.sender] += shares;
}
// No safeguard against flash-loan boosted deposit/withdraw loops
}
Issues:
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
contract SaferPool {
uint256 public totalShares;
uint256 public totalAssets;
uint256 public lastUpdateBlock;
mapping(address => uint256) public sharesOf;
error TooFrequentInteraction();
modifier rateLimited() {
// Simple example: limit high-impact operations to once per block
if (lastUpdateBlock == block.number) revert TooFrequentInteraction();
_;
lastUpdateBlock = block.number;
}
function deposit(uint256 assets) external rateLimited {
require(assets > 0, "zero assets");
uint256 shares;
if (totalShares == 0) {
shares = assets;
} else {
// Use rounding that errs in favor of the protocol and is formally analyzed
shares = (assets * totalShares + totalAssets - 1) / totalAssets;
}
totalAssets += assets;
totalShares += shares;
sharesOf[msg.sender] += shares;
}
}
Security Improvements:
Note: Real protocols should use stronger defense-in-depth mechanisms rather than relying solely on per-block limits.
mint() function (integer division rounding down) allowed attackers to inflate the lending_accumulator via repeated deposits/withdrawals. Flash loans scaled the position—turning small per-iteration precision gains into a ~$9.5M drain. Flash loans were the force multiplier.