Skip to main content
Glama

write.account.remove_liquidity

Idempotent

Partially decrease liquidity from an Arcadia Finance LP position while keeping the position open and tokens in your account. Use this tool to adjust exposure without fully closing positions.

Instructions

Flash-action: PARTIALLY decreases liquidity from an LP position. The position remains open with reduced liquidity; underlying tokens stay in the account.

For FULL position exit (burn LP + swap + repay + withdraw), use write.account.close instead — it batches everything into one atomic transaction.

The returned calldata is time-sensitive — sign and broadcast within 30 seconds. If the transaction reverts due to price movement, rebuild and sign again immediately (retry at least once before giving up). Response includes tenderly_sim_url and tenderly_sim_status for pre-broadcast validation.

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
account_addressYesArcadia account address
asset_addressYesPosition manager contract
asset_idYesNFT token ID
adjustmentYesLiquidity amount to remove (raw uint128 value as string). Must be less than total liquidity — for full removal use write.account.close.
chain_idNoChain ID: 8453 (Base) or 130 (Unichain)

Output Schema

TableJSON Schema
NameRequiredDescriptionDefault
afterNo
beforeNo
descriptionNo
transactionYes
tenderly_sim_urlNo
tenderly_sim_statusNo
expected_value_changeNo

Implementation Reference

  • The definition and handler for the "write.account.remove_liquidity" tool. It validates the input and calls `api.getDecreaseLiquidityCalldata` to retrieve the transaction calldata.
      server.registerTool(
        "write.account.remove_liquidity",
        {
          annotations: {
            title: "Build Remove Liquidity Transaction",
            readOnlyHint: false,
            destructiveHint: false,
            idempotentHint: true,
            openWorldHint: true,
          },
          outputSchema: BatchedTransactionOutput,
          description: `Flash-action: PARTIALLY decreases liquidity from an LP position. The position remains open with reduced liquidity; underlying tokens stay in the account.
    
    For FULL position exit (burn LP + swap + repay + withdraw), use write.account.close instead — it batches everything into one atomic transaction.
    
    The returned calldata is time-sensitive — sign and broadcast within 30 seconds. If the transaction reverts due to price movement, rebuild and sign again immediately (retry at least once before giving up). Response includes tenderly_sim_url and tenderly_sim_status for pre-broadcast validation.`,
          inputSchema: {
            account_address: z.string().describe("Arcadia account address"),
            asset_address: z.string().describe("Position manager contract"),
            asset_id: z.number().describe("NFT token ID"),
            adjustment: z
              .string()
              .describe(
                "Liquidity amount to remove (raw uint128 value as string). Must be less than total liquidity — for full removal use write.account.close.",
              ),
            chain_id: z.number().default(8453).describe("Chain ID: 8453 (Base) or 130 (Unichain)"),
          },
        },
        async ({ account_address, asset_address, asset_id, adjustment, chain_id }) => {
          try {
            validateAddress(account_address, "account_address");
            validateAddress(asset_address, "asset_address");
            const result = await api.getDecreaseLiquidityCalldata({
              chain_id,
              account_address,
              asset_address,
              asset_id,
              adjustment,
            });
    
            const res = result as Record<string, unknown>;
            if (res.tenderly_sim_status === "false") {
              const simUrl = res.tenderly_sim_url
                ? `\nTenderly simulation: ${res.tenderly_sim_url}`
                : "";
              const simError = res.tenderly_sim_error
                ? `\nRevert reason: ${res.tenderly_sim_error}`
                : "";
              return {
                content: [
                  {
                    type: "text" as const,
                    text: `Error: Transaction simulation FAILED — do NOT broadcast.${simError}${simUrl}`,
                  },
                ],
                isError: true,
              };
            }
    
            const response = formatBatchedResponse(res, chain_id, "Remove liquidity from LP position");
            return {
              content: [
                {
                  type: "text" as const,
                  text: JSON.stringify(response, null, 2),
                },
              ],
              structuredContent: response,
            };
          } catch (err) {
            const msg = err instanceof Error ? err.message : String(err);
            const hint =
              msg.includes("500") || msg.includes("Web3")
                ? " This usually means the position (asset_id) does not exist in the account. Verify with read.account.info first."
                : "";
            return {
              content: [{ type: "text" as const, text: `Error: ${msg}${hint}` }],
              isError: true,
            };
          }
        },
      );
  • Function that registers the "write.account.remove_liquidity" tool to the MCP server.
    export function registerRemoveLiquidityTool(server: McpServer, api: ArcadiaApiClient) {
      server.registerTool(
        "write.account.remove_liquidity",
        {
          annotations: {
            title: "Build Remove Liquidity Transaction",
            readOnlyHint: false,
            destructiveHint: false,
            idempotentHint: true,
            openWorldHint: true,
          },
          outputSchema: BatchedTransactionOutput,
          description: `Flash-action: PARTIALLY decreases liquidity from an LP position. The position remains open with reduced liquidity; underlying tokens stay in the account.
    
    For FULL position exit (burn LP + swap + repay + withdraw), use write.account.close instead — it batches everything into one atomic transaction.
    
    The returned calldata is time-sensitive — sign and broadcast within 30 seconds. If the transaction reverts due to price movement, rebuild and sign again immediately (retry at least once before giving up). Response includes tenderly_sim_url and tenderly_sim_status for pre-broadcast validation.`,
          inputSchema: {
            account_address: z.string().describe("Arcadia account address"),
            asset_address: z.string().describe("Position manager contract"),
            asset_id: z.number().describe("NFT token ID"),
            adjustment: z
              .string()
              .describe(
                "Liquidity amount to remove (raw uint128 value as string). Must be less than total liquidity — for full removal use write.account.close.",
              ),
            chain_id: z.number().default(8453).describe("Chain ID: 8453 (Base) or 130 (Unichain)"),
          },
        },
        async ({ account_address, asset_address, asset_id, adjustment, chain_id }) => {
          try {
            validateAddress(account_address, "account_address");
            validateAddress(asset_address, "asset_address");
            const result = await api.getDecreaseLiquidityCalldata({
              chain_id,
              account_address,
              asset_address,
              asset_id,
              adjustment,
            });
    
            const res = result as Record<string, unknown>;
            if (res.tenderly_sim_status === "false") {
              const simUrl = res.tenderly_sim_url
                ? `\nTenderly simulation: ${res.tenderly_sim_url}`
                : "";
              const simError = res.tenderly_sim_error
                ? `\nRevert reason: ${res.tenderly_sim_error}`
                : "";
              return {
                content: [
                  {
                    type: "text" as const,
                    text: `Error: Transaction simulation FAILED — do NOT broadcast.${simError}${simUrl}`,
                  },
                ],
                isError: true,
              };
            }
    
            const response = formatBatchedResponse(res, chain_id, "Remove liquidity from LP position");
            return {
              content: [
                {
                  type: "text" as const,
                  text: JSON.stringify(response, null, 2),
                },
              ],
              structuredContent: response,
            };
          } catch (err) {
            const msg = err instanceof Error ? err.message : String(err);
            const hint =
              msg.includes("500") || msg.includes("Web3")
                ? " This usually means the position (asset_id) does not exist in the account. Verify with read.account.info first."
                : "";
            return {
              content: [{ type: "text" as const, text: `Error: ${msg}${hint}` }],
              isError: true,
            };
          }
        },
      );
    }

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/arcadia-finance/arcadia-finance-mcp-server'

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