Skip to main content
Glama

build_add_liquidity

Create an unsigned transaction to add liquidity to a token pair on the Casper Network DEX, specifying token amounts and slippage parameters.

Instructions

Build an unsigned add-liquidity transaction for a token pair

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
token_aYesFirst token: symbol, name, or hash
token_bYesSecond token: symbol, name, or hash
amount_aYesHuman-readable amount of first token
amount_bYesHuman-readable amount of second token
slippage_bpsNoSlippage in basis points (default 300)
deadline_minutesNoDeadline in minutes (default 20)
sender_public_keyYesSender hex public key
token_a_balanceNoRaw token A balance for one-time approval
token_b_balanceNoRaw token B balance for one-time approval

Implementation Reference

  • The 'build_add_liquidity' tool is registered in this file, which calls the client's buildAddLiquidity method.
    server.tool(
      'build_add_liquidity',
      'Build an unsigned add-liquidity transaction for a token pair',
      {
        token_a: z.string().describe('First token: symbol, name, or hash'),
        token_b: z.string().describe('Second token: symbol, name, or hash'),
        amount_a: z.string().describe('Human-readable amount of first token'),
        amount_b: z.string().describe('Human-readable amount of second token'),
        slippage_bps: z.number().optional().describe('Slippage in basis points (default 300)'),
        deadline_minutes: z.number().optional().describe('Deadline in minutes (default 20)'),
        sender_public_key: z.string().describe('Sender hex public key'),
        token_a_balance: z.string().optional().describe('Raw token A balance for one-time approval'),
        token_b_balance: z.string().optional().describe('Raw token B balance for one-time approval'),
      },
      async (args) => {
        const bundle = await client.buildAddLiquidity({
          tokenA: args.token_a,
          tokenB: args.token_b,
          amountA: args.amount_a,
          amountB: args.amount_b,
          slippageBps: args.slippage_bps,
          deadlineMinutes: args.deadline_minutes,
          senderPublicKey: args.sender_public_key,
          tokenABalance: args.token_a_balance,
          tokenBBalance: args.token_b_balance,
        });
    
        const parts = [bundle.summary];
    
        if (bundle.approvalsRequired?.length) {
          parts.push('\n--- APPROVALS REQUIRED ---');
          for (let i = 0; i < bundle.approvalsRequired.length; i++) {
            const approval = bundle.approvalsRequired[i];
            const approvalPath = await writeDeployFile(approval.transactionJson);
            parts.push(`\nStep ${i + 1}: ${approval.summary}`);
            parts.push(`Approval transaction saved to: ${approvalPath}`);
            parts.push(`Gas: ${approval.estimatedGasCost}`);
          }
          parts.push('\n--- ADD LIQUIDITY TRANSACTION ---');
        }
    
        const deployPath = await writeDeployFile(bundle.transactionJson);
        parts.push(`\nTransaction saved to: ${deployPath}`);
    
        if (bundle.approvalsRequired?.length) {
          parts.push('\nWorkflow: Sign and submit each approval with submit_transaction, then sign and submit the add-liquidity transaction with submit_transaction.');
        }
    
        return { content: [{ type: 'text' as const, text: parts.join('\n') }] };
      },
    );
  • The actual implementation of 'buildAddLiquidity' which calculates transaction arguments and builds the unsigned transaction.
    async buildAddLiquidity(params: AddLiquidityParams): Promise<TransactionBundle> {
      const tokenA = await this.tokenResolver.resolve(params.tokenA);
      const tokenB = await this.tokenResolver.resolve(params.tokenB);
    
      const slippageBps = params.slippageBps ?? DEFAULT_SLIPPAGE_BPS;
      const deadlineMinutes = params.deadlineMinutes ?? DEFAULT_DEADLINE_MINUTES;
      const deadline = Date.now() + deadlineMinutes * 60 * 1000;
    
      const isCSPRPair = tokenA.id === CSPR_TOKEN_ID || tokenB.id === CSPR_TOKEN_ID;
      const rawAmountA = toRawAmount(params.amountA, tokenA.decimals);
      const rawAmountB = toRawAmount(params.amountB, tokenB.decimals);
      const accountHash = PublicKey.fromHex(params.senderPublicKey).accountHash().toPrefixedString();
    
      let innerArgs;
      let entryPoint: string;
      let attachedValue = '0';
    
      if (isCSPRPair) {
        const csprToken = tokenA.id === CSPR_TOKEN_ID ? tokenA : tokenB;
        const otherToken = tokenA.id === CSPR_TOKEN_ID ? tokenB : tokenA;
        const motesAmount = tokenA.id === CSPR_TOKEN_ID ? rawAmountA : rawAmountB;
        const tokenAmount = tokenA.id === CSPR_TOKEN_ID ? rawAmountB : rawAmountA;
    
        innerArgs = buildAddLiquidityInnerArgs({
          isCSPRPair: true,
          tokenHash: otherToken.packageHash,
          amountTokenDesired: tokenAmount,
          amountTokenMin: calculateMinWithSlippage(tokenAmount, slippageBps),
          amountCSPRMin: calculateMinWithSlippage(motesAmount, slippageBps),
          accountHash,
          deadline,
        });
        entryPoint = 'add_liquidity_cspr';
        attachedValue = motesAmount;
      } else {
        // Sort tokens by package hash for consistency
        const [sortedA, sortedB, sortedAmountA, sortedAmountB] =
          tokenA.packageHash < tokenB.packageHash
            ? [tokenA, tokenB, rawAmountA, rawAmountB]
            : [tokenB, tokenA, rawAmountB, rawAmountA];
    
        innerArgs = buildAddLiquidityInnerArgs({
          isCSPRPair: false,
          tokenAHash: sortedA.packageHash,
          tokenBHash: sortedB.packageHash,
          amountADesired: sortedAmountA,
          amountBDesired: sortedAmountB,
          amountAMin: calculateMinWithSlippage(sortedAmountA, slippageBps),
          amountBMin: calculateMinWithSlippage(sortedAmountB, slippageBps),
          accountHash,
          deadline,
        });
        entryPoint = 'add_liquidity';
      }
    
      // Use higher gas for potentially new pools
      const gasCost = GAS_COSTS.addLiquidity; // caller can override if new pool
    
      const proxyArgs = buildProxyWasmArgs({
        routerPackageHash: this.networkConfig.routerPackageHash.replace('hash-', ''),
        entryPoint,
        innerArgs,
        attachedValue,
      });
      const wasmBinary = await getProxyCallerWasm();
      const transaction = buildWasmTransaction({
        publicKey: params.senderPublicKey,
        paymentAmount: gasCost.toString(),
        wasmBinary,
        runtimeArgs: proxyArgs,
        networkConfig: this.networkConfig,
      });
    
      // Build approval transactions for non-CSPR tokens.
      // When CSPR is one side, the router handles wrapping internally — no approval needed for that side.
      const tokenBalances = [params.tokenABalance, params.tokenBBalance];
      const rawAmounts = [rawAmountA, rawAmountB];
      const approvals: TransactionBundle[] = [];
      for (let i = 0; i < 2; i++) {
        const token = [tokenA, tokenB][i];
        if (token.id === CSPR_TOKEN_ID) continue; // Router handles CSPR wrapping
        const approvalAmount = tokenBalances[i] ?? rawAmounts[i];
        const approval = await this.buildApproval({
          tokenContractPackageHash: token.packageHash,
          spenderPackageHash: this.networkConfig.routerPackageHash,
          amount: approvalAmount,
          senderPublicKey: params.senderPublicKey,
        });
        approval.summary = tokenBalances[i]
          ? `Approve ${token.symbol} for router (full balance — one-time)`
          : `Approve ${token.symbol} for router (liquidity amount only)`;
        approvals.push(approval);
      }
    
      const summary = [
        `Add liquidity: ${params.amountA} ${tokenA.symbol} + ${params.amountB} ${tokenB.symbol}`,
        `Slippage tolerance: ${(slippageBps / 100).toFixed(2)}%`,
        `Deadline: ${deadlineMinutes} minutes`,
        `Estimated gas: ${Number(gasCost) / 1_000_000_000} CSPR`,
      ].join('\n');
    
      return {
        transactionJson: JSON.stringify(transaction.toJSON()),
        summary,
        estimatedGasCost: `${Number(gasCost) / 1_000_000_000} CSPR`,
        approvalsRequired: approvals.length > 0 ? approvals : undefined,
        warnings: [],
      };
    }
Behavior3/5

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

Correctly discloses that the output is 'unsigned', indicating the tool constructs but does not execute or submit the transaction. However, with no annotations provided, the description carries the full burden and omits key behavioral context: it does not state that the transaction requires subsequent signing, potential token approval prerequisites, or that the operation is non-destructive/read-only until submission.

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?

Extremely concise single sentence with zero redundancy. However, given the domain complexity (DeFi liquidity operations with 9 parameters and no annotations), the brevity leaves critical workflow context unmentioned, slightly hurting its utility.

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

Completeness3/5

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

Factually complete for a transaction-building tool with full schema coverage, but minimal given the complexity. Lacks explanation of the broader transaction lifecycle, approval requirements, and relationship to sibling tools that would prevent user errors in a multi-step DeFi workflow.

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?

With 100% schema description coverage, all 9 parameters are adequately documented in the schema itself. The description adds no supplementary parameter semantics (e.g., explaining human-readable vs raw formats, or the relationship between token_a and token_b), warranting the baseline score of 3.

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?

Provides a specific verb (Build) and resource (unsigned add-liquidity transaction) with clear scope (for a token pair). Implicitly distinguishes from siblings like submit_transaction (unsigned vs submitted) and build_remove_liquidity (add vs remove), though it does not explicitly name alternative tools.

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?

Lacks explicit guidance on when to use this versus build_swap or the prerequisite workflow. Critical DeFi context missing: it does not mention that token approvals (via build_approve_token) are typically required before adding liquidity, nor does it explain the build → sign → submit lifecycle implied by 'unsigned'.

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/make-software/cspr-trade-mcp'

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