Pact Swap
SWAPPact Swap Liquidity ProviderC1/C2 Orders API Guide

C1/C2 Orders API Guide

How to convert UI Commands into Coinweb L2 transactions and sign/broadcast them.


Prerequisites

  • Node.js (v18 or higher recommended)
  • Yarn package manager (v1.22.0 or higher)

1. Install dependencies

Add .yarnrc.yml file to project repository

checksumBehavior: update

nodeLinker: node-modules

npmScopes:
  coinweb:
    npmRegistryServer: 'https://npm.coinweb.io/'

Then run

yarn add @coinweb/wallet-lib bip39 hdkey secp256k1

2. Create coinweb-wallet.ts file

This class can be used to operate wallet functions of Coinweb.

import {
  create_wallet as createWallet,
  from_hex_string as fromHexString,
  compose_ui_commands as composeUiCommand,
  create_tx_monitor as createTxMonitor,
  add_txs as addTxs,
  sign,
  NetworkName,
  embed,
  L2TransactionDataInput,
  UiCommand,
  TransactionMonitor,
  type Transaction,
} from '@coinweb/wallet-lib';
import { mnemonicToSeedSync } from 'bip39';
import HDKey from 'hdkey';
import secp256k1 from 'secp256k1';

/**
 * CoinWeb Wallet class
 * Manages wallet instance, transaction monitor, and UI command execution
 */
export class CoinWebWallet {
  public readonly wallet: Awaited<ReturnType<typeof createWallet>>;
  public readonly txMonitor: TransactionMonitor;
  public readonly pubKey: string;

  private constructor(
    wallet: Awaited<ReturnType<typeof createWallet>>,
    txMonitor: TransactionMonitor
  ) {
    this.wallet = wallet;
    this.txMonitor = txMonitor;
    this.pubKey = wallet.pub_key;
  }

  /**
   * Creates a CoinWeb wallet instance from a mnemonic phrase
   * Factory method to create and initialize a wallet with transaction monitor
   *
   * @param params - Wallet configuration parameters
   * @returns CoinWebWallet instance ready for L2 operations
   */
  static async create(params: {
    mnemonic: string;
    backendUrl: string;
    shard: NetworkName;
  }): Promise<CoinWebWallet> {
    // Generate HD key from mnemonic
    const hdkey = CoinWebWallet.mnemonicToHDKey(params.mnemonic);

    // Create wallet with backend connection and signing configuration
    const wallet = await createWallet({
      address: params.backendUrl, // HTTP endpoint for wallet operations
      ws_address: `${params.backendUrl.replace(/^https:/, 'wss:')}`, // WebSocket endpoint (convert https to wss)
      pub_key: hdkey.publicKey?.toString('hex') ?? '', // Public key in hex format
      shard: params.shard, // Network shard
      max_retry_time_secs: null, // No retry timeout limit
      enable_retries: null, // Retry behavior (null = default)
      // Callback function for signing messages - converts private key and signs
      sign_callback: (msg) => {
        return sign(
          fromHexString(hdkey.privateKey?.toString('hex') ?? ''),
          msg
        );
      },
      broadcaster_wallet_address: null, // No custom broadcaster address
    });

    // Create transaction monitor
    const pendingTxs: Transaction[] = [];
    const utxos = new Map<string, unknown[]>();
    const txMonitor = createTxMonitor(pendingTxs, utxos);

    return new CoinWebWallet(wallet, txMonitor);
  }

  /**
   * Converts a mnemonic phrase to an HD (Hierarchical Deterministic) key
   * This function generates a master key from the mnemonic seed and sets up
   * a custom signing function that uses secp256k1 elliptic curve cryptography
   *
   * @param mnemonic - BIP39 mnemonic phrase (12 or 24 words)
   * @returns HDKey instance with custom signing capability for CoinWeb wallet
   */
  private static mnemonicToHDKey(mnemonic: string) {
    // Convert mnemonic to seed using BIP39 standard
    const seed = mnemonicToSeedSync(mnemonic);
    // Generate HD key from master seed
    const hdkey = HDKey.fromMasterSeed(seed);

    // Override the sign method to use secp256k1 signing algorithm
    // This is required for CoinWeb wallet signature format
    hdkey.sign = function (hash: Buffer<ArrayBufferLike>) {
      // Sign the hash with the private key using secp256k1 ECDSA
      const sig = secp256k1.ecdsaSign(
        Uint8Array.from(hash),
        Uint8Array.from(hdkey.privateKey ?? [])
      );

      // Normalize the signature to ensure consistent format
      const array = secp256k1.signatureNormalize(sig.signature);

      // Export signature as Buffer for CoinWeb wallet library
      return Buffer.from(secp256k1.signatureExport(array));
    };

    return hdkey;
  }

  /**
   * Executes a UI command by composing it into an L2 transaction and embedding it
   * This is a convenience method that combines compose and embed operations
   *
   * @param jsonTokenCommand - UI command in JSON format (deposit, orders, etc.)
   * @param networkWrite - Optional network name for write operations (null = default)
   * @returns Transaction UUID (hash) for tracking the transaction
   */
  async executeUiCommand(
    jsonTokenCommand: UiCommand,
    networkWrite: NetworkName | null = null
  ): Promise<string> {
    if (!jsonTokenCommand) throw new Error('UI command not found');

    // Compose the UI command into an L2 transaction structure
    const l2TransactionData = await composeUiCommand(
      this.wallet,
      [jsonTokenCommand],
      networkWrite
    );

    // Add the transaction to the monitor for tracking and state management
    await addTxs(this.txMonitor, l2TransactionData?.l2_transaction);

    // Submit transaction to L2 network and get transaction UUID
    const uuid = await embed(this.wallet, l2TransactionData.l2_transaction);

    return uuid;
  }

  /**
   * Composes and adds a token command to the transaction monitor
   * This function takes a UI command (like deposit or create orders), composes it into
   * an L2 transaction, and adds it to the transaction monitor for tracking
   *
   * @param jsonTokenCommand - UI command in JSON format (deposit, orders, etc.)
   * @param networkWrite - Optional network name for write operations (null = default)
   * @returns Object containing L2 transaction data and newly added transactions
   */
  async composeTokenCommand(
    jsonTokenCommand: UiCommand,
    networkWrite: NetworkName | null = null
  ) {
    if (!jsonTokenCommand) throw new Error('UI command not found');

    // Compose the UI command into an L2 transaction structure
    const l2TransactionData = await composeUiCommand(
      this.wallet,
      [jsonTokenCommand],
      networkWrite
    );
    // Add the transaction to the monitor for tracking and state management
    const newTxs = await addTxs(
      this.txMonitor,
      l2TransactionData?.l2_transaction
    );

    return { l2TransactionData, newTxs };
  }

  /**
   * Embeds an L2 transaction into the blockchain
   * This submits the transaction to the CoinWeb L2 network and returns a transaction UUID
   *
   * @param l2Transaction - L2 transaction data to embed
   * @returns Transaction UUID (hash) for tracking the transaction
   */
  async embedTransaction(
    l2Transaction: L2TransactionDataInput
  ): Promise<string> {
    if (!l2Transaction) throw new Error('L2 transaction not found');

    // Submit transaction to L2 network and get transaction UUID
    const uuid = await embed(this.wallet, l2Transaction);

    return uuid;
  }
}

3. Example how to propagate the /c2/makeDeposit UI Command

Processing the "Deposit CWEB collateral into C2(Ask) token contract" UI Command.

// Step 1: Import dependencies
import { CoinWebWallet } from './coinweb-wallet.ts';
import { NetworkName } from '@coinweb/claims-client';

// Step 2: Create wallet from mnemonic
const coinWebWallet = await CoinWebWallet.create({
  mnemonic: 'Coinweb Wallet mnemonic',
  backendUrl: 'https://api-cloud.coinweb.io/wallet', // Coinweb Backend wallet url'
  shard: NetworkName.BNB, // Shard to operate on Coinweb Chain
});
console.log('✅ Wallet created');
console.log('   Public Key:', coinWebWallet.pubKey);

// Step 3: Call Pact Swap REST API to build the UI Command for the "/c2/makeDeposit"
const depositAmount = 100 * 10 ** 18; // 100 CWEB

const data = await axios.post<{ rawTx: string }>(
  `https://api.pactswap.io/pactswap_cm/ui-commands/c2/makeDeposit`,
  {
    contractId: 'C2 contract id',
    depositAmount: depositAmount,
  }
);

// The UI Command
const rawTx = data.data.rawTx;
console.log('   📝 Composed deposit collateral command');

// Step 4: Propagate(Compose/Sign/Broadcast) the UI Command via the Coinweb wallet library
const hashTxDeposit = await this.coinWebWallet.executeUiCommand(
  JSON.parse(rawTx)
);
console.log('   ✅ Deposit transaction hash:', hashTxDeposit);

Was this documentation helpful? Any suggestions?