Create a flash loan arbitrage Solidity smart contract. Flash loans are a form of uncollateralized lending where a borrower must repay the funds within the same blockchain transaction. This sounds complex but it is easy to learn. In this tutorial I will explain the flash loan process in more detail and will show you how to combine it with an arbitrage swap on two decentralized exchanges (Uniswap & SushiSwap). Create a flash loan arbitrage trading bot by following the steps below.
Flash loans on the blockchain
A flash loan is an instant loan that is issued and repaid in the same blockchain block. This is not a loan you can initiate on a website or application as it must be called using a program. The loan is valid for several seconds and if the total amount of the loan is not paid back by the end of the block time the entire transaction is automatically rolled back.
Different decentralized platforms offer flash loans with different terms. Some flash loans are free to borrow funds and some loans charge a small transaction fee. This tutorial will focus on creating a flash loan using Uniswap. This process is very similar to other decentralized platforms.
Arbitrage trading on the blockchain
Arbitrage is the process of purchasing and selling the same asset in different markets at the same time. The goal is to profit from small differences in the asset’s listed price on different exchanges. This process exploits small price differences in identical or similar financial instruments. There are many different types of arbitrage strategies (cross asset, market neutral, tax, etc.) and for this exercises we will focus on price arbitrage (buying on one exchange and selling on another exchange) with the goal of making money on the price differences.
Now let take this a step further and use a flash loan to borrow a large sum of money to execute an arbitrage trade on two decentralized exchanges. If you combine an instant loan with an arbitrage trading strategy you have a very powerful tool.
Flash loan arbitrage Solidity smart contract
The process flow below details how the Solidity smart contract will work. At a high level it will:
- Borrow tokens on Uniswap
- Sell these tokens on Sushiswap
- Repay the loan on Uniswap
To understand how the contract works in detail read the comments in the code below. For more information on Uniswap flash swaps read their documentation and example contracts. This was a Truffle project and it needs to be built by importing the interfaces and library below:
- import ‘./IERC20.sol’;
- import ‘./IUniswapV2Router02.sol’;
- import ‘./IUniswapV2Pair.sol’;
- import ‘./IUniswapV2Factory.sol’
- import ‘./UniswapV2Library.sol’;
pragma solidity ^0.5.16;
// import required interfaces
import './IERC20.sol';
import './IUniswapV2Router02.sol';
import './IUniswapV2Pair.sol';
import './IUniswapV2Factory.sol'
import './UniswapV2Library.sol';
contract FlashLoanArbitrage {
//uniswap factory address
address public factory;
// trade deadline used for expiration
uint constant deadline = now + 100;
//create pointer to the sushiswapRouter
IUniswapV2Router02 public sushiSwapRouter;
constructor(address _factory, address _sushiSwapRouter) public {
// create uniswap factory
factory = _factory;
// create sushiswapRouter
sushiSwapRouter = IUniswapV2Router02(_sushiSwapRouter);
}
// trader needs to monitor for arbitrage opportunities with a bot or script
// this is the function that trader will call when an arbitrage opportunity exists
// tokens are the addresses that you want to trade
// this first function will create the flash loan on uniswap
// one of the amounts will be 0 and the other amount will be the amount you want to borrow
function executeTrade(address token0, address token1, uint amount0, uint amount1) external {
// get liquidity pair address for tokens on uniswap
address pairAddress = IUniswapV2Factory(factory).getPair(token0, token1);
// make sure the pair exists in uniswap
require(pairAddress != address(0), 'Could not find pool on uniswap');
// create flashloan
// create pointer to the liquidity pair address
// to create a flashloan call the swap function on the pair contract
// one amount will be 0 and the non 0 amount is for the token you want to borrow
// address is where you want to receive token that you are borrowing
// bytes can not be empty. Need to inculde some text to initiate the flash loan
// if bytes is empty it will initiate a traditional swap
IUniswapV2Pair(pairAddress).swap(amount0, amount1, address(this), bytes('flashloan'));
}
// After the flashloan is created the below function will be called back by Uniswap
// Uniswap is expecting the function to be named uniswapV2Call
// the parameters below will be sent
// sender is the smart contract address
// amount will be the amount borrowed from the flashloan and other amount will be 0
// bytes is the calldata passed in above
function uniswapV2Call(address _sender, uint _amount0, uint _amount1, bytes calldata _data) external {
// the path is the array of addresses to capture pricing information
address[] memory path = new address[](2);
// get the amount of tokens that were borrowed in the flash loan amount 0 or amount 1
// call it amountTokenBorrowed and will use later in the function
uint amountTokenBorrowed = _amount0 == 0 ? _amount1 : _amount0;
// get the addresses of the two tokens from the uniswap liquidity pool
address token0 = IUniswapV2Pair(msg.sender).token0();
address token1 = IUniswapV2Pair(msg.sender).token1();
// make sure the call to this function originated from
// one of the pair contracts in uniswap to prevent unauthorized behavior
require(msg.sender == UniswapV2Library.pairFor(factory, token0, token1), 'Invalid Request');
// make sure one of the amounts = 0
require(_amount0 == 0 || _amount1 == 0);
// create and populate path array for sushiswap.
// this defines what token we are buying or selling
// if amount0 == 0 then we are going to sell token 1 and buy token 0 on sushiswap
// if amount0 is not 0 then we are going to sell token 0 and buy token 1 on sushiswap
path[0] = _amount0 == 0 ? token1 : token0;
path[1] = _amount0 == 0 ? token0 : token1;
// create a pointer to the token we are going to sell on sushiswap
IERC20 token = IERC20(_amount0 == 0 ? token1 : token0);
// approve the sushiSwapRouter to spend our tokens so the trade can occur
token.approve(address(sushiSwapRouter), amountTokenBorrowed);
// calculate the amount of tokens we need to reimburse uniswap for the flashloan
uint amountRequired = UniswapV2Library.getAmountsIn(factory, amountTokenBorrowed, path)[0];
// finally sell the token we borrowed from uniswap on sushiswap
// amountTokenBorrowed is the amount to sell
// amountRequired is the minimum amount of token to receive in exchange required to payback the flash loan
// path what we are selling or buying
// msg.sender address to receive the tokens
// deadline is the order time limit
// if the amount received does not cover the flash loan the entire transaction is reverted
uint amountReceived = sushiSwapRouter.swapExactTokensForTokens( amountTokenBorrowed, amountRequired, path, msg.sender, deadline)[1];
// pointer to output token from sushiswap
IERC20 outputToken = IERC20(_amount0 == 0 ? token0 : token1);
// amount to payback flashloan
// amountRequired is the amount we need to payback
// uniswap can accept any token as payment
outputToken.transfer(msg.sender, amountRequired);
// send profit (remaining tokens) back to the address that initiated the transaction
outputToken.transfer(tx.origin, amountReceived - amountRequired);
}
}
Try it in Remix
Notice how a different token was used to pay back the Uniswap loan. Uniswap will convert the funds to the correct amount to ensure the loan is paid back in full.
Build your flash loan arbitrage bot
- Create a process that listens to market data on different exchanges
- Compares market data for prices differences
- Submit a transaction to a flash swap arbitrage Solidity smart contract
Modify the smart contract for any DEX
Ethereum
- Uniswap
- Sushiswap
- SashimiSwap
- Balancer
- Aave
- Kyber
- Mooniswap
Binance Smart Chain
- MDEX
- Pancake Swap
- Burger Swap
- Bakery Swap
Polygon
- Quickswap
- Aave
This code is for learning and entertainment purposes only. This code has not been audited so use at your own risk. Remember smart contracts are experimental and could contain bugs.
Click here for more information about how to use the Ethereum test network and how to obtain test ETH.
Resources
Blockchain Networks
Below is a list of EVM compatible Mainnet and Testnet blockchain networks. Each link contains network configuration, links to multiple faucets for test ETH and tokens, bridge details, and technical resources for each blockchain. Basically everything you need to test and deploy smart contracts or decentralized applications on each chain. For a list of popular Ethereum forums and chat applications click here.
Ethereum test network configuration and test ETH faucet information | |
Optimistic Ethereum Mainnet and Testnet configuration, bridge details, etc. | |
Polygon network Mainnet and Testnet configuration, faucets for test MATIC tokens, bridge details, etc. | |
Binance Smart Chain Mainnet and Testnet configuration, faucets for test BNB tokens, bridge details, etc. | |
Fanton networt Mainnet and Testnet configuration, faucets for test FTM tokens, bridge details, etc. | |
Kucoin Chain Mainnet and Testnet configuration, faucets for test KCS tokens, bridge details, etc. |
Web3 Software Libraries
You can use the following libraries to interact with an EVM compatible blockchain.
- Python: Web3.py Python library for interacting with Ethereum. Web3.py examples
- Js: web3.js Ethereum JavaScript API
- Java: web3j Web3 Java Ethereum Ðapp API
- PHP: web3.php A php interface for interacting with the Ethereum blockchain and ecosystem.
Nodes
Learn how to run a Geth node. Read getting started with Geth to run an Ethereum node.
Fix a transaction
How to fix a pending transaction stuck on Ethereum or EVM compatible chain
3 thoughts on “Flash loan arbitrage on Uniswap and SushiSwap”