ODOS_GET_QUOTE
Retrieve a swap quote for token exchange on a decentralized exchange. Input the from token, to token, and amount in wei to receive a quote.
Instructions
Get a quote for a swap or exchange operation
Input Schema
| Name | Required | Description | Default |
|---|---|---|---|
| chain | No | The blockchain network to execute the transaction on. uses fraxtal as default | fraxtal |
| fromToken | Yes | The token to swap from (address). | |
| toToken | Yes | The token to swap to (address). | |
| amount | Yes | The amount of tokens to swap, in wei. | |
| prettyFormat | No | Whether to pretty format the quote. |
Implementation Reference
- src/tools/get-quote.ts:35-89 (handler)The main handler for the ODOS_GET_QUOTE tool. It validates env vars, resolves the chain, creates a WalletService, calls GetQuoteActionService.execute() to fetch a quote from the Odos API, and formats the result.
export const getQuoteTool = { name: "ODOS_GET_QUOTE", description: "Get a quote for a swap or exchange operation", parameters: getQuoteParamsSchema, execute: async (args: z.infer<typeof getQuoteParamsSchema>) => { try { const walletPrivateKey = process.env.WALLET_PRIVATE_KEY; if (!walletPrivateKey) { throw new Error( "WALLET_PRIVATE_KEY is not set in the environment. This is required to execute trades.", ); } const inputChain = args.chain.toLowerCase(); const chainObject = getChainFromName(inputChain); const walletService = new WalletService( walletPrivateKey, chainObject ?? fraxtal, ); console.log( `[ODOS_GET_QUOTE] Using chain: ${chainObject} (${chainObject.id})`, ); console.log( walletService.getWalletClient()?.account?.address ?? "No wallet address found", ); const service = new GetQuoteActionService(walletService); const quote = await service.execute( args.fromToken, args.toToken, chainObject.id, args.amount, ); if (quote instanceof Error) { return `Error fetching quote: ${quote.message}`; } return args.prettyFormat ? service.format(quote) : JSON.stringify(quote, null, 2); } catch (error: unknown) { const message = error instanceof Error ? error.message : "An unknown error occurred while fetching quote."; console.error(`[ODOS_GET_QUOTE] Error: ${message}`); throw new Error(`Failed to fetch quote: ${message}`); } }, }; - src/tools/get-quote.ts:8-33 (schema)Zod schema defining input parameters for ODOS_GET_QUOTE: chain (defaults to fraxtal), fromToken, toToken (both validated as Ethereum addresses), amount (string in wei), and prettyFormat (boolean, defaults true).
const getQuoteParamsSchema = z.object({ chain: z .string() .optional() .describe( "The blockchain network to execute the transaction on. uses fraxtal as default", ) .default("fraxtal"), fromToken: z .string() .refine(isAddress, { message: "Invalid fromToken address" }) .describe("The token to swap from (address)."), toToken: z .string() .refine(isAddress, { message: "Invalid toToken address" }) .describe("The token to swap to (address)."), amount: z .string() .regex(/^\d+$/, { message: "Amount must be a string in wei (no decimals)" }) .describe("The amount of tokens to swap, in wei."), prettyFormat: z .boolean() .optional() .describe("Whether to pretty format the quote.") .default(true), }); - src/index.ts:5-18 (registration)Registration of the ODOS_GET_QUOTE tool in the FastMCP server via server.addTool(getQuoteTool).
import { getQuoteTool } from "./tools/get-quote.js"; import { swapTool } from "./tools/swap.js"; async function main() { console.log("Initializing MCP Odos Server..."); const server = new FastMCP({ name: "IQAI Odos MCP Server", version: "0.0.1", }); server.addTool(getQuoteTool); server.addTool(swapTool); server.addTool(chainIdTool); - src/services/get-quote.ts:8-97 (helper)Service class that executes the Odos API quote request (POST to /sor/quote/v2) and formats the QuoteResponse into a human-readable string.
export class GetQuoteActionService { private readonly walletService: WalletService; constructor(walletService: WalletService) { this.walletService = walletService; } async execute( fromToken: string, toToken: string, chainId: number, amount: string, ) { const userAddr = this.walletService.getWalletClient()?.account?.address; if (!userAddr) { throw new Error("User address is not defined"); } try { const response = await fetch(`${ODOS_API_URL}/sor/quote/v2`, { method: "POST", headers: { "Content-Type": "application/json", }, body: JSON.stringify({ chainId, userAddr, inputTokens: [ { tokenAddress: fromToken, amount, }, ], outputTokens: [ { proportion: 1, tokenAddress: toToken, }, ], slippageLimitPercent: 0.3, referralCode: 0, disableRFQs: true, compact: true, }), }); const data = await response.json(); if (!response.ok) { const errorData = data as ErrorResponse; throw new Error( `Failed to fetch quote: ${errorData.detail} (Trace ID: ${errorData.traceId}, Error Code: ${errorData.errorCode})`, ); } return data as QuoteResponse; } catch (error) { console.error("Error fetching quote:", error); throw new Error( `Fatally Failed to fetch quote: ${(error as Error).message} with code ${ (error as { code?: string }).code || "unknown" }`, ); } } format(quote: QuoteResponse) { const formattedQuote = dedent` 💱 Quote Details - Input: ${formatUnits(BigInt(quote.inAmounts[0]), 18)} ${quote.inTokens[0]} - Output: ${formatUnits(BigInt(quote.outAmounts[0]), 18)} ${quote.outTokens[0]} - Price Impact: ${quote.priceImpact ? `${quote.priceImpact?.toFixed(2)}%` : "N/A"} - Gas Estimate: ${quote.gasEstimate} (${quote.gasEstimateValue.toFixed(2)} USD) - Net Output Value: $${quote.netOutValue.toFixed(2)} - Deprecated: ${quote.deprecated ? quote.deprecated : "N/A"} - Partner Fee Percent: ${quote.partnerFeePercent} % - Path Viz Image: ${quote.pathVizImage ? quote.pathVizImage : "N/A"} - Path ID: ${quote.pathId ? quote.pathId : "N/A"} - Block Number: ${quote.blockNumber} - Percent Diff: ${quote.percentDiff.toFixed(2)}% - Data Gas Estimate: ${quote.dataGasEstimate} units - Gwei Per Gas: ${quote.gweiPerGas} gwei - In Values: ${quote.inValues.map((v) => v.toFixed(2)).join(", ")} - Out Values: ${quote.outValues.map((v) => v.toFixed(2)).join(", ")} - other data: ${JSON.stringify(quote, null, 2)} `; return formattedQuote; } }