ref_finance_get_swap_estimate
Get a swap estimate for exchanging tokens on Ref Finance via NEAR, using either the smart router for best price or a specific pool ID.
Instructions
Get a swap estimate from the Ref Finance exchange contract based on two tokens and a pool id.
Input Schema
| Name | Required | Description | Default |
|---|---|---|---|
| tokenIn | Yes | ||
| tokenOut | Yes | ||
| amount | Yes | The amount of the input tokens to swap | |
| estimateType | No | The type of estimate to get. Defaults to the ref finance smart router to find the best price over all available pools | |
| networkId | No | mainnet |
Implementation Reference
- src/services.ts:2235-2457 (registration)The tool 'ref_finance_get_swap_estimate' is registered via mcp.tool() with its schema (input validation via Zod) and handler function. This is the primary registration and handler location.
mcp.tool( 'ref_finance_get_swap_estimate', noLeadingWhitespace` Get a swap estimate from the Ref Finance exchange contract based on two tokens and a pool id.`, { tokenIn: z.object({ contractId: z .string() .describe('The contract id of the input token to be swapped'), symbol: z.string().describe('The symbol of the input token'), }), tokenOut: z.object({ contractId: z .string() .describe('The contract id of the output token to be swapped'), symbol: z.string().describe('The symbol of the output token'), }), amount: z .union([ z .number() .describe( 'The amount of input tokens with decimal formatting (e.g., 1.5 wNEAR)', ), z .bigint() .describe( 'The amount in smallest denomination (e.g., yoctowNEAR for wNEAR, or equivalent for other tokens based on their decimals)', ), ]) .describe('The amount of the input tokens to swap'), estimateType: z .union([ z.object({ type: z .literal('bySmartRoute') .describe( 'Get an estimate using the ref finance smart router to find the best pool', ), pathDepth: z .number() .default(3) .describe('The depth of the path to search for the best pool'), slippagePercent: z .number() .default(0.001) .describe( 'The slippage to use for the estimate. Only use 0.001, 0.005, or 0.01', ), }), z.object({ type: z .literal('byPoolId') .describe('Get an estimate using a specific pool id'), poolId: z.number().describe('The pool id (e.g. 1)'), }), ]) .default({ type: 'bySmartRoute' }) .describe( 'The type of estimate to get. Defaults to the ref finance smart router to find the best price over all available pools', ), networkId: z.enum(['testnet', 'mainnet']).default('mainnet'), }, async (args, _) => { if (args.tokenIn === args.tokenOut) { return { content: [ { type: 'text', text: `Error: Token in and token out cannot be the same`, }, ], }; } const connection = await connect({ networkId: args.networkId, keyStore: keystore, nodeUrl: getEndpointsByNetwork(args.networkId)[0]!, }); const tokenInContractAccountResult = await getAccount( args.tokenIn.contractId, connection, ); if (!tokenInContractAccountResult.ok) { return { content: [ { type: 'text', text: `Error: ${tokenInContractAccountResult.error}`, }, ], }; } const tokenIn = tokenInContractAccountResult.value; const tokenOutContractAccountResult = await getAccount( args.tokenOut.contractId, connection, ); if (!tokenOutContractAccountResult.ok) { return { content: [ { type: 'text', text: `Error: ${tokenOutContractAccountResult.error}`, }, ], }; } const tokenOut = tokenOutContractAccountResult.value; const tokenInMetadata = await getFungibleTokenContractMetadataResult( tokenIn.accountId, connection, ); if (!tokenInMetadata.ok) { return { content: [{ type: 'text', text: `Error: ${tokenInMetadata.error}` }], }; } const tokenOutMetadata = await getFungibleTokenContractMetadataResult( tokenOut.accountId, connection, ); if (!tokenOutMetadata.ok) { return { content: [{ type: 'text', text: `Error: ${tokenOutMetadata.error}` }], }; } const estimateType = args.estimateType; if (estimateType.type === 'byPoolId') { const poolResult = await refFinanceGetPoolFromId( connection, estimateType.poolId, ); if (!poolResult.ok) { return { content: [{ type: 'text', text: `Error: ${poolResult.error}` }], }; } const poolInfo = poolResult.value; if ( !poolInfo.tokenIds.includes(tokenIn.accountId) || !poolInfo.tokenIds.includes(tokenOut.accountId) ) { return { content: [ { type: 'text', text: `Error: Pool tokens [${poolInfo.tokenIds.join(', ')}] do not include ${args.tokenIn.contractId} or ${args.tokenOut.contractId}`, }, ], }; } // calculate the pool estimate const poolEstimate = await refFinanceGetEstimate( { id: tokenIn.accountId, metadata: tokenInMetadata.value, }, { id: tokenOut.accountId, metadata: tokenOutMetadata.value, }, poolInfo, args.amount.toString(), ); if (!poolEstimate.ok) { return { content: [{ type: 'text', text: `Error: ${poolEstimate.error}` }], }; } return { content: [ { type: 'text', text: `Pool info: ${stringify_bigint(poolEstimate.value)}`, }, ], }; } else { // convert the amount into the decimals of the fungible token const amountInDecimals = typeof args.amount === 'number' ? BigInt( Math.floor(args.amount * 10 ** tokenInMetadata.value.decimals), ) : args.amount; const smartRouteEstimate = await getSmartRouteRefSwapEstimate( amountInDecimals.toString(), tokenIn.accountId, tokenOut.accountId, estimateType.pathDepth, estimateType.slippagePercent, ); if (!smartRouteEstimate.ok) { return { content: [ { type: 'text', text: `Error: ${smartRouteEstimate.error}` }, ], }; } return { content: [ { type: 'text', text: `Smart route estimate: ${stringify_bigint( smartRouteEstimate.value, )}`, }, ], }; } }, ); - src/services.ts:2298-2456 (handler)The handler function that executes the swap estimate logic. It validates tokens, fetches metadata, and dispatches to either 'byPoolId' estimate (using refFinanceGetEstimate helper) or 'bySmartRoute' estimate (using getSmartRouteRefSwapEstimate from utils).
async (args, _) => { if (args.tokenIn === args.tokenOut) { return { content: [ { type: 'text', text: `Error: Token in and token out cannot be the same`, }, ], }; } const connection = await connect({ networkId: args.networkId, keyStore: keystore, nodeUrl: getEndpointsByNetwork(args.networkId)[0]!, }); const tokenInContractAccountResult = await getAccount( args.tokenIn.contractId, connection, ); if (!tokenInContractAccountResult.ok) { return { content: [ { type: 'text', text: `Error: ${tokenInContractAccountResult.error}`, }, ], }; } const tokenIn = tokenInContractAccountResult.value; const tokenOutContractAccountResult = await getAccount( args.tokenOut.contractId, connection, ); if (!tokenOutContractAccountResult.ok) { return { content: [ { type: 'text', text: `Error: ${tokenOutContractAccountResult.error}`, }, ], }; } const tokenOut = tokenOutContractAccountResult.value; const tokenInMetadata = await getFungibleTokenContractMetadataResult( tokenIn.accountId, connection, ); if (!tokenInMetadata.ok) { return { content: [{ type: 'text', text: `Error: ${tokenInMetadata.error}` }], }; } const tokenOutMetadata = await getFungibleTokenContractMetadataResult( tokenOut.accountId, connection, ); if (!tokenOutMetadata.ok) { return { content: [{ type: 'text', text: `Error: ${tokenOutMetadata.error}` }], }; } const estimateType = args.estimateType; if (estimateType.type === 'byPoolId') { const poolResult = await refFinanceGetPoolFromId( connection, estimateType.poolId, ); if (!poolResult.ok) { return { content: [{ type: 'text', text: `Error: ${poolResult.error}` }], }; } const poolInfo = poolResult.value; if ( !poolInfo.tokenIds.includes(tokenIn.accountId) || !poolInfo.tokenIds.includes(tokenOut.accountId) ) { return { content: [ { type: 'text', text: `Error: Pool tokens [${poolInfo.tokenIds.join(', ')}] do not include ${args.tokenIn.contractId} or ${args.tokenOut.contractId}`, }, ], }; } // calculate the pool estimate const poolEstimate = await refFinanceGetEstimate( { id: tokenIn.accountId, metadata: tokenInMetadata.value, }, { id: tokenOut.accountId, metadata: tokenOutMetadata.value, }, poolInfo, args.amount.toString(), ); if (!poolEstimate.ok) { return { content: [{ type: 'text', text: `Error: ${poolEstimate.error}` }], }; } return { content: [ { type: 'text', text: `Pool info: ${stringify_bigint(poolEstimate.value)}`, }, ], }; } else { // convert the amount into the decimals of the fungible token const amountInDecimals = typeof args.amount === 'number' ? BigInt( Math.floor(args.amount * 10 ** tokenInMetadata.value.decimals), ) : args.amount; const smartRouteEstimate = await getSmartRouteRefSwapEstimate( amountInDecimals.toString(), tokenIn.accountId, tokenOut.accountId, estimateType.pathDepth, estimateType.slippagePercent, ); if (!smartRouteEstimate.ok) { return { content: [ { type: 'text', text: `Error: ${smartRouteEstimate.error}` }, ], }; } return { content: [ { type: 'text', text: `Smart route estimate: ${stringify_bigint( smartRouteEstimate.value, )}`, }, ], }; } }, - src/services.ts:2239-2297 (schema)The input schema using Zod defining all parameters: tokenIn, tokenOut, amount, estimateType (bySmartRoute or byPoolId with pathDepth/slippagePercent or poolId), and networkId.
{ tokenIn: z.object({ contractId: z .string() .describe('The contract id of the input token to be swapped'), symbol: z.string().describe('The symbol of the input token'), }), tokenOut: z.object({ contractId: z .string() .describe('The contract id of the output token to be swapped'), symbol: z.string().describe('The symbol of the output token'), }), amount: z .union([ z .number() .describe( 'The amount of input tokens with decimal formatting (e.g., 1.5 wNEAR)', ), z .bigint() .describe( 'The amount in smallest denomination (e.g., yoctowNEAR for wNEAR, or equivalent for other tokens based on their decimals)', ), ]) .describe('The amount of the input tokens to swap'), estimateType: z .union([ z.object({ type: z .literal('bySmartRoute') .describe( 'Get an estimate using the ref finance smart router to find the best pool', ), pathDepth: z .number() .default(3) .describe('The depth of the path to search for the best pool'), slippagePercent: z .number() .default(0.001) .describe( 'The slippage to use for the estimate. Only use 0.001, 0.005, or 0.01', ), }), z.object({ type: z .literal('byPoolId') .describe('Get an estimate using a specific pool id'), poolId: z.number().describe('The pool id (e.g. 1)'), }), ]) .default({ type: 'bySmartRoute' }) .describe( 'The type of estimate to get. Defaults to the ref finance smart router to find the best price over all available pools', ), networkId: z.enum(['testnet', 'mainnet']).default('mainnet'), }, - src/services.ts:259-307 (helper)The refFinanceGetEstimate helper function that calculates the swap estimate for a specific pool using constant product AMM formula.
export const refFinanceGetEstimate = async ( tokenIn: TokenMetadata, tokenOut: TokenMetadata, pool: Pool, amountIn: string, ): Promise<Result<RefFinanceEstimate, Error>> => { try { const amountInBigInt = BigInt(amountIn); const feeDivisorBigInt = BigInt(FEE_DIVISOR); const poolFeeBigInt = BigInt(pool.fee); const amount_with_fee = amountInBigInt * (feeDivisorBigInt - poolFeeBigInt); // Use the raw supply values which should be strings representing integers const in_balance = BigInt( toReadableNumber(tokenIn.metadata.decimals, pool.supplies[tokenIn.id]), ); const out_balance = BigInt( toReadableNumber(tokenOut.metadata.decimals, pool.supplies[tokenOut.id]), ); // Perform calculation using BigInt division // Note: BigInt division truncates the result (floor division) const numerator = amount_with_fee * out_balance; const denominator = feeDivisorBigInt * in_balance + amount_with_fee; // Avoid division by zero if (denominator === 0n) { return { ok: false, error: new Error('Division by zero in estimate calculation'), }; } const estimate = (numerator / denominator).toString(); return { ok: true, value: { estimate, pool, outputToken: tokenOut.id, inputToken: tokenIn.id, }, }; } catch (e) { return { ok: false, error: new Error(e as string) }; } }; - src/utils.ts:198-225 (helper)The getSmartRouteRefSwapEstimate helper function that calls the Ref Finance smart router API to find the best swap route across pools.
export const getSmartRouteRefSwapEstimate = async ( amountIn: string, tokenIn: string, tokenOut: string, pathDepth: number, slippagePercent: number, ): Promise<Result<SwapEstimate, Error>> => { try { const params = new URLSearchParams({ amountIn, tokenIn, tokenOut, pathDeep: pathDepth.toString(), slippage: slippagePercent.toString(), }); const url = `https://smartrouter.ref.finance/findPath?${params.toString()}`; const response = await fetch(url); if (!response.ok) { throw new Error(`Failed to fetch swap estimate: ${response.statusText}`); } const data = (await response.json()) as SwapEstimate; return { ok: true, value: data }; } catch (error) { return { ok: false, error: error as Error }; } };