detect_arb_opportunity
Identify profitable arbitrage opportunities by comparing token prices across Uniswap V2, Uniswap V3, and Aerodrome on Base blockchain.
Instructions
Compare token prices across Uniswap V2, Uniswap V3, and Aerodrome on Base. Find profitable arbitrage routes.
Input Schema
TableJSON Schema
| Name | Required | Description | Default |
|---|---|---|---|
| token_address | Yes | Token contract address on Base | |
| min_profit_bps | No | Minimum profit in basis points (default 50 = 0.5%) |
Implementation Reference
- src/index.ts:516-635 (handler)The handler for the "detect_arb_opportunity" tool which compares token prices across different DEXes (Uniswap V2/V3, Aerodrome) to identify arbitrage opportunities.
server.tool( "detect_arb_opportunity", "Compare token prices across Uniswap V2, Uniswap V3, and Aerodrome on Base. Find profitable arbitrage routes.", { token_address: z.string().describe("Token contract address on Base"), min_profit_bps: z .number() .default(50) .describe("Minimum profit in basis points (default 50 = 0.5%)"), }, async ({ token_address, min_profit_bps }) => { try { const symbol = await getSymbol(token_address); const testAmount = ethers.parseEther("0.01"); // Test with 0.01 ETH const gasCost = await estimateGasCost(); // Get all buy quotes const buyQuotes = await getAllBuyQuotes(token_address, testAmount); if (buyQuotes.length < 2) { return { content: [ { type: "text" as const, text: JSON.stringify( { token: token_address, symbol, result: "INSUFFICIENT_ROUTES", message: `Only ${buyQuotes.length} DEX route(s) found. Need at least 2 for cross-DEX arb.`, availableRoutes: buyQuotes.map((q) => q.label), }, null, 2 ), }, ], }; } // Sort by best rate (most tokens per ETH) buyQuotes.sort((a, b) => (b.amountOut > a.amountOut ? 1 : -1)); const bestBuy = buyQuotes[0]; // Get sell quotes for the tokens we'd receive const sellQuotes = await getAllSellQuotes( token_address, bestBuy.amountOut ); const opportunities = []; for (const sellQ of sellQuotes) { // Skip same route if (bestBuy.label === sellQ.label) continue; const ethOut = sellQ.amountOut; const profitWei = ethOut - testAmount; const profitAfterGas = profitWei - gasCost; const profitBps = testAmount > 0n ? Number((profitWei * 10000n) / testAmount) : 0; if (profitBps >= min_profit_bps) { opportunities.push({ buyOn: bestBuy.label, sellOn: sellQ.label, ethIn: ethers.formatEther(testAmount), tokensReceived: bestBuy.amountOut.toString(), ethOut: ethers.formatEther(ethOut), grossProfitEth: ethers.formatEther(profitWei), gasCostEth: ethers.formatEther(gasCost), netProfitEth: ethers.formatEther(profitAfterGas), profitBps, profitable: profitAfterGas > 0n, }); } } opportunities.sort( (a, b) => parseFloat(b.netProfitEth) - parseFloat(a.netProfitEth) ); return { content: [ { type: "text" as const, text: JSON.stringify( { token: token_address, symbol, testAmountEth: "0.01", routesChecked: buyQuotes.length, opportunitiesFound: opportunities.length, opportunities, allBuyQuotes: buyQuotes.map((q) => ({ label: q.label, tokensOut: q.amountOut.toString(), })), }, null, 2 ), }, ], }; } catch (e) { return { content: [ { type: "text" as const, text: `Error detecting arb: ${e instanceof Error ? e.message : String(e)}`, }, ], isError: true, }; } } );