An overview of the fundamental principles and components required to build a functional and profitable arbitrage bot for decentralized exchanges (DEXs).
Building a Simple Arbitrage Bot for DEXs
Core Arbitrage Concepts
Price Discrepancy Detection
Price discrepancy is the core mechanism where a bot identifies a profitable spread between the same asset on different DEXs. It continuously monitors multiple liquidity pools for temporary price differences caused by varying supply, demand, and network latency.
- Real-time monitoring of token pairs across DEXs like Uniswap and SushiSwap.
- Example: Spotting a 0.5% lower price for ETH/USDC on Curve versus Balancer.
- This matters as it is the foundational signal that triggers an arbitrage opportunity, enabling the bot to buy low and sell high almost instantly.
Gas Optimization & Profit Calculation
Gas optimization is critical for ensuring net profitability by minimizing Ethereum transaction costs. The bot must calculate if the potential profit from an arbitrage exceeds the gas fees and any slippage incurred during the trade execution.
- Dynamic fee estimation using services like ETH Gas Station.
- Example: A $100 profit opportunity may be unviable if network gas fees are $80.
- This directly impacts the bot's bottom line, as high fees can turn a theoretical gain into a net loss, requiring sophisticated pre-trade simulations.
Atomic Swap Execution
Atomic swap execution ensures the entire arbitrage trade either completes successfully or fails entirely, preventing partial fills and capital loss. This is achieved by bundling the buy and sell transactions into a single, indivisible operation using smart contract calls.
- Use of flash loans to borrow capital without collateral for the trade.
- Example: Using Aave's flash loan to buy ETH on DEX A and sell it on DEX B in one blockchain transaction.
- This matters for security and capital efficiency, eliminating the risk of being stuck with an asset at a loss if one leg of the trade fails.
Slippage and MEV Protection
Slippage and Miner Extractable Value (MEV) are significant risks. Slippage is the difference between expected and executed prices due to market movement. MEV involves bots front-running or sandwiching your transaction to steal profits.
- Setting maximum slippage tolerances (e.g., 0.5%) in trade parameters.
- Example: A bot's trade being sandwiched by a searcher, resulting in worse execution prices.
- Protection is vital as these factors can drastically reduce or eliminate profits, requiring strategies like private transaction relays or optimized gas bidding.
Liquidity Source Aggregation
Liquidity source aggregation involves sourcing the best prices and deepest pools across numerous DEXs to maximize arbitrage opportunities. A sophisticated bot doesn't just check two exchanges but aggregates data from dozens of pools and automated market makers (AMMs).
- Integrating with aggregators like 1inch or building custom pool indexes.
- Use case: Finding the best route for a WETH/DAI trade across Uniswap V3, SushiSwap, and Balancer V2 pools simultaneously.
- This expands the opportunity set and improves profit potential by ensuring the bot always accesses the most favorable market prices.
Risk Management & Circuit Breakers
Risk management protocols are essential safeguards that prevent catastrophic losses. These include circuit breakers that pause trading during extreme volatility, wallet balance checks, and maximum loss limits per trade or per day.
- Automated stop-losses based on price movement or portfolio drawdown.
- Example: Halting all bot activity if Ethereum network gas prices spike above 200 Gwei.
- This matters for long-term sustainability, protecting the trading capital from unexpected market events, smart contract bugs, or operational failures.
Bot Architecture and Data Flow
Process overview for building a bot that identifies and executes profitable token swaps across decentralized exchanges.
Step 1: Monitor Liquidity Pools and Prices
Continuously track token prices across multiple DEXs to identify arbitrage opportunities.
Detailed Instructions
Price monitoring is the core function of the arbitrage bot. You must fetch real-time price data for the same token pair (e.g., WETH/USDC) from multiple DEXs like Uniswap V3, SushiSwap, and Balancer. This involves querying the on-chain liquidity pools to calculate the effective price, factoring in fees and slippage.
- Sub-step 1: Set up WebSocket or RPC listeners to subscribe to new block events on Ethereum Mainnet (Chain ID: 1) or other supported chains.
- Sub-step 2: For each monitored pool, call the
getReserves()function on the pair contract to obtain the current token reserves. - Sub-step 3: Calculate the implied price using the reserve ratio. For a constant product AMM, price = reserve_tokenB / reserve_tokenA.
- Sub-step 4: Store and compare prices in a local database, flagging instances where the price difference exceeds your predefined profit threshold (e.g., 0.5%).
Tip: Use multicall contracts (e.g., MakerDAO's Multicall2) to batch read calls and reduce RPC load and latency, which is critical for speed.
Step 2: Calculate Profitable Arbitrage Paths
Analyze price discrepancies to determine the optimal trade route and expected profit.
Detailed Instructions
Once a price discrepancy is detected, you must perform arbitrage path calculation. This involves simulating the trade to account for all costs and ensure a net positive gain. The simplest path is a direct two-pool swap (Buy on DEX A, Sell on DEX B), but you may need a multi-hop route through a bridge token like WETH.
- Sub-step 1: For each opportunity, simulate the swap using the
getAmountsOutfunction of the router contract (e.g., Uniswap V2 Router 0x7a250d5630B4cF539739dF2C5dAcb4c659F2488D). - Sub-step 2: Account for all transaction costs, including DEX swap fees (typically 0.3% or 0.05% for V3), network gas fees (estimate using
eth_gasPrice), and your bot's operational overhead. - Sub-step 3: Calculate the net profit in a stable denomination (e.g., USD). The formula is:
Profit = Output_Amount - Input_Amount - Total_Costs. Only proceed if profit > your minimum (e.g., $10). - Sub-step 4: Determine the optimal gas price using a service like Etherscan's Gas Tracker to balance speed and cost, aiming for a base fee + priority fee that ensures inclusion in the next 1-2 blocks.
Tip: Implement a circuit breaker that discards opportunities if the calculated profit margin is below 0.1% after costs, as these are often unprofitable due to price movement.
Step 3: Construct and Send the Transaction
Build, sign, and broadcast the atomic swap transaction to the blockchain.
Detailed Instructions
Transaction construction must be fast and atomic to avoid front-running and sandwich attacks. The entire arbitrage sequence should be executed within a single transaction using a smart contract, often called a flash loan arbitrage contract, to avoid capital risk.
- Sub-step 1: If using a flash loan, initiate a loan from a provider like Aave (LendingPool address: 0x7d2768dE32b0b80b7a3454c06BdAc94A69DDc7A9) for the required input amount. The entire logic must be in the
executeOperationcallback. - Sub-step 2: Encode the swap calls. For example, to swap on Uniswap V2, you would encode a call to
swapExactTokensForTokens.
solidity// Example encoded call for a swap bytes memory data = abi.encodeWithSelector( IUniswapV2Router02.swapExactTokensForTokens.selector, 1000000000000000000, // amountIn (1 ETH) 0, // amountOutMin (calculated earlier) path, // address[] e.g., [WETH, USDC] address(this), // to block.timestamp + 300 // deadline );
- Sub-step 3: Sign the transaction using your bot's private key (securely stored in an environment variable) with a high nonce to ensure it's processed immediately.
- Sub-step 4: Broadcast via a reliable node provider (e.g., Alchemy, Infura) using
eth_sendRawTransaction.
Tip: Use a private transaction pool (e.g., Flashbots RPC) to submit your transaction directly to miners/validators, mitigating front-running by keeping it out of the public mempool.
Step 4: Verify Execution and Handle Results
Confirm transaction success, manage profits, and log outcomes for analysis.
Detailed Instructions
Post-trade verification is critical to ensure the bot's actions were successful and profitable. You must listen for transaction receipts and parse logs to confirm the expected amounts were received and to update your accounting.
- Sub-step 1: Monitor the transaction hash for confirmation. Poll
eth_getTransactionReceiptuntil it returns a status of0x1(success). A status of0x0indicates a revert, requiring error analysis. - Sub-step 2: Parse the transaction logs to extract the actual
amountOutevents. Compare this to your simulated profit. A significant deviation may indicate slippage or being out-prioritized by another bot. - Sub-step 3: If a flash loan was used, verify the loan was repaid by checking the final balance of your contract. The contract should end with a positive balance representing the profit.
- Sub-step 4: Securely transfer profits to your treasury wallet (e.g., 0xYourTreasuryAddress) by calling a
withdrawProfitfunction in your contract. Update your internal ledger and database with the trade details: timestamp, pair, profit, gas used, and net ROI. - Sub-step 5: Implement alerting for failed transactions or profitability below a certain threshold. Use tools like Discord webhooks or PagerDuty to notify you of critical issues.
Tip: Maintain a fallback RPC endpoint (e.g., a secondary Infura project ID) to switch to if your primary node provider fails during this critical verification phase.
DEX Protocol Comparison for Arbitrage
Comparison of key protocol features for building a simple arbitrage bot on Ethereum mainnet.
| Feature | Uniswap V3 | Curve V2 | Balancer V2 | SushiSwap V2 |
|---|---|---|---|---|
Fee Tier (Typical) | 0.05% (ETH/USDC) | 0.04% (3pool) | 0.05% (80/20 BAL/WETH) | 0.30% (SUSHI/ETH) |
Gas Cost (Swap, approx) | 150k - 200k gas | 180k - 250k gas | 200k - 280k gas | 140k - 190k gas |
MEV Resistance | Low (Public Mempool) | Medium (routing complexity) | Low (Public Mempool) | Low (Public Mempool) |
Native Price Oracle | TWAP Oracle | Internal Oracle (EMA) | Weighted Oracle | TWAP Oracle (requires SushiXSwap) |
Flash Loan Support | No (requires separate provider) | Yes (integrated in pools) | Yes (via Vault) | No (requires separate provider) |
Liquidity Concentration | Concentrated (custom ranges) | Amplified (stable/pegged) | Weighted (custom ratios) | Uniform (standard AMM) |
Primary Use Case for Arb | Volatile pairs, wide ranges | Stable/pegged asset pairs | Custom pool compositions | Established volatile pairs |
Implementation Paths and Trade-offs
Getting Started
Arbitrage is the practice of buying an asset on one market and simultaneously selling it on another to profit from price differences. In decentralized finance (DeFi), this often involves swapping tokens across different DEXs like Uniswap and SushiSwap.
Key Points
- Simple Logic: The bot scans multiple DEXs for price discrepancies for the same token pair. When it finds a profitable opportunity after accounting for gas fees, it executes the trades automatically.
- Example: If 1 ETH costs 3000 DAI on Uniswap V3 but 3020 DAI on Curve, the bot buys ETH on Uniswap and sells it on Curve instantly.
- Use Case: This strategy provides market efficiency by balancing prices across platforms, but beginners must be wary of high network congestion and impermanent loss if providing liquidity.
Example Workflow
When using Uniswap and SushiSwap, you would first use an API or on-chain query to fetch the current ETH/USDC price on both. If SushiSwap offers a higher price, the bot swaps USDC for ETH on Uniswap, then swaps that ETH for more USDC on SushiSwap, keeping the difference as profit.
Code Walkthrough: A Basic Bot Skeleton
A step-by-step guide to building a simple arbitrage bot that monitors price differences between decentralized exchanges (DEXs) and executes profitable trades.
Step 1: Project Setup and Dependencies
Initialize the project and install necessary libraries for blockchain interaction and data fetching.
Detailed Instructions
Begin by creating a new Node.js project and installing core dependencies. The most critical package is ethers.js v6, which provides the interface for connecting to the Ethereum blockchain and interacting with smart contracts. You'll also need axios or a similar HTTP client for fetching price data from DEX APIs and dotenv for managing private keys and RPC URLs securely.
- Sub-step 1: Run
npm init -yto create apackage.jsonfile. - Sub-step 2: Install dependencies:
npm install ethers@6 axios dotenv. - Sub-step 3: Create a
.envfile to store your sensitive data:PRIVATE_KEY=your_wallet_private_key,INFURA_URL=https://mainnet.infura.io/v3/YOUR_API_KEY. - Sub-step 4: Set up the basic project structure with
index.jsas the main entry point and aconfig.jsfile for constants like DEX router addresses (e.g., Uniswap V2:0x7a250d5630B4cF539739dF2C5dAcb4c659F2488D).
Tip: Never commit your
.envfile to version control. Use a.gitignorefile to exclude it.
Step 2: Initialize Providers and Wallet
Establish connections to the blockchain and instantiate a wallet for signing transactions.
Detailed Instructions
This step involves creating the foundational objects that allow your bot to read blockchain data and send transactions. You will instantiate a JSON-RPC Provider using a service like Infura or Alchemy to connect to the network. Then, you'll create a Wallet object from your private key, which is linked to the provider for signing.
- Sub-step 1: Import
ethersand load environment variables in your main script. - Sub-step 2: Create the provider:
const provider = new ethers.JsonRpcProvider(process.env.INFURA_URL);. - Sub-step 3: Create the wallet:
const wallet = new ethers.Wallet(process.env.PRIVATE_KEY, provider);. This wallet will be used to pay for gas and sign arbitrage transactions. - Sub-step 4: Verify the connection by fetching the wallet's balance:
const balance = await provider.getBalance(wallet.address); console.log(Balance: ${ethers.formatEther(balance)} ETH);.
Tip: For mainnet testing, start with a Sepolia testnet provider and use test ETH to avoid losing real funds during development.
Step 3: Fetch Token Prices from Multiple DEXs
Query liquidity pools on different DEXs to find price discrepancies for a target token pair.
Detailed Instructions
The core arbitrage logic starts here. You need to fetch the exchange rate for a specific token pair (e.g., WETH/USDC) from at least two DEXs, such as Uniswap V2 and Sushiswap. This involves interacting with the DEX's router contract to get quotes or directly reading the reserves from the pair contract.
- Sub-step 1: Define the token addresses. For example, use WETH (
0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2) and USDC (0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48). - Sub-step 2: Create contract instances for the DEX routers using the ABI and address. For a simple quote, you can call the
getAmountsOutfunction.
javascriptconst uniswapRouter = new ethers.Contract(UNISWAP_V2_ROUTER, ['function getAmountsOut(uint amountIn, address[] memory path) view returns (uint[] memory amounts)'], provider); const amounts = await uniswapRouter.getAmountsOut(ethers.parseEther('1'), [WETH, USDC]); const uniswapPrice = amounts[1]; // Output amount in USDC
- Sub-step 3: Repeat Sub-step 2 for a second DEX (e.g., Sushiswap Router:
0xd9e1cE17f2641f24aE83637ab66a2cca9C378B9F). - Sub-step 4: Calculate the price difference percentage. An opportunity exists if the difference exceeds your minimum profit threshold (e.g., 0.5%) after accounting for gas fees.
Step 4: Construct and Execute the Arbitrage Trade
Build and send the transaction to perform the swap on the profitable DEX.
Detailed Instructions
Once a profitable opportunity is identified, your bot must execute a swap transaction. This involves constructing the transaction data for the swapExactTokensForTokens function on the DEX router with the more favorable price. You must set a slippage tolerance and ensure the transaction is sent with appropriate gas settings.
- Sub-step 1: Calculate the exact input amount you wish to trade, starting with a small test amount (e.g., 0.01 WETH).
- Sub-step 2: Define the path (token addresses array) and the minimum amount of output tokens you will accept, applying your slippage (e.g., 0.5%):
minAmountOut = expectedOutput * 0.995. - Sub-step 3: Connect the router contract with your wallet to create a signer contract:
const routerWithSigner = uniswapRouter.connect(wallet);. - Sub-step 4: Call the swap function and wait for confirmation.
javascriptconst tx = await routerWithSigner.swapExactTokensForTokens( ethers.parseEther('0.01'), // amountIn minAmountOut, // amountOutMin [WETH, USDC], // path wallet.address, // to Math.floor(Date.now() / 1000) + 120 // deadline (2 minutes) ); const receipt = await tx.wait(); console.log(`Transaction mined in block ${receipt.blockNumber}`);
- Sub-step 5: Implement error handling (try/catch) for failed transactions and revert conditions.
Tip: Always use a deadline parameter to prevent pending transactions from being executed at unfavorable prices much later.
Step 5: Implement Monitoring Loop and Profit Tracking
Create a continuous loop to scan for opportunities and log the bot's performance.
Detailed Instructions
A functional bot runs continuously. You need to implement a polling loop that checks prices at regular intervals (e.g., every 10 seconds). Additionally, you should track the bot's starting capital and profit/loss over time to evaluate its performance.
- Sub-step 1: Wrap the price fetching and execution logic (Steps 3 & 4) inside an async function like
checkAndExecuteArbitrage(). - Sub-step 2: Use
setIntervalto call this function periodically:setInterval(checkAndExecuteArbitrage, 10000);. - Sub-step 3: Initialize variables to track the wallet's balance in the quote token (e.g., USDC) before starting the loop.
- Sub-step 4: After each successful trade, calculate the profit by comparing the new USDC balance to the previous one. Log the result:
console.log(Profit this trade: ${profit} USDC);. - Sub-step 5: Add a cooldown mechanism or a rate-limiting condition to prevent excessive API calls or rapid-fire transactions that could fail due to nonce issues.
Tip: Consider moving to an event-driven architecture using WebSocket providers to listen for new blocks instead of polling, which is more efficient and faster.
Critical Risks and Operational FAQs
The primary financial risks involve impermanent loss and slippage.
- Impermanent loss occurs when the price ratio of the paired assets in a liquidity pool changes compared to holding them, potentially erasing arbitrage profits. For example, a 20% price divergence can lead to a loss of over 2% of capital versus simply holding.
- Slippage is the difference between expected and executed trade prices, especially problematic in low-liquidity pools. A bot targeting a 0.5% profit might lose it all if slippage is 1% on a large trade.
- Gas fees on networks like Ethereum can be substantial; a failed transaction during a congested period can cost over $50, turning a profitable opportunity into a net loss.