Skip to main content
Glama

get_swap_quote

Retrieve a token swap quote on SailFish DEX by specifying input and output token addresses, amount, and fee tier for accurate exchange calculations.

Instructions

Get a quote for swapping tokens on SailFish DEX

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
amountInYesAmount of input token to swap
feeNoFee tier (100=0.01%, 500=0.05%, 3000=0.3%, 10000=1%)
tokenInYesAddress of the input token
tokenOutYesAddress of the output token

Implementation Reference

  • src/index.ts:492-515 (registration)
    Registration of the 'get_swap_quote' tool including its schema definition in the ListToolsRequestSchema handler.
    name: 'get_swap_quote',
    description: 'Get a quote for swapping tokens on SailFish DEX',
    inputSchema: {
      type: 'object',
      properties: {
        tokenIn: {
          type: 'string',
          description: 'Address of the input token',
        },
        tokenOut: {
          type: 'string',
          description: 'Address of the output token',
        },
        amountIn: {
          type: 'string',
          description: 'Amount of input token to swap',
        },
        fee: {
          type: 'number',
          description: 'Fee tier (100=0.01%, 500=0.05%, 3000=0.3%, 10000=1%)',
        },
      },
      required: ['tokenIn', 'tokenOut', 'amountIn'],
    },
  • MCP tool handler for 'get_swap_quote' in the CallToolRequestSchema switch statement. Validates input, calls swap.getSwapQuote, formats and returns the quote.
    case 'get_swap_quote': {
      if (!args.tokenIn || typeof args.tokenIn !== 'string') {
        throw new McpError(ErrorCode.InvalidParams, 'Input token address is required');
      }
      
      if (!args.tokenOut || typeof args.tokenOut !== 'string') {
        throw new McpError(ErrorCode.InvalidParams, 'Output token address is required');
      }
      
      if (!args.amountIn || typeof args.amountIn !== 'string') {
        throw new McpError(ErrorCode.InvalidParams, 'Input amount is required');
      }
      
      const slippagePercentage = typeof args.slippagePercentage === 'number' ? args.slippagePercentage : 0.5;
      
      const quote = await swap.getSwapQuote(args.tokenIn, args.tokenOut, args.amountIn, slippagePercentage);
      
      return {
        content: [
          {
            type: 'text',
            text: JSON.stringify({
              inputToken: {
                address: args.tokenIn,
                symbol: quote.tokenInSymbol,
                decimals: quote.tokenInDecimals,
                amount: args.amountIn,
                rawAmount: ethers.parseUnits(args.amountIn, quote.tokenInDecimals).toString()
              },
              outputToken: {
                address: args.tokenOut,
                symbol: quote.tokenOutSymbol,
                decimals: quote.tokenOutDecimals,
                amount: quote.formattedAmountOut,
                minimumAmount: quote.formattedMinimumAmountOut,
                rawAmount: quote.amountOut,
                rawMinimumAmount: quote.minimumAmountOut
              },
              exchangeRate: (Number(quote.formattedAmountOut) / Number(args.amountIn)).toString(),
              priceImpact: quote.priceImpact.toFixed(2),
              routeType: quote.route.type,
              slippage: slippagePercentage.toString(),
              note: "Amounts are formatted using the token's decimal places. Raw amounts are in wei units."
            }, null, 2),
          },
        ],
      };
    }
  • Core implementation of getSwapQuote helper function: finds best route, gets token info, queries QuoterV2 for amountOut (direct/multi-hop), applies slippage, calculates price impact.
    export async function getSwapQuote(
      tokenIn: string,
      tokenOut: string,
      amountIn: string,
      slippagePercentage: number = 0.5
    ): Promise<{
      amountOut: string;
      formattedAmountOut: string;
      minimumAmountOut: string;
      formattedMinimumAmountOut: string;
      tokenInSymbol: string;
      tokenOutSymbol: string;
      tokenInDecimals: number;
      tokenOutDecimals: number;
      route: routes.RouteInfo;
      priceImpact: number;
      midPrice: string;
    }> {
      try {
        // Find the best route for the token pair
        const route = await findBestRoute(tokenIn, tokenOut);
        
        const provider = blockchain.getProvider();
        
        // Get token details
        const tokenInContract = new ethers.Contract(tokenIn, ERC20_ABI, provider);
        const tokenOutContract = new ethers.Contract(tokenOut, ERC20_ABI, provider);
        
        const [tokenInDecimals, tokenOutDecimals, tokenInSymbol, tokenOutSymbol] = await Promise.all([
          tokenInContract.decimals(),
          tokenOutContract.decimals(),
          tokenInContract.symbol(),
          tokenOutContract.symbol(),
        ]);
        
        // Convert amount to token units
        const amountInWei = ethers.parseUnits(amountIn, tokenInDecimals);
        
        // Get quote based on route type
        let amountOutWei: bigint;
        let midPrice: string;
        
        if (route.type === 'direct') {
          // Direct route (single hop)
          const pool = route.path[0];
          const fee = parseInt(pool.feeTier);
          
          // Get quote using the QuoterV2 contract
          const quoterContract = new ethers.Contract(CONTRACTS.QuoterV2, QUOTER_ABI, provider);
          const quoteParams = {
            tokenIn,
            tokenOut,
            amountIn: amountInWei,
            fee,
            sqrtPriceLimitX96: 0 // No price limit
          };
          
          // Use a static call to get the quote without sending a transaction
          const quoterInterface = new ethers.Interface(QUOTER_ABI);
          const calldata = quoterInterface.encodeFunctionData('quoteExactInputSingle', [quoteParams]);
          
          const result = await provider.call({
            to: CONTRACTS.QuoterV2,
            data: calldata,
          });
          
          const decodedResult = quoterInterface.decodeFunctionResult('quoteExactInputSingle', result);
          amountOutWei = decodedResult[0]; // Get the first return value (amountOut)
          
          // Get mid price from the pool
          if (pool.token0.address.toLowerCase() === tokenIn.toLowerCase()) {
            midPrice = pool.token1Price || '0';
          } else {
            midPrice = pool.token0Price || '0';
          }
        } else {
          // Indirect route (multi-hop)
          // For simplicity, we'll use the direct route approach for each hop and multiply the results
          // In a production environment, you would use the exactInput function with a path
          
          // First hop
          const pool1 = route.path[0];
          const fee1 = parseInt(pool1.feeTier);
          const intermediaryToken = route.intermediaryToken!;
          
          // Second hop
          const pool2 = route.path[1];
          const fee2 = parseInt(pool2.feeTier);
          
          // Get quote for first hop
          const quoterContract = new ethers.Contract(CONTRACTS.QuoterV2, QUOTER_ABI, provider);
          
          // First hop quote
          const quoteParams1 = {
            tokenIn,
            tokenOut: intermediaryToken.address,
            amountIn: amountInWei,
            fee: fee1,
            sqrtPriceLimitX96: 0
          };
          
          const quoterInterface = new ethers.Interface(QUOTER_ABI);
          const calldata1 = quoterInterface.encodeFunctionData('quoteExactInputSingle', [quoteParams1]);
          
          const result1 = await provider.call({
            to: CONTRACTS.QuoterV2,
            data: calldata1,
          });
          
          const decodedResult1 = quoterInterface.decodeFunctionResult('quoteExactInputSingle', result1);
          const intermediateAmountWei = decodedResult1[0];
          
          // Second hop quote
          const quoteParams2 = {
            tokenIn: intermediaryToken.address,
            tokenOut,
            amountIn: intermediateAmountWei,
            fee: fee2,
            sqrtPriceLimitX96: 0
          };
          
          const calldata2 = quoterInterface.encodeFunctionData('quoteExactInputSingle', [quoteParams2]);
          
          const result2 = await provider.call({
            to: CONTRACTS.QuoterV2,
            data: calldata2,
          });
          
          const decodedResult2 = quoterInterface.decodeFunctionResult('quoteExactInputSingle', result2);
          amountOutWei = decodedResult2[0];
          
          // Calculate mid price for multi-hop (approximate)
          const midPrice1 = pool1.token0.address.toLowerCase() === tokenIn.toLowerCase() 
            ? pool1.token1Price || '0'
            : pool1.token0Price || '0';
            
          const midPrice2 = pool2.token0.address.toLowerCase() === intermediaryToken.address.toLowerCase()
            ? pool2.token1Price || '0'
            : pool2.token0Price || '0';
            
          midPrice = (parseFloat(midPrice1) * parseFloat(midPrice2)).toString();
        }
        
        // Format amount out
        const formattedAmountOut = ethers.formatUnits(amountOutWei, tokenOutDecimals);
        
        // Calculate minimum amount out with slippage
        const minimumAmountOut = applySlippage(amountOutWei.toString(), slippagePercentage);
        const formattedMinimumAmountOut = ethers.formatUnits(minimumAmountOut, tokenOutDecimals);
        
        // Calculate price impact
        const priceImpact = calculatePriceImpact(
          amountIn,
          formattedAmountOut,
          midPrice,
          tokenInDecimals,
          tokenOutDecimals
        );
        
        return {
          amountOut: amountOutWei.toString(),
          formattedAmountOut,
          minimumAmountOut,
          formattedMinimumAmountOut,
          tokenInSymbol,
          tokenOutSymbol,
          tokenInDecimals,
          tokenOutDecimals,
          route,
          priceImpact,
          midPrice
        };
      } catch (error) {
  • Helper function findBestRoute that delegates to routes.getBestRoute to find optimal swap path (direct or indirect).
    export async function findBestRoute(
      tokenA: string,
      tokenB: string
    ): Promise<routes.RouteInfo> {
      try {
        // Get the best route from the routes module
        const bestRoute = await routes.getBestRoute(tokenA, tokenB);
        
        if (!bestRoute) {
          throw new Error(`No route found for token pair ${tokenA}/${tokenB}`);
        }
        
        // Remove console.log to prevent interference with JSON parsing
        // console.log(`Found ${bestRoute.type} route for ${tokenA}/${tokenB} with fee: ${bestRoute.totalFee * 100}%`);
        
        return bestRoute;
      } catch (error) {
        console.error('Error finding best route:', error);
        throw error;
      }
  • Input schema definition for the get_swap_quote tool.
    inputSchema: {
      type: 'object',
      properties: {
        tokenIn: {
          type: 'string',
          description: 'Address of the input token',
        },
        tokenOut: {
          type: 'string',
          description: 'Address of the output token',
        },
        amountIn: {
          type: 'string',
          description: 'Amount of input token to swap',
        },
        fee: {
          type: 'number',
          description: 'Fee tier (100=0.01%, 500=0.05%, 3000=0.3%, 10000=1%)',
        },
      },
      required: ['tokenIn', 'tokenOut', 'amountIn'],
    },

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/SailFish-Finance/educhain-ai-agent-kit'

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