What are Flash Loans?
Flash loans are uncollateralized loans that must be borrowed and repaid within a single transaction. If the loan isn't repaid by the end of the transaction, the entire transaction reverts as if it never happened.
💡 Key Characteristics
- • No collateral required - Borrow millions with zero upfront capital
- • Atomic execution - Everything happens in one transaction
- • All-or-nothing - If repayment fails, loan never happened
- • Available on: Aave, dYdX, Uniswap V2/V3, Balancer
1// Basic flash loan structure
2interface IFlashLoanReceiver {
3 function executeOperation(
4 address[] calldata assets,
5 uint256[] calldata amounts,
6 uint256[] calldata premiums,
7 address initiator,
8 bytes calldata params
9 ) external returns (bool);
10}
11
12contract FlashLoanExample is IFlashLoanReceiver {
13 IPool public aavePool;
14
15 function executeFlashLoan(address asset, uint256 amount) external {
16 address[] memory assets = new address[](1);
17 assets[0] = asset;
18
19 uint256[] memory amounts = new uint256[](1);
20 amounts[0] = amount;
21
22 // 0 = no debt, 1 = stable, 2 = variable
23 uint256[] memory modes = new uint256[](1);
24 modes[0] = 0;
25
26 aavePool.flashLoan(
27 address(this),
28 assets,
29 amounts,
30 modes,
31 address(this),
32 "",
33 0
34 );
35 }
36
37 function executeOperation(
38 address[] calldata assets,
39 uint256[] calldata amounts,
40 uint256[] calldata premiums,
41 address initiator,
42 bytes calldata params
43 ) external returns (bool) {
44 // Your attack/arbitrage logic here
45
46 // Repay the loan + premium
47 uint256 amountOwed = amounts[0] + premiums[0];
48 IERC20(assets[0]).approve(address(aavePool), amountOwed);
49
50 return true;
51 }
52}Common Attack Vectors
Flash loans themselves aren't vulnerabilities - they're a feature. However, they amplify other vulnerabilities by providing attackers with massive capital. Here are the main vectors:
🎯 Price Oracle Manipulation
Temporarily manipulate spot prices to trick protocols using on-chain price feeds.
🗳️ Governance Attacks
Borrow governance tokens to pass malicious proposals in a single block.
💱 Arbitrage Exploitation
Exploit pricing discrepancies between DEXs or lending protocols.
🏦 Collateral Manipulation
Inflate collateral value to borrow more than should be allowed.
Price Oracle Manipulation
The most common flash loan attack involves manipulating prices on AMMs (Automated Market Makers) that protocols use as price oracles.
Attack Pattern
- Flash loan a large amount of Token A
- Swap Token A for Token B on a DEX (crashes Token A price)
- Interact with vulnerable protocol using manipulated price
- Profit from mispriced assets or liquidations
- Swap back and repay flash loan
1// ❌ VULNERABLE: Using spot price from AMM
2contract VulnerableLending {
3 IUniswapV2Pair public pair;
4
5 function getPrice() public view returns (uint256) {
6 (uint112 reserve0, uint112 reserve1,) = pair.getReserves();
7 // This can be manipulated within a single transaction!
8 return (uint256(reserve1) * 1e18) / uint256(reserve0);
9 }
10
11 function borrow(uint256 collateralAmount) external {
12 uint256 price = getPrice(); // Manipulable!
13 uint256 borrowLimit = (collateralAmount * price) / 1e18;
14 // Attacker can inflate price to borrow more
15 _transferTokens(msg.sender, borrowLimit);
16 }
17}
18
19// ✅ SECURE: Using Chainlink or TWAP
20contract SecureLending {
21 AggregatorV3Interface public priceFeed;
22
23 function getPrice() public view returns (uint256) {
24 (
25 ,
26 int256 price,
27 ,
28 uint256 updatedAt,
29
30 ) = priceFeed.latestRoundData();
31
32 // Check for stale data
33 require(block.timestamp - updatedAt < 1 hours, "Stale price");
34 require(price > 0, "Invalid price");
35
36 return uint256(price);
37 }
38}Governance Attacks
Flash loans can be used to temporarily acquire enough governance tokens to pass malicious proposals or manipulate voting outcomes.
🚨 Beanstalk Attack (April 2022)
Attacker used $1B in flash loaned funds to acquire enough STALK tokens to pass a malicious governance proposal that drained $182M from the protocol.
1// ❌ VULNERABLE: No snapshot or timelock
2contract VulnerableGovernance {
3 mapping(address => uint256) public votingPower;
4
5 function vote(uint256 proposalId, bool support) external {
6 // Uses current balance - can be manipulated!
7 uint256 votes = token.balanceOf(msg.sender);
8 proposals[proposalId].votes += votes;
9 }
10
11 function execute(uint256 proposalId) external {
12 // No timelock - immediate execution
13 require(proposals[proposalId].votes > quorum, "Not passed");
14 _executeProposal(proposalId);
15 }
16}
17
18// ✅ SECURE: Snapshot voting + timelock
19contract SecureGovernance {
20 function vote(uint256 proposalId, bool support) external {
21 // Use snapshot from proposal creation block
22 uint256 snapshotBlock = proposals[proposalId].snapshotBlock;
23 uint256 votes = token.getPastVotes(msg.sender, snapshotBlock);
24
25 proposals[proposalId].votes += votes;
26 }
27
28 function execute(uint256 proposalId) external {
29 require(proposals[proposalId].votes > quorum, "Not passed");
30 // Timelock prevents immediate execution
31 require(
32 block.timestamp >= proposals[proposalId].eta,
33 "Timelock not expired"
34 );
35 _executeProposal(proposalId);
36 }
37}Real-World Examples
Euler Finance (March 2023)
$197MFlash loan + donation function exploit. Attacker used flash loans to create leveraged positions, then donated to make themselves liquidatable at a profit.
Beanstalk (April 2022)
$182MFlash loan governance attack. Borrowed $1B to gain voting majority and pass a malicious proposal in a single transaction.
Cream Finance (October 2021)
$130MFlash loan + price oracle manipulation. Exploited the pricing of yUSD vault tokens to drain lending pools.
Pancake Bunny (May 2021)
$45MFlash loan used to manipulate BUNNY/BNB price, then exploit the protocol's reward calculation mechanism.
Prevention Strategies
✅ Use Time-Weighted Oracles
TWAP (Time-Weighted Average Price) oracles average prices over time, making single-block manipulation impractical.
✅ Chainlink Price Feeds
Decentralized oracle networks aggregate data from multiple sources, resistant to on-chain manipulation.
✅ Governance Timelocks
Delay between proposal passing and execution gives community time to react to malicious proposals.
✅ Snapshot Voting
Use token balances from a past block (snapshot) for voting power, not current balance.
✅ Multi-Block Validation
Require actions to span multiple blocks, making atomic flash loan attacks impossible.
✅ Flash Loan Guards
Detect and block interactions from contracts that borrowed via flash loans in the same transaction.
1// Flash loan detection guard
2contract FlashLoanGuard {
3 mapping(address => uint256) private _lastBlock;
4
5 modifier noFlashLoan() {
6 require(
7 _lastBlock[tx.origin] != block.number,
8 "Flash loan detected"
9 );
10 _lastBlock[tx.origin] = block.number;
11 _;
12 }
13
14 // Alternative: Check if caller is a contract with code
15 modifier noContract() {
16 require(msg.sender == tx.origin, "No contracts");
17 _;
18 }
19}
20
21// TWAP Oracle implementation
22contract TWAPOracle {
23 uint256 public constant PERIOD = 30 minutes;
24
25 uint256 public price0CumulativeLast;
26 uint256 public price1CumulativeLast;
27 uint32 public blockTimestampLast;
28
29 FixedPoint.uq112x112 public price0Average;
30 FixedPoint.uq112x112 public price1Average;
31
32 function update() external {
33 (
34 uint256 price0Cumulative,
35 uint256 price1Cumulative,
36 uint32 blockTimestamp
37 ) = UniswapV2OracleLibrary.currentCumulativePrices(pair);
38
39 uint32 timeElapsed = blockTimestamp - blockTimestampLast;
40 require(timeElapsed >= PERIOD, "Period not elapsed");
41
42 price0Average = FixedPoint.uq112x112(
43 uint224((price0Cumulative - price0CumulativeLast) / timeElapsed)
44 );
45 price1Average = FixedPoint.uq112x112(
46 uint224((price1Cumulative - price1CumulativeLast) / timeElapsed)
47 );
48
49 price0CumulativeLast = price0Cumulative;
50 price1CumulativeLast = price1Cumulative;
51 blockTimestampLast = blockTimestamp;
52 }
53}