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: [],
      };
    }

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