estimate_flash_profit
Calculate potential profit from arbitrage opportunities using flash loans on Base network. Estimates net earnings after accounting for gas fees when exploiting price differences between two liquidity pools.
Instructions
Given two pools with a price difference, estimate flash loan profit after gas.
Input Schema
TableJSON Schema
| Name | Required | Description | Default |
|---|---|---|---|
| token_address | Yes | Token contract address | |
| pool_a_address | Yes | Address of pool A (buy cheap here) | |
| pool_b_address | Yes | Address of pool B (sell expensive here) | |
| loan_amount_eth | No | Flash loan amount in ETH (default 1.0) | 1.0 |
Implementation Reference
- src/index.ts:681-839 (handler)The tool "estimate_flash_profit" is defined and implemented here. It calculates the price difference between two liquidity pools and estimates potential profit from a flash loan, accounting for flash loan fees and gas costs.
server.tool( "estimate_flash_profit", "Given two pools with a price difference, estimate flash loan profit after gas.", { token_address: z.string().describe("Token contract address"), pool_a_address: z.string().describe("Address of pool A (buy cheap here)"), pool_b_address: z.string().describe("Address of pool B (sell expensive here)"), loan_amount_eth: z .string() .default("1.0") .describe("Flash loan amount in ETH (default 1.0)"), }, async ({ token_address, pool_a_address, pool_b_address, loan_amount_eth }) => { try { const symbol = await getSymbol(token_address); const loanWei = ethers.parseEther(loan_amount_eth); const gasCost = await estimateGasCost(); // Identify pool types by checking factories const v2Factory = new ethers.Contract( UNIV2_FACTORY, UNIV2_FACTORY_ABI, provider ); const aeroFactory = new ethers.Contract( AERO_FACTORY, AERO_FACTORY_ABI, provider ); // Try to get reserves from both pools const pairAbi = [ ...UNIV2_PAIR_ABI, "function stable() view returns (bool)", ]; async function getPoolPrice( poolAddr: string ): Promise<{ priceInWeth: number; type: string } | null> { try { const pair = new ethers.Contract(poolAddr, pairAbi, provider); const [r0, r1] = await pair.getReserves(); const t0: string = await pair.token0(); const isWeth0 = t0.toLowerCase() === WETH.toLowerCase(); const wethRes = isWeth0 ? r0 : r1; const tokenRes = isWeth0 ? r1 : r0; if (tokenRes === 0n) return null; const price = Number(ethers.formatEther(wethRes)) / Number(ethers.formatUnits(tokenRes, 18)); // Detect type let poolType = "unknown"; try { const v2Pair = await v2Factory.getPair(WETH, token_address); if (v2Pair.toLowerCase() === poolAddr.toLowerCase()) poolType = "Uniswap V2"; } catch { /* ignore */ } try { for (const stable of [false, true]) { const aeroPool = await aeroFactory.getPool( WETH, token_address, stable ); if (aeroPool.toLowerCase() === poolAddr.toLowerCase()) poolType = `Aerodrome (${stable ? "stable" : "volatile"})`; } } catch { /* ignore */ } return { priceInWeth: price, type: poolType }; } catch { return null; } } const [poolAInfo, poolBInfo] = await Promise.all([ getPoolPrice(pool_a_address), getPoolPrice(pool_b_address), ]); if (!poolAInfo || !poolBInfo) { return { content: [ { type: "text" as const, text: JSON.stringify({ error: "Could not read reserves from one or both pools", poolA: poolAInfo ? "OK" : "FAILED", poolB: poolBInfo ? "OK" : "FAILED", }), }, ], isError: true, }; } // Calculate arb: buy cheap on A, sell expensive on B const priceDiffBps = Math.abs( ((poolAInfo.priceInWeth - poolBInfo.priceInWeth) / Math.min(poolAInfo.priceInWeth, poolBInfo.priceInWeth)) * 10000 ); // Estimate tokens from loan const cheaperPool = poolAInfo.priceInWeth < poolBInfo.priceInWeth ? "A" : "B"; const cheapPrice = Math.min(poolAInfo.priceInWeth, poolBInfo.priceInWeth); const expPrice = Math.max(poolAInfo.priceInWeth, poolBInfo.priceInWeth); const tokensFromLoan = cheapPrice > 0 ? Number(ethers.formatEther(loanWei)) / cheapPrice : 0; const ethFromSell = tokensFromLoan * expPrice; const grossProfitEth = ethFromSell - Number(ethers.formatEther(loanWei)); const flashLoanFee = Number(ethers.formatEther(loanWei)) * 0.0009; // Aave 0.09% const netProfitEth = grossProfitEth - flashLoanFee - Number(ethers.formatEther(gasCost)); return { content: [ { type: "text" as const, text: JSON.stringify( { token: token_address, symbol, loanAmountEth: loan_amount_eth, poolA: { address: pool_a_address, type: poolAInfo.type, priceInWeth: poolAInfo.priceInWeth.toFixed(18), }, poolB: { address: pool_b_address, type: poolBInfo.type, priceInWeth: poolBInfo.priceInWeth.toFixed(18), }, priceDiffBps: Math.round(priceDiffBps), buyOn: cheaperPool === "A" ? "Pool A" : "Pool B", sellOn: cheaperPool === "A" ? "Pool B" : "Pool A", estimatedTokens: tokensFromLoan.toFixed(4), ethFromSell: ethFromSell.toFixed(6), grossProfitEth: grossProfitEth.toFixed(6), flashLoanFeeEth: flashLoanFee.toFixed(6), gasCostEth: ethers.formatEther(gasCost), netProfitEth: netProfitEth.toFixed(6), profitable: netProfitEth > 0, warning: "Estimates assume no slippage. Real execution will have slippage proportional to trade size vs pool liquidity.", }, null, 2