Page cover

Arbitrage Scanner: Automated Trade Execution

1.1 Arbitrage Scanner Overview

Arbitrage Scanner is a tool for identifying and executing arbitrage opportunities across decentralized exchanges (DEXs) using automated trade execution. By monitoring price differences of the same asset across multiple DEXs, the scanner can identify profitable arbitrage opportunities and execute trades to capitalize on price discrepancies.


1.2 Setting Up the Environment

This section describes the initial setup required to use and deploy the Arbitrage Scanner.

1.2.1 Prerequisites

  • Node.js for running JavaScript scripts and managing dependencies.

  • Web3.js for Ethereum interactions.

  • dotenv for secure management of API keys and private keys.

Install required dependencies:

npm install web3 dotenv axios

1.2.2 Configuration

Use a .env file to securely store your private key, API keys, and node provider URL.

INFURA_API_KEY=your_infura_key
PRIVATE_KEY=your_private_wallet_key

1.3 DEX Monitoring

This section describes the process of monitoring price differences on DEXs.

1.3.1 Initial Setup for DEX Price Checking

We will use the Uniswap and Sushiswap smart contracts to retrieve price information and identify potential arbitrage opportunities.

Uniswap Price Fetching (using Web3.js and Uniswap V2 SDK)

const Web3 = require("web3");
require("dotenv").config();

const web3 = new Web3(`https://mainnet.infura.io/v3/${process.env.INFURA_API_KEY}`);
const UniswapV2PairABI = [/* ABI for UniswapV2Pair contract */];
const uniswapPairAddress = "0xB4e16d0168e52d35CaCD2c6185b44281Ec28C9Dc";  // USDC/ETH pair on Uniswap

async function getUniswapPrice() {
    const pairContract = new web3.eth.Contract(UniswapV2PairABI, uniswapPairAddress);
    const reserves = await pairContract.methods.getReserves().call();
    
    const reserve0 = reserves._reserve0; // USDC reserve
    const reserve1 = reserves._reserve1; // ETH reserve
    const price = reserve0 / reserve1;   // Price of USDC in terms of ETH

    console.log("Uniswap Price (USDC/ETH):", price);
    return price;
}

Sushiswap Price Fetching

Using the same code structure, we can fetch the Sushiswap price for the USDC/ETH pair.

const sushiswapPairAddress = "0x397FF1542f962076d0BFE58eA045FfA2d347ACa0";  // USDC/ETH pair on Sushiswap

async function getSushiswapPrice() {
    const pairContract = new web3.eth.Contract(UniswapV2PairABI, sushiswapPairAddress);
    const reserves = await pairContract.methods.getReserves().call();
    
    const reserve0 = reserves._reserve0;
    const reserve1 = reserves._reserve1;
    const price = reserve0 / reserve1;

    console.log("Sushiswap Price (USDC/ETH):", price);
    return price;
}

1.3.2 Monitoring Prices and Identifying Arbitrage

Once we have the prices from both exchanges, we can compare them to determine if an arbitrage opportunity exists. Here’s an example script to do this.

async function checkArbitrage() {
    const uniswapPrice = await getUniswapPrice();
    const sushiswapPrice = await getSushiswapPrice();

    const priceDifference = Math.abs(uniswapPrice - sushiswapPrice);

    // Define a minimum threshold for profitable arbitrage
    const minArbitrageProfit = 0.5; // Adjust this based on gas fees and desired profit margin

    if (priceDifference >= minArbitrageProfit) {
        console.log("Arbitrage Opportunity Detected!");
        executeTrade(uniswapPrice > sushiswapPrice ? "buy" : "sell");
    } else {
        console.log("No arbitrage opportunity.");
    }
}

1.4 Automated Trade Execution

When an arbitrage opportunity is identified, the script can automatically execute trades on the DEXs. This example demonstrates how to execute trades on Uniswap and Sushiswap using Web3.js.

1.4.1 Trade Execution (Buy/Sell)

Defining Swap Function

This function interacts with the Uniswap Router to perform a token swap.

const UniswapV2RouterABI = [/* ABI for UniswapV2Router02 */];
const uniswapRouterAddress = "0x5C69bEe701ef814a2B6a3EDD4B1652CB9cc5aA6f";  // UniswapV2Router02 address

async function swapTokens(amountIn, path, to) {
    const router = new web3.eth.Contract(UniswapV2RouterABI, uniswapRouterAddress);

    // Set transaction parameters
    const tx = {
        from: process.env.PUBLIC_KEY,
        to: uniswapRouterAddress,
        data: router.methods.swapExactTokensForTokens(
            amountIn,
            0,       // minimum amount out
            path,
            to,
            Math.floor(Date.now() / 1000) + 60 * 10 // 10 minutes
        ).encodeABI(),
        gas: 200000,
        gasPrice: await web3.eth.getGasPrice(),
    };

    // Sign and send transaction
    const signedTx = await web3.eth.accounts.signTransaction(tx, process.env.PRIVATE_KEY);
    const receipt = await web3.eth.sendSignedTransaction(signedTx.rawTransaction);
    console.log("Trade executed:", receipt);
}

Example Arbitrage Execution

After the trade execution function is set up, we can call it to execute trades when a profitable arbitrage is detected.

async function executeTrade(action) {
    const amountIn = web3.utils.toWei("1", "ether");  // Define the amount based on liquidity and desired profit
    const path = action === "buy" 
        ? ["0xA0b86991c6218b36c1d19d4a2e9eb0ce3606eb48", "0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2"] // USDC -> ETH
        : ["0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2", "0xA0b86991c6218b36c1d19d4a2e9eb0ce3606eb48"]; // ETH -> USDC

    try {
        await swapTokens(amountIn, path, process.env.PUBLIC_KEY);
    } catch (error) {
        console.error("Trade execution failed:", error);
    }
}

1.5 Scheduling and Execution Automation

By setting up scheduled intervals, the script can monitor prices and execute trades as soon as an arbitrage opportunity is detected.

1.5.1 Automation using setInterval

The following example calls checkArbitrage every 30 seconds.

const checkInterval = 30000; // Check every 30 seconds

setInterval(() => {
    checkArbitrage();
}, checkInterval);

1.5.2 Advanced Scheduling with Node Scheduler

For more robust scheduling, consider using the node-schedule library for more complex execution timing.

const schedule = require("node-schedule");

// Check every 30 seconds on the dot
schedule.scheduleJob("*/30 * * * * *", async () => {
    await checkArbitrage();
});

1.6 Risk Management and Constraints

Setting parameters such as minimum profit thresholds and gas fees helps avoid unprofitable trades. Adjust these parameters based on current gas prices and liquidity.

{
    "minProfitThreshold": 0.5,
    "maxGasPrice": "10000000000", // in wei (10 Gwei)
    "liquidityBuffer": 1.1
}

Last updated