Pact Swap
SWAPPACTSWAP Market MakerC1/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 Pactswap 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://cwap-api.coinhq.store/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?