ref_finance_execute_swap
Swap tokens on Ref Finance by specifying input and output tokens, using smart routing or a specific pool to get optimal rates.
Instructions
Execute a swap on Ref Finance based on two tokens and a pool id. Prioritize pools with higher liquidity and better rates for the user.
Input Schema
| Name | Required | Description | Default |
|---|---|---|---|
| accountId | Yes | The account id of the user doing the swap | |
| tokenIn | Yes | ||
| tokenOut | Yes | ||
| amount | Yes | The amount of the input tokens to swap | |
| swapType | 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:2459-2715 (registration)Registration of the 'ref_finance_execute_swap' tool via mcp.tool() in createMcpServer. This is where the tool name, description, input schema (Zod), and handler are defined.
mcp.tool( 'ref_finance_execute_swap', noLeadingWhitespace` Execute a swap on Ref Finance based on two tokens and a pool id. Prioritize pools with higher liquidity and better rates for the user.`, { accountId: z .string() .describe('The account id of the user doing the swap'), 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'), swapType: 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}` }], }; } // 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 swapType = args.swapType; if (swapType.type === 'byPoolId') { const poolResult = await refFinanceGetPoolFromId( connection, swapType.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}` }], }; } // execute swap const swapResult = await executeRefSwap( connection, args.accountId, tokenIn, amountInDecimals.toString(), [ { pool_id: poolInfo.id, token_in: tokenIn.accountId, amount_out: '0', token_out: tokenOut.accountId, min_amount_out: poolEstimate.value.estimate, }, ], ); return { content: [ { type: 'text', text: `Swap result: ${stringify_bigint(swapResult)}`, }, ], }; } else { const smartRouteEstimate = await getSmartRouteRefSwapEstimate( amountInDecimals.toString(), tokenIn.accountId, tokenOut.accountId, swapType.pathDepth, swapType.slippagePercent, ); if (!smartRouteEstimate.ok) { return { content: [ { type: 'text', text: `Error: ${smartRouteEstimate.error}` }, ], }; } // execute swap const swapResult = await executeRefSwap( connection, args.accountId, tokenIn, amountInDecimals.toString(), refSwapEstimateToActions(smartRouteEstimate.value), ); if (!swapResult.ok) { return { content: [{ type: 'text', text: `Error: ${swapResult.error}` }], }; } return { content: [ { type: 'text', text: `Swap result: ${stringify_bigint(swapResult.value)}`, }, ], }; } }, ); - src/services.ts:2526-2714 (handler)The handler function for ref_finance_execute_swap. It validates tokens, gets metadata, converts amounts, gets estimates (by pool ID or smart route), and calls executeRefSwap to perform the swap on-chain.
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}` }], }; } // 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 swapType = args.swapType; if (swapType.type === 'byPoolId') { const poolResult = await refFinanceGetPoolFromId( connection, swapType.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}` }], }; } // execute swap const swapResult = await executeRefSwap( connection, args.accountId, tokenIn, amountInDecimals.toString(), [ { pool_id: poolInfo.id, token_in: tokenIn.accountId, amount_out: '0', token_out: tokenOut.accountId, min_amount_out: poolEstimate.value.estimate, }, ], ); return { content: [ { type: 'text', text: `Swap result: ${stringify_bigint(swapResult)}`, }, ], }; } else { const smartRouteEstimate = await getSmartRouteRefSwapEstimate( amountInDecimals.toString(), tokenIn.accountId, tokenOut.accountId, swapType.pathDepth, swapType.slippagePercent, ); if (!smartRouteEstimate.ok) { return { content: [ { type: 'text', text: `Error: ${smartRouteEstimate.error}` }, ], }; } // execute swap const swapResult = await executeRefSwap( connection, args.accountId, tokenIn, amountInDecimals.toString(), refSwapEstimateToActions(smartRouteEstimate.value), ); if (!swapResult.ok) { return { content: [{ type: 'text', text: `Error: ${swapResult.error}` }], }; } return { content: [ { type: 'text', text: `Swap result: ${stringify_bigint(swapResult.value)}`, }, ], }; } }, - src/services.ts:2464-2525 (schema)Input schema (Zod) for the ref_finance_execute_swap tool. Defines accountId, tokenIn, tokenOut, amount, swapType (bySmartRoute or byPoolId), and networkId parameters.
{ accountId: z .string() .describe('The account id of the user doing the swap'), 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'), swapType: 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:378-409 (helper)The executeRefSwap helper function that actually performs the swap by calling ft_transfer_call on the input token contract with Ref Finance actions as the message.
export const executeRefSwap = async ( connection: Near, accountId: string, tokenIn: Account, tokenAmountIn: string, actions: RefSwapByOutputAction[], ): Promise<Result<object, Error>> => { try { if (actions.length === 0) throw new Error('No actions to execute'); const network = connection.connection.networkId; const refConfig = refGetConfig(network); const signer = await connection.account(accountId); const swapResult = await signer.functionCall({ contractId: tokenIn.accountId, methodName: 'ft_transfer_call', args: { receiver_id: refConfig.REF_FI_CONTRACT_ID, amount: tokenAmountIn, msg: JSON.stringify({ actions, }), }, gas: DEFAULT_GAS, attachedDeposit: NearToken.parse_yocto_near('1').as_yocto_near(), }); return { ok: true, value: swapResult }; } catch (error) { return { ok: false, error: error as Error }; } }; - src/utils.ts:235-249 (helper)The refSwapEstimateToActions helper that converts a SwapEstimate from the smart router into RefSwapByOutputAction[] format needed for executeRefSwap.
export const refSwapEstimateToActions = ( estimate: SwapEstimate, ): RefSwapByOutputAction[] => { return estimate.result_data.routes.flatMap((route) => route.pools.map((pool) => { return { pool_id: Number(pool.pool_id), token_in: pool.token_in, amount_out: route.amount_out, token_out: pool.token_out, min_amount_out: pool.min_amount_out, }; }), ); };