Client Package

@ckboost/client SDK

Overview

The @ckboost/client SDK provides a simple, type-safe way to integrate CKBoost acceleration services into your dApp. CKBoost reduces ckBTC conversion times from 1 hour (6 confirmations) to 7-10 minutes (1-2 confirmations) through a network of liquidity providers. We are planning to expend ckBoost and support other chain-key tokens (ckETH, in the future ckSOL, etc)

Key Features

  • 🚀 Fast Integration: Simple API with just two main functions

  • 📝 TypeScript First: Full type safety and excellent developer experience

  • Real-time Monitoring: Track boost request status changes

  • 🔒 Secure: Built on Internet Computer Protocol (ICP)

  • 🎯 dApp Ready: Designed specifically for frontend integration

Installation

npm install @ckboost/client
yarn add @ckboost/client
pnpm add @ckboost/client

Quick Start

import { ckTESTBTCClient, BoostStatus } from '@ckboost/client';

// Initialize client
const client = new ckTESTBTCClient({
  host: 'https://icp-api.io',
  timeout: 30000
});

// Create a boost request
const result = await client.generateDepositAddress({
  amount: '0.01',              // 0.01 ckTESTBTC
  maxFeePercentage: 1.5        // 1.5% maximum fee
});

if (result.success) {
  const { requestId, address, explorerUrl } = result.data;
  
  // Show deposit address to user
  console.log(`Send Bitcoin to: ${address}`);
  console.log(`Track at: ${explorerUrl}`);
  
  // Monitor status
  const statusResult = await client.getBoostRequest(requestId);
  if (statusResult.success) {
    console.log(`Status: ${statusResult.data.status}`);
  }
}

API Reference

Client Initialization

ckTESTBTCClient

Creates a new client instance for ckTESTBTC (Bitcoin testnet).

import { ckTESTBTCClient } from '@ckboost/client';

const client = new ckTESTBTCClient(config?: ClientConfig);

Parameters:

Parameter
Type
Default
Description

config

ClientConfig

{}

Optional client configuration

ClientConfig:

interface ClientConfig {
  host?: string;     // ICP host URL (default: 'https://icp-api.io')
  timeout?: number;  // Request timeout in ms (default: 30000)
}

Core Methods

generateDepositAddress()

Creates a new boost request and returns deposit information.

async generateDepositAddress(
  params: DepositAddressParams
): Promise<ApiResponse<DepositInfo>>

Parameters:

interface DepositAddressParams {
  amount: string;                    // Amount in ckTESTBTC (e.g., "0.01")
  maxFeePercentage: number;          // Maximum fee as percentage (e.g., 1.5)
  confirmationsRequired?: number;    // Override default confirmations
  preferredBooster?: string;         // Specific booster principal ID
}

Returns:

interface DepositInfo {
  requestId: string;           // Unique request identifier
  address: string;             // Bitcoin deposit address
  amount: string;              // Amount in ckTESTBTC
  amountRaw: string;           // Amount in satoshis
  maxFeePercentage: number;    // Maximum fee percentage
  explorerUrl: string;         // Block explorer URL
}

getBoostRequest()

Retrieves detailed information about a specific boost request.

async getBoostRequest(
  requestId: string
): Promise<ApiResponse<BoostRequest>>

Parameters:

Parameter
Type
Description

requestId

string

The unique request identifier

Returns:

interface BoostRequest {
  id: string;                    // Request ID
  status: BoostStatus;           // Current status
  amount: string;                // Requested amount in ckTESTBTC
  receivedAmount: string;        // Amount received so far
  maxFeePercentage: number;      // Maximum fee percentage
  confirmationsRequired: number;  // Required confirmations
  depositAddress?: string;       // Bitcoin deposit address
  booster?: string;             // Assigned booster principal
  createdAt: number;            // Creation timestamp (ms)
  updatedAt: number;            // Last update timestamp (ms)
}

getPendingBoostRequests()

Retrieves all pending boost requests.

async getPendingBoostRequests(): Promise<ApiResponse<BoostRequest[]>>

Utility Methods

getTokenConfig()

Returns the current token configuration.

getTokenConfig(): TokenConfig

Returns:

interface TokenConfig {
  token: SupportedToken;         // Token type
  minimumAmount: string;         // Minimum boost amount
  maximumAmount: string;         // Maximum boost amount
  standardFee: string;           // Standard fee amount
  confirmationsRequired: number; // Default confirmations
  decimals: number;             // Token decimals (8 for Bitcoin)
  isTestnet: boolean;           // Whether this is testnet
  blockExplorerUrl: string;     // Block explorer base URL
}

rawToTokenAmount()

Converts raw amount (satoshis) to token amount (ckTESTBTC).

rawToTokenAmount(rawAmount: string | bigint): string

tokenToRawAmount()

Converts token amount (ckTESTBTC) to raw amount (satoshis).

tokenToRawAmount(tokenAmount: string): string

Types Reference

Enums

BoostStatus

enum BoostStatus {
  PENDING = 'pending',       // Waiting for Bitcoin deposit
  ACTIVE = 'active',         // Processing the boost
  COMPLETED = 'completed',   // ckTESTBTC delivered
  CANCELLED = 'cancelled'    // Request cancelled
}

SupportedToken

enum SupportedToken {
  CK_TEST_BTC = 'ckTESTBTC'
}

CKBoostErrorType

enum CKBoostErrorType {
  INVALID_AMOUNT = 'INVALID_AMOUNT',
  NETWORK_ERROR = 'NETWORK_ERROR',
  REQUEST_NOT_FOUND = 'REQUEST_NOT_FOUND',
  CANISTER_ERROR = 'CANISTER_ERROR',
  VALIDATION_ERROR = 'VALIDATION_ERROR',
  UNKNOWN_ERROR = 'UNKNOWN_ERROR'
}

Response Types

ApiResponse<T>

All API methods return this discriminated union type:

type ApiResponse<T> = 
  | { success: true; data: T; }
  | { success: false; error: CKBoostError; };

interface CKBoostError {
  type: CKBoostErrorType;
  message: string;
  details?: any;
}

Examples

Basic Integration

import { ckTESTBTCClient, BoostStatus } from '@ckboost/client';

class BitcoinAccelerator {
  private client = new ckTESTBTCClient();

  async createBoost(amount: string, maxFee: number) {
    const result = await this.client.generateDepositAddress({
      amount,
      maxFeePercentage: maxFee
    });

    if (result.success) {
      return {
        success: true,
        depositAddress: result.data.address,
        requestId: result.data.requestId,
        explorerUrl: result.data.explorerUrl
      };
    } else {
      return {
        success: false,
        error: result.error.message
      };
    }
  }

  async checkStatus(requestId: string) {
    const result = await this.client.getBoostRequest(requestId);
    
    if (result.success) {
      const request = result.data;
      return {
        status: request.status,
        progress: {
          requested: request.amount,
          received: request.receivedAmount,
          percentage: (parseFloat(request.receivedAmount) / parseFloat(request.amount)) * 100
        },
        isComplete: request.status === BoostStatus.COMPLETED
      };
    }
    
    return { error: result.error.message };
  }
}

React Hook Integration

import { useState, useEffect } from 'react';
import { ckTESTBTCClient, BoostRequest, BoostStatus } from '@ckboost/client';

export function useBoostRequest(requestId?: string) {
  const [request, setRequest] = useState<BoostRequest | null>(null);
  const [loading, setLoading] = useState(false);
  const [error, setError] = useState<string | null>(null);

  const client = new ckTESTBTCClient();

  useEffect(() => {
    if (!requestId) return;

    const pollStatus = async () => {
      setLoading(true);
      setError(null);

      try {
        const result = await client.getBoostRequest(requestId);
        
        if (result.success) {
          setRequest(result.data);
        } else {
          setError(result.error.message);
        }
      } catch (err) {
        setError('Failed to fetch boost request');
      } finally {
        setLoading(false);
      }
    };

    pollStatus();
    
    // Poll every 10 seconds until complete
    const interval = setInterval(() => {
      if (request?.status !== BoostStatus.COMPLETED && 
          request?.status !== BoostStatus.CANCELLED) {
        pollStatus();
      }
    }, 10000);

    return () => clearInterval(interval);
  }, [requestId, request?.status]);

  return { request, loading, error };
}

Complete dApp Integration

import { ckTESTBTCClient, BoostStatus } from '@ckboost/client';

class CKBoostService {
  private client: ckTESTBTCClient;
  private activeRequests = new Map<string, NodeJS.Timeout>();

  constructor() {
    this.client = new ckTESTBTCClient({
      host: 'https://icp-api.io',
      timeout: 30000
    });
  }

  async createBoostRequest(amount: string, maxFeePercentage: number) {
    // Validate amount first
    const config = this.client.getTokenConfig();
    const amountNum = parseFloat(amount);
    const minAmount = parseFloat(config.minimumAmount);
    const maxAmount = parseFloat(config.maximumAmount);

    if (amountNum < minAmount || amountNum > maxAmount) {
      return {
        success: false,
        error: `Amount must be between ${minAmount} and ${maxAmount} ckTESTBTC`
      };
    }

    const result = await this.client.generateDepositAddress({
      amount,
      maxFeePercentage
    });

    if (result.success) {
      // Start monitoring this request
      this.startMonitoring(result.data.requestId);
      
      return {
        success: true,
        data: result.data
      };
    }

    return {
      success: false,
      error: result.error.message
    };
  }

  private startMonitoring(requestId: string, onUpdate?: (request: BoostRequest) => void) {
    // Clear existing timeout for this request
    if (this.activeRequests.has(requestId)) {
      clearTimeout(this.activeRequests.get(requestId)!);
    }

    const poll = async () => {
      const result = await this.client.getBoostRequest(requestId);
      
      if (result.success) {
        const request = result.data;
        
        // Notify about update
        if (onUpdate) {
          onUpdate(request);
        }

        // Continue polling if not in final state
        if (request.status !== BoostStatus.COMPLETED && 
            request.status !== BoostStatus.CANCELLED) {
          const timeout = setTimeout(poll, 10000); // Poll every 10 seconds
          this.activeRequests.set(requestId, timeout);
        } else {
          this.activeRequests.delete(requestId);
        }
      }
    };

    // Start immediate poll
    poll();
  }

  stopMonitoring(requestId: string) {
    if (this.activeRequests.has(requestId)) {
      clearTimeout(this.activeRequests.get(requestId)!);
      this.activeRequests.delete(requestId);
    }
  }

  destroy() {
    // Clean up all active monitoring
    this.activeRequests.forEach(timeout => clearTimeout(timeout));
    this.activeRequests.clear();
  }
}

Error Handling

Error Types

The SDK provides specific error types to help you handle different scenarios:

import { CKBoostErrorType } from '@ckboost/client';

async function handleBoostRequest() {
  const result = await client.generateDepositAddress({
    amount: '0.01',
    maxFeePercentage: 1.5
  });

  if (!result.success) {
    switch (result.error.type) {
      case CKBoostErrorType.INVALID_AMOUNT:
        // Show amount validation error to user
        alert('Please enter a valid amount between the minimum and maximum limits');
        break;
        
      case CKBoostErrorType.NETWORK_ERROR:
        // Show network error and retry option
        alert('Network error. Please check your connection and try again');
        break;
        
      case CKBoostErrorType.CANISTER_ERROR:
        // Backend service error
        alert('Service temporarily unavailable. Please try again later');
        break;
        
      default:
        // Generic error handling
        alert(`Error: ${result.error.message}`);
    }
  }
}

Best Practices

  1. Always check the success property before accessing data

  2. Provide user-friendly error messages based on error types

  3. Implement retry logic for network errors

  4. Validate amounts before making requests

  5. Monitor request status until completion

Configuration

Canister IDs

The SDK includes the required canister IDs:

import { ckTESTBTC_CANISTER_IDS } from '@ckboost/client';

console.log('Backend Canister:', ckTESTBTC_CANISTER_IDS.CKBOOST_BACKEND);
console.log('Ledger Canister:', ckTESTBTC_CANISTER_IDS.CKTESTBTC_LEDGER);

Network Configuration

For production applications, use the default configuration:

const client = new ckTESTBTCClient({
  host: 'https://icp-api.io',  // Production ICP host
  timeout: 30000               // 30 second timeout
});

For development, you might want to use a local replica:

const client = new ckTESTBTCClient({
  host: 'http://localhost:4943',  // Local dfx replica
  timeout: 10000                  // Shorter timeout for local testing
});

Monitoring and Real-time Updates

Polling Strategy

For production applications, implement efficient polling:

class BoostMonitor {
  private intervals = new Map<string, NodeJS.Timeout>();

  startMonitoring(requestId: string, callback: (request: BoostRequest) => void) {
    let attempts = 0;
    const maxAttempts = 360; // Monitor for 1 hour max (10s intervals)
    
    const poll = async () => {
      attempts++;
      
      const result = await client.getBoostRequest(requestId);
      if (result.success) {
        const request = result.data;
        callback(request);
        
        // Stop if complete or max attempts reached
        if (request.status === BoostStatus.COMPLETED || 
            request.status === BoostStatus.CANCELLED ||
            attempts >= maxAttempts) {
          this.stopMonitoring(requestId);
          return;
        }
      }
      
      // Continue polling with exponential backoff
      const delay = Math.min(10000 + (attempts * 1000), 30000);
      const timeout = setTimeout(poll, delay);
      this.intervals.set(requestId, timeout);
    };
    
    poll();
  }
  
  stopMonitoring(requestId: string) {
    const interval = this.intervals.get(requestId);
    if (interval) {
      clearTimeout(interval);
      this.intervals.delete(requestId);
    }
  }
}

WebSocket Alternative

For real-time updates, consider implementing WebSocket connections to the backend canister (when available) instead of polling.

Troubleshooting

Common Issues

Import Errors

// ❌ Don't do this
import CKBoostClient from '@ckboost/client';

// ✅ Do this
import { ckTESTBTCClient } from '@ckboost/client';

Type Errors

Make sure you have TypeScript configured properly:

// tsconfig.json
{
  "compilerOptions": {
    "moduleResolution": "node",
    "esModuleInterop": true,
    "allowSyntheticDefaultImports": true
  }
}

Network Errors

If you're getting network errors:

  1. Check your internet connection

  2. Verify the ICP host URL is correct

  3. Ensure the canister IDs are valid

  4. Try increasing the timeout value

Support

Last updated