If you’ve been following along, you’ve laid the foundation for flashloan arbitrage—understanding flashloans, integrating Aave V3, and thinking through swap logic. Now let’s build the full package: a flashloan arbitrage bot—a Solidity contract that borrows capital, executes trades across DEXs, ensures profitability, and handles everything legally and cleanly.
Table of Contents
ToggleWhy “flashloan arbitrage bot” matters
This term aligns with how developers and DeFi strategists search for real solutions. It reflects automation, flashloan mastery, and DeFi trading—all in one.
Step 1: What Your Contract Will Do
Your flashloan arbitrage bot will:
- Borrow assets via Aave V3
- Execute swaps (Uniswap → SushiSwap)
- Dynamically handle trade paths
- Verify profit before repayment
- Repay loan with premiums
- Log success or failure
- Restrict key actions to the owner
Let’s combine everything into a single contract.
Step 2: Setup & Dependencies
Begin by installing these in your Hardhat or Remix project:
npm install @openzeppelin/contracts @uniswap/v2-periphery @aave/core-v3
You’ll need interfaces for ERC20, Aave’s IPool, and your swap routers.
Step 3: The Full Bot Code
Here’s your complete FlashloanArbitrageBot.sol:
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import {IPool} from "@aave/core-v3/contracts/interfaces/IPool.sol";
import {IFlashLoanSimpleReceiver} from "@aave/core-v3/contracts/flashloan/interfaces/IFlashLoanSimpleReceiver.sol";
// Minimal router interface
interface IUniswapV2Router {
function swapExactTokensForTokens(
uint amountIn, uint amountOutMin,
address[] calldata path, address to,
uint deadline
) external returns (uint[] memory amounts);
}
contract FlashloanArbitrageBot is IFlashLoanSimpleReceiver {
address public immutable owner;
IPool public immutable aavePool;
IUniswapV2Router public immutable uniswapRouter;
IUniswapV2Router public immutable sushiswapRouter;
event FlashloanRequested(address indexed asset, uint256 amount);
event ArbitrageSuccess(address indexed executor, uint256 profit);
modifier onlyOwner() {
require(msg.sender == owner, "Unauthorized");
_;
}
constructor(address _aavePool, address _uni, address _sushi) {
owner = msg.sender;
aavePool = IPool(_aavePool);
uniswapRouter = IUniswapV2Router(_uni);
sushiswapRouter = IUniswapV2Router(_sushi);
}
function initiateArbitrage(address asset, uint256 amount, bytes calldata params)
external onlyOwner
{
emit FlashloanRequested(asset, amount);
aavePool.flashLoanSimple(address(this), asset, amount, params, 0);
}
function executeOperation(
address asset, uint256 amount, uint256 premium,
address initiator, bytes calldata params
) external override returns (bool) {
require(msg.sender == address(aavePool), "Invalid caller");
require(initiator == address(this), "Not initiated by contract");
(address[] memory path1, address[] memory path2) = abi.decode(params, (address[], address[]));
// First swap (Uniswap)
IERC20(path1[0]).approve(address(uniswapRouter), amount);
uint[] memory out1 = uniswapRouter.swapExactTokensForTokens(
amount, 1, path1, address(this), block.timestamp
);
// Second swap (Sushi)
IERC20(path2[0]).approve(address(sushiswapRouter), out1[1]);
uint[] memory out2 = sushiswapRouter.swapExactTokensForTokens(
out1[1], 1, path2, address(this), block.timestamp
);
uint256 totalOwed = amount + premium;
require(out2[1] >= totalOwed, "No profit");
uint256 profit = out2[1] - totalOwed;
IERC20(asset).approve(address(aavePool), totalOwed);
emit ArbitrageSuccess(tx.origin, profit);
return true;
}
function withdrawToken(address token) external onlyOwner {
uint256 bal = IERC20(token).balanceOf(address(this));
require(bal > 0, "Nothing to withdraw");
IERC20(token).transfer(msg.sender, bal);
}
}
Step 4: How to Use It
Deploy the bot with your Aave pool and router addresses. Then call:
const params = ethers.utils.defaultAbiCoder.encode(
["address[]","address[]"],
[[USDC, WETH], [WETH, USDC]]
);
await bot.initiateArbitrage(USDC, amount, params);
Step 5: Add Flexibility & Protection
- Add slippage control using
amountOutMin - Make paths dynamic for front-end flexibility
- Optimize gas with
immutable, minimize approvals - Track trade details via
ArbitrageSuccesslogs - Pause trading in emergencies with a circuit breaker
Step 6: Testing First
Test locally using Hardhat with mainnet forking or deploy to Goerli/Mumbai. Always simulate full flashloan flow before going live.
Step 7: Secure and Iterate
- Keep
onlyOwneron critical functions - Include
withdrawToken()for safety - Monitor live logs to analyze profitability
- Update routes or DEXs as protocols evolve
Final Thoughts
You now have a complete, smart, and secure flashloan arbitrage bot in Solidity: borrowing with Aave, swapping across DEXs, validating profit, and securing your execution. This is your launchpad for building faster, smarter DeFi strategies. Next up: deploying and automating your arbitrage bot via scripts and live bots.
Flow:
- Start → Owner calls
initiateArbitrage() - Aave V3 → Contract requests flashloan
- DEX Swap 1 → Token swap on Uniswap
- DEX Swap 2 → Token swap on SushiSwap
- Profit Check → Compare
output vs repayment - Repay Loan → Repay flashloan + premium to Aave
- Result → Emit
ArbitrageSuccessevent + withdraw profits

Call to Action
Want this code ready to run?
Download the Aave V3 Flashloan Bot Source Code
If you’re interested, you can read other chapters here.
Chapter 1: Flash Loan Arbitrage with Solidity: The Ultimate Beginner’s Guide
Chapter 2: The Anatomy of a Flash Loan in Solidity
Chapter 3: Solidity Fundamentals for Flash Loan Developers
Chapter 4: Interfaces and Functions — Building Blocks of Flashloans
Chapter 5: Aave V3 Flashloan Developer Guide — How to Harness DeFi Liquidity Like a Pro
Chapter 6: 7 Proven Strategies for Arbitrage Execution Across DEXs with Uniswap & Paraswap