Skip to main content
Glama
lordbasilaiassistant-sudo

base-flash-arb-mcp

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
NameRequiredDescriptionDefault
token_addressYesToken contract address on Base
min_profit_bpsNoMinimum profit in basis points (default 50 = 0.5%)

Implementation Reference

  • 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,
          };
        }
      }
    );

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/lordbasilaiassistant-sudo/base-flash-arb-mcp'

If you have feedback or need assistance with the MCP directory API, please join our Discord server