bostrom-mcp/src/tools/liquidity.ts

import { z } from "zod";
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { ok, safe, WRITE_ANNOTATIONS, READ_ONLY_ANNOTATIONS } from "../util.js";
import * as svc from "../services/liquidity.js";

export function registerLiquidityTools(server: McpServer) {
  server.registerTool(
    "liquidity_create_pool",
    {
      description:
        "Create a new Gravity DEX liquidity pool. WARNING: costs ~1,000 BOOT. " +
        "Deposit coins are sorted alphabetically by denom automatically.",
      inputSchema: {
        denom_a: z.string().describe("First token denom"),
        amount_a: z.string().describe("First token amount (base units)"),
        denom_b: z.string().describe("Second token denom"),
        amount_b: z.string().describe("Second token amount (base units)"),
      },
      annotations: WRITE_ANNOTATIONS,
    },
    safe(async ({ denom_a, amount_a, denom_b, amount_b }) =>
      ok(await svc.createPool(denom_a, amount_a, denom_b, amount_b)),
    ),
  );

  server.registerTool(
    "liquidity_deposit",
    {
      description:
        "Deposit tokens into an existing liquidity pool. " +
        "Executes in batch at end of block.",
      inputSchema: {
        pool_id: z.number().min(1).describe("Pool ID"),
        denom_a: z.string().describe("First token denom"),
        amount_a: z.string().describe("First token amount"),
        denom_b: z.string().describe("Second token denom"),
        amount_b: z.string().describe("Second token amount"),
      },
      annotations: WRITE_ANNOTATIONS,
    },
    safe(async ({ pool_id, denom_a, amount_a, denom_b, amount_b }) =>
      ok(await svc.deposit(pool_id, denom_a, amount_a, denom_b, amount_b)),
    ),
  );

  server.registerTool(
    "liquidity_withdraw",
    {
      description:
        "Withdraw LP tokens from a pool to receive underlying assets. " +
        "Executes in batch at end of block.",
      inputSchema: {
        pool_id: z.number().min(1).describe("Pool ID"),
        pool_coin_amount: z.string().describe("Amount of LP tokens to withdraw"),
        pool_coin_denom: z.string().describe("LP token denom (pool{id} format)"),
      },
      annotations: WRITE_ANNOTATIONS,
    },
    safe(async ({ pool_id, pool_coin_amount, pool_coin_denom }) =>
      ok(await svc.withdraw(pool_id, pool_coin_amount, pool_coin_denom)),
    ),
  );

  server.registerTool(
    "liquidity_swap",
    {
      description:
        "Swap tokens via a Gravity DEX pool. " +
        "Batched execution: swap executes at end of block, not immediately. " +
        "order_price is the limit price (use pool price for market swap).",
      inputSchema: {
        pool_id: z.number().min(1).describe("Pool ID"),
        offer_denom: z.string().describe("Denom you are selling"),
        offer_amount: z.string().describe("Amount you are selling (base units)"),
        demand_denom: z.string().describe("Denom you want to receive"),
        order_price: z.string().describe("Limit price (decimal string, e.g. '1.5')"),
      },
      annotations: WRITE_ANNOTATIONS,
    },
    safe(async ({ pool_id, offer_denom, offer_amount, demand_denom, order_price }) =>
      ok(await svc.swap(pool_id, offer_denom, offer_amount, demand_denom, order_price)),
    ),
  );

  server.registerTool(
    "liquidity_pool_detail",
    {
      description:
        "Get pool details: reserves, parameters, current batch info.",
      inputSchema: {
        pool_id: z.number().min(1).describe("Pool ID"),
      },
      annotations: READ_ONLY_ANNOTATIONS,
    },
    safe(async ({ pool_id }) =>
      ok(await svc.getPoolDetail(pool_id)),
    ),
  );

  server.registerTool(
    "swap_tokens",
    {
      description:
        "Swap tokens using Gravity DEX. Auto-discovers the right pool and calculates market price. " +
        "Applies slippage tolerance (default 3%). Batched execution: swap executes at end of block. " +
        "Use swap_estimate first to preview the swap.",
      inputSchema: {
        offer_denom: z.string().describe("Denom you are selling (e.g. 'boot', 'hydrogen')"),
        offer_amount: z.string().describe("Amount you are selling (base units)"),
        demand_denom: z.string().describe("Denom you want to receive"),
        slippage_percent: z.number().min(0).max(50).default(3).describe("Slippage tolerance in percent (default 3)"),
      },
      annotations: WRITE_ANNOTATIONS,
    },
    safe(async ({ offer_denom, offer_amount, demand_denom, slippage_percent }) =>
      ok(await svc.swapTokens(offer_denom, offer_amount, demand_denom, slippage_percent)),
    ),
  );

  server.registerTool(
    "swap_estimate",
    {
      description:
        "Estimate a token swap: find the pool, get current price, and calculate expected output. " +
        "Does not execute any transaction โ€” use swap_tokens to execute.",
      inputSchema: {
        offer_denom: z.string().describe("Denom you are selling"),
        offer_amount: z.string().describe("Amount you are selling (base units)"),
        demand_denom: z.string().describe("Denom you want to receive"),
      },
      annotations: READ_ONLY_ANNOTATIONS,
    },
    safe(async ({ offer_denom, offer_amount, demand_denom }) => {
      const found = await svc.findPool(offer_denom, demand_denom);
      if (!found) {
        return ok({
          error: `No pool found for ${offer_denom}/${demand_denom}`,
          hint: "Check available pools with economy_pools",
        });
      }
      const { pool, reserves } = found;
      const offerReserve = Number(reserves[offer_denom] || "0");
      const demandReserve = Number(reserves[demand_denom] || "0");
      const marketPrice = offerReserve / demandReserve;
      const estimatedOutput = Math.floor(Number(offer_amount) * demandReserve / offerReserve);
      // Swap fee (0.3% from params, half from each side)
      const feeAmount = Math.ceil(Number(offer_amount) * 0.003 / 2);
      return ok({
        poolId: parseInt(pool.id),
        poolDenoms: pool.reserve_coin_denoms,
        reserves,
        marketPrice: marketPrice.toFixed(8),
        estimatedOutput: String(estimatedOutput),
        offerCoinFee: String(feeAmount),
        note: "Estimates only โ€” actual execution depends on batch settlement",
      });
    }),
  );
}

Synonyms

soft3.js/src/queries/liquidity.ts
bostrom-mcp/src/services/liquidity.ts
soft3.js/src/codec/tendermint/liquidity/v1beta1/liquidity.ts
cyber-ts/packages/cyber-ts/src/cyber/liquidity/v1beta1/liquidity.ts

Neighbours