Skip to main content
Glama

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

TableJSON Schema
NameRequiredDescriptionDefault
accountIdYesThe account id of the user doing the swap
tokenInYes
tokenOutYes
amountYesThe amount of the input tokens to swap
swapTypeNoThe type of estimate to get. Defaults to the ref finance smart router to find the best price over all available pools
networkIdNomainnet

Implementation Reference

  • 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)}`,
              },
            ],
          };
        }
      },
    );
  • 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)}`,
            },
          ],
        };
      }
    },
  • 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'),
    },
  • 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 };
      }
    };
  • 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,
          };
        }),
      );
    };
Behavior2/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

With no annotations, the description bears full burden but offers minimal behavioral details: it mentions 'prioritize pools with higher liquidity and better rates' but does not disclose atomicity, return value, failure modes, or authorization needs (e.g., token approval).

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Two concise sentences, front-loaded with core action. The second sentence adds some value but is somewhat redundant with the smart routing default. No wasted words.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness2/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

Given the tool's complexity (nested objects, two swap methods, no output schema), the description is insufficient. It omits key aspects: what the return value is, how to interpret the swap types, and the significance of slippage/path depth parameters.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 50%, but the schema itself describes many parameters (tokenIn, tokenOut, amount, swapType). The description adds a high-level prioritization hint but does not clarify the pool id parameter's optionality or refine the swapType choices beyond the schema.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose4/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description clearly states the action (execute a swap) and resource (Ref Finance), but it ambiguously mentions 'based on two tokens and a pool id' while the schema supports both pool-specific and smart routing. It does not fully differentiate the two modes.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines2/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

No explicit guidance on when to use this tool versus alternatives like 'ref_finance_get_swap_estimate' or prerequisites for a swap. The description only implies the core action without context.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

Install Server

Other Tools

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/nearai/near-mcp'

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