5/5
## Uniswap V2 Arbitrage In this lesson, we are going to write a Uniswap V2 arbitrage smart contract. We will cover two different approaches to arbitrage: one using a standard swap, and the other using a flash swap. ### Arbitrage with Swap We will create a smart contract that will execute an arbitrage between two Uniswap V2 router contracts. The token to start the arbitrage will come from the message sender. We will need to pull the tokens in from the message sender into our smart contract. We will need to implement a function called `swap`. The function will need to take in a struct called `SwapParams`, which we will define. `SwapParams` will contain the following: ```solidity struct SwapParams { address router0; address router1; address tokenIn; address tokenOut; uint256 amountIn; uint256 minProfit; } ``` The first parameter, `router0`, is the address of the Uniswap V2 router contract that we will call to execute the first swap. We will use `tokenIn` as the input to the first swap, and the output will be `tokenOut`. `router1` is the address of the contract that we will use for the second swap. The tokens will be reversed for the second swap, `tokenIn` will be the `tokenOut` from the first swap, and `tokenOut` will be the `tokenIn` from the first swap. The next parameter, `tokenIn`, is the address of the token that will be used as input to the first swap. `tokenOut` is the address of the token that will come out from the first swap. The final parameter, `minProfit`, is the minimum profit that the arbitrage should make. If the arbitrage profit is less than this amount, the function will revert. The `swap` function will be responsible for: 1. Pulling the tokens in from the message sender. 2. Executing the first swap on `router0`. 3. Executing the second swap on `router1`. 4. Sending the remaining tokens (including profit) back to the message sender. After the arbitrage is complete, the `swap` function will return `amountIn` plus the profit made. ### Arbitrage with Flash Swap We will implement another function called `flashSwap` that will perform arbitrage using a flash swap. The difference between this function and the `swap` function is that we will borrow the tokens we need to start the arbitrage from a Uniswap V2 pair contract. The function `flashSwap` will take in the following parameters: ```solidity function flashSwap(address pair, bool isToken0, SwapParams calldata params) external; ``` * `pair`: The address of the Uniswap V2 pair contract to borrow tokens from. * `isToken0`: A boolean value that is `true` if the token to borrow is `token0` of the pair. For example, if we are borrowing DAI from the DAI/MKR pair contract, and DAI is equal to `token0` on that pair contract, `isToken0` would be `true`. * `params`: The `SwapParams` struct we defined earlier. The `flashSwap` function will: 1. Borrow the tokens needed for the arbitrage from the specified pair contract. 2. Call a function called `UniswapV2Call` to execute the arbitrage. 3. Repay the tokens borrowed from the pair contract. 4. Send the remaining tokens to the message sender of the `flashSwap` function. The function `UniswapV2Call` will be called back by the pair contract and will contain the logic for executing the arbitrage and repaying the borrowed tokens. ### Example Exercises Exercises `foundry/test/uniswap-v2/exercises/UniswapV2Arb1.sol` ```solidity // SPDX-License-Identifier: MIT pragma solidity 0.8.24; import {IUniswapV2Pair} from "../../../src/interfaces/uniswap-v2/IUniswapV2Pair.sol"; import {IUniswapV2Router02} from "../../../src/interfaces/uniswap-v2/IUniswapV2Router02.sol"; import {IERC20} from "../../../src/interfaces/IERC20.sol"; contract UniswapV2Arb1 { struct SwapParams { // Router to execute first swap - tokenIn for tokenOut address router0; // Router to execute second swap - tokenOut for tokenIn address router1; // Token in of first swap address tokenIn; // Token out of first swap address tokenOut; // Amount in for the first swap uint256 amountIn; // Revert the arbitrage if profit is less than this minimum uint256 minProfit; } // Exercise 1 // - Execute an arbitrage between router0 and router1 // - Pull tokenIn from msg.sender // - Send amountIn + profit back to msg.sender function swap(SwapParams calldata params) external { // Write your code here // Don’t change any other code } // Exercise 2 // - Execute an arbitrage between router0 and router1 using flash swap // - Borrow tokenIn with flash swap from pair // - Send profit back to msg.sender /** * @param pair Address of pair contract to flash swap and borrow tokenIn * @param isToken0 True if token to borrow is token0 of pair * @param params Swap parameters */ function flashSwap(address pair, bool isToken0, SwapParams calldata params) external { // Write your code here // Don’t change any other code } function uniswapV2Call( address sender, uint256 amount0Out, uint256 amount1Out, bytes calldata data ) external { // Write your code here // Don’t change any other code } } ``` ### Example Test Let's look at an example test for our arbitrage contract: Test `foundry/test/uniswap-v2/exercises/UniswapV2Arb1.test.sol` ```solidity // SPDX-License-Identifier: MIT pragma solidity 0.8.24; import {Test, console2} from "forge-std/Test.sol"; import {IERC20} from "../../../src/interfaces/IERC20.sol"; import {IWETH} from "../../../src/interfaces/IWETH.sol"; import {IUniswapV2Router02} from "../../../src/interfaces/uniswap-v2/IUniswapV2Router02.sol"; import { DAI, WETH, UNISWAP_V2_ROUTER_02, SUSHISWAP_V2_ROUTER_02, UNISWAP_V2_PAIR_DAI_WETH, UNISWAP_V2_PAIR_DAI_MKR } from "../../../src/Constants.sol"; import {UniswapV2Arb1} from "./UniswapV2Arb1.sol"; // Test arbitrage between Uniswap and Sushiswap // Buy WETH on Uniswap, sell on Sushiswap. // For flashSwap, borrow DAI from DAI/MKR pair contract UniswapV2Arb1Test is Test { IUniswapV2Router02 private constant uni_router = IUniswapV2Router02(UNISWAP_V2_ROUTER_02); IUniswapV2Router02 private constant sushi_router = IUniswapV2Router02(SUSHISWAP_V2_ROUTER_02); IERC20 private constant dai = IERC20(DAI); IWETH private constant weth = IWETH(WETH); address constant user = address(11); UniswapV2Arb1 private arb; function setUp() public { arb = new UniswapV2Arb1(); // Setup - WETH cheaper on Uniswap than Sushiswap deal(address(this), 100 * 1e18); weth.deposit{value: 100 * 1e18}(); weth.approve(address(uni_router), type(uint256).max); address[] memory path = new address[](2); path[0] = WETH; path[1] = DAI; uni_router.swapExactTokensForTokens({ amountIn: 100 * 1e18, amountOutMin: 1, path: path, to: user, deadline: block.timestamp }); // Setup - user has DAI, approves arb to spend DAI deal(DAI, user, 10000 * 1e18); vm.prank(user); dai.approve(address(arb), type(uint256).max); } function test_swap() public { uint256 bal0 = dai.balanceOf(user); vm.prank(user); arb.swap( UniswapV2Arb1.SwapParams({ router0: UNISWAP_V2_ROUTER_02, router1: SUSHISWAP_V2_ROUTER_02, tokenIn: DAI, tokenOut: WETH, amountIn: 100 * 1e18, minProfit: 1 }) ); uint256 bal1 = dai.balanceOf(user); assertGe(bal1, bal0, "no profit"); assertEq(dai.balanceOf(address(arb)), 0, "DAI balance of arb != 0"); console2.log("profit", bal1 - bal0); } function test_flashSwap() public { uint256 bal0 = dai.balanceOf(user); vm.prank(user); arb.flashSwap( UNISWAP_V2_PAIR_DAI_MKR, true, UniswapV2Arb1.SwapParams({ router0: UNISWAP_V2_ROUTER_02, router1: SUSHISWAP_V2_ROUTER_02, tokenIn: DAI, tokenOut: WETH, amountIn: 100 * 1e18, minProfit: 1 }) ); uint256 bal1 = dai.balanceOf(user); assertGe(bal1, bal0, "no profit"); assertEq(dai.balanceOf(address(arb)), 0, "DAI balance of arb != 0"); console2.log("profit", bal1 - bal0); } } ``` This test will execute both the `swap` and the `flashSwap` functions and verify that each function successfully performs an arbitrage.
In this lesson, we are going to write a Uniswap V2 arbitrage smart contract. We will cover two different approaches to arbitrage: one using a standard swap, and the other using a flash swap.
We will create a smart contract that will execute an arbitrage between two Uniswap V2 router contracts. The token to start the arbitrage will come from the message sender. We will need to pull the tokens in from the message sender into our smart contract.
We will need to implement a function called swap
. The function will need to take in a struct called SwapParams
, which we will define. SwapParams
will contain the following:
The first parameter, router0
, is the address of the Uniswap V2 router contract that we will call to execute the first swap. We will use tokenIn
as the input to the first swap, and the output will be tokenOut
.
router1
is the address of the contract that we will use for the second swap. The tokens will be reversed for the second swap, tokenIn
will be the tokenOut
from the first swap, and tokenOut
will be the tokenIn
from the first swap.
The next parameter, tokenIn
, is the address of the token that will be used as input to the first swap. tokenOut
is the address of the token that will come out from the first swap.
The final parameter, minProfit
, is the minimum profit that the arbitrage should make. If the arbitrage profit is less than this amount, the function will revert.
The swap
function will be responsible for:
Pulling the tokens in from the message sender.
Executing the first swap on router0
.
Executing the second swap on router1
.
Sending the remaining tokens (including profit) back to the message sender.
After the arbitrage is complete, the swap
function will return amountIn
plus the profit made.
We will implement another function called flashSwap
that will perform arbitrage using a flash swap. The difference between this function and the swap
function is that we will borrow the tokens we need to start the arbitrage from a Uniswap V2 pair contract. The function flashSwap
will take in the following parameters:
pair
: The address of the Uniswap V2 pair contract to borrow tokens from.
isToken0
: A boolean value that is true
if the token to borrow is token0
of the pair. For example, if we are borrowing DAI from the DAI/MKR pair contract, and DAI is equal to token0
on that pair contract, isToken0
would be true
.
params
: The SwapParams
struct we defined earlier.
The flashSwap
function will:
Borrow the tokens needed for the arbitrage from the specified pair contract.
Call a function called UniswapV2Call
to execute the arbitrage.
Repay the tokens borrowed from the pair contract.
Send the remaining tokens to the message sender of the flashSwap
function.
The function UniswapV2Call
will be called back by the pair contract and will contain the logic for executing the arbitrage and repaying the borrowed tokens.
Exercises foundry/test/uniswap-v2/exercises/UniswapV2Arb1.sol
Let's look at an example test for our arbitrage contract:
Test foundry/test/uniswap-v2/exercises/UniswapV2Arb1.test.sol
This test will execute both the swap
and the flashSwap
functions and verify that each function successfully performs an arbitrage.
A comprehensive guide to implementing Uniswap V2 arbitrage in Solidity - This lesson explores how to build a Solidity smart contract that performs arbitrage between Uniswap V2 router contracts. The lesson covers two arbitrage strategies, one using a simple swap and the other using a flash swap, along with code examples and a test.
Previous lesson
Previous
Next lesson
Next
Give us feedback
Course Overview
About the course
How to use Uniswap v2 dex and contracts
Interacting with the Uniswap v2 router and factory
How to create Uniswap v2 liquidity pools
How to add liquidity to Uniswap v2 pools
Swaps, flash swaps, flash swap arbitrage, and time-weighted average price (TWAP)
Security researcher
$49,999 - $120,000 (avg. salary)
Smart Contract Auditor
$100,000 - $200,000 (avg. salary)
Smart Contract Engineer
$100,000 - $150,000 (avg. salary)
Web3 developer
$60,000 - $150,000 (avg. salary)
Web3 Developer Relations
$85,000 - $125,000 (avg. salary)
Last updated on June 6, 2025
Duration: 14min
Duration: 1h 20min
Duration: 10min
Duration: 54min
Duration: 25min
Duration: 26min
Duration: 1h 03min
Duration: 59min
Course Overview
About the course
How to use Uniswap v2 dex and contracts
Interacting with the Uniswap v2 router and factory
How to create Uniswap v2 liquidity pools
How to add liquidity to Uniswap v2 pools
Swaps, flash swaps, flash swap arbitrage, and time-weighted average price (TWAP)
Security researcher
$49,999 - $120,000 (avg. salary)
Smart Contract Auditor
$100,000 - $200,000 (avg. salary)
Smart Contract Engineer
$100,000 - $150,000 (avg. salary)
Web3 developer
$60,000 - $150,000 (avg. salary)
Web3 Developer Relations
$85,000 - $125,000 (avg. salary)
Last updated on June 6, 2025