@arkade-os/sdk Documentation - v0.3.2
    Preparing search index...

    @arkade-os/sdk Documentation - v0.3.2

    Arkade TypeScript SDK

    The Arkade SDK is a TypeScript library for building Bitcoin wallets with support for both on-chain and off-chain transactions via the Ark protocol.

    TypeScript Documentation Ask DeepWiki

    npm install @arkade-os/sdk
    
    import { SingleKey, Wallet } from '@arkade-os/sdk'

    // Create a new in-memory key (or use an external signer)
    const identity = SingleKey.fromHex('your_private_key_hex')

    // Create a wallet with Ark support
    const wallet = await Wallet.create({
    identity,
    // Esplora API, can be left empty - mempool.space API will be used
    esploraUrl: 'https://mutinynet.com/api',
    arkServerUrl: 'https://mutinynet.arkade.sh',
    // Optional: specify storage adapter (defaults to InMemoryStorageAdapter)
    // storage: new LocalStorageAdapter() // for browser persistence
    })
    import { waitForIncomingFunds } from '@arkade-os/sdk'

    // Get wallet addresses
    const arkAddress = await wallet.getAddress()
    const boardingAddress = await wallet.getBoardingAddress()
    console.log('Ark Address:', arkAddress)
    console.log('Boarding Address:', boardingAddress)

    const incomingFunds = await waitForIncomingFunds(wallet)
    if (incomingFunds.type === "vtxo") {
    // Virtual coins received
    console.log("VTXOs: ", incomingFunds.vtxos)
    } else if (incomingFunds.type === "utxo") {
    // Boarding coins received
    console.log("UTXOs: ", incomingFunds.coins)
    }

    Onboarding allows you to swap on-chain funds into VTXOs:

    import { Ramps } from '@arkade-os/sdk'

    const onboardTxid = await new Ramps(wallet).onboard();
    // Get detailed balance information
    const balance = await wallet.getBalance()
    console.log('Total Balance:', balance.total)
    console.log('Boarding Total:', balance.boarding.total)
    console.log('Offchain Available:', balance.available)
    console.log('Offchain Settled:', balance.settled)
    console.log('Offchain Preconfirmed:', balance.preconfirmed)
    console.log('Recoverable:', balance.recoverable)

    // Get virtual UTXOs (off-chain)
    const virtualCoins = await wallet.getVtxos()

    // Get boarding UTXOs
    const boardingUtxos = await wallet.getBoardingUtxos()
    // Send bitcoin via Ark
    const txid = await wallet.sendBitcoin({
    address: 'tb1qw508d6qejxtdg4y5r3zarvary0c5xw7kxpjzsx',
    amount: 50000, // in satoshis
    feeRate: 1 // optional, in sats/vbyte
    })

    This can be used to move preconfirmed balances into finalized balances and to manually convert UTXOs and VTXOs.

    // For settling transactions
    const settleTxid = await wallet.settle({
    inputs, // from getVtxos() or getBoardingUtxos()
    outputs: [{
    address: destinationAddress,
    amount: BigInt(amount)
    }]
    })

    VTXOs have an expiration time (batch expiry). The SDK provides the VtxoManager class to handle both:

    • Renewal: Renew VTXOs before they expire to maintain unilateral control of the funds.
    • Recovery: Reclaim swept or expired VTXOs back to the wallet in case renewal window was missed.
    import { VtxoManager } from '@arkade-os/sdk'

    // Create manager with optional renewal configuration
    const manager = new VtxoManager(wallet, {
    enabled: true, // Enable expiration monitoring
    thresholdPercentage: 10 // Alert when 10% of lifetime remains (default)
    })

    Renew VTXOs before they expire to retain unilateral control of funds. This settles expiring and recoverable VTXOs back to your wallet, refreshing their expiration time.

    // Renew all VTXOs to prevent expiration
    const txid = await manager.renewVtxos()
    console.log('Renewed:', txid)

    // Check which VTXOs are expiring soon
    const expiringVtxos = await manager.getExpiringVtxos()
    // Override threshold percentage (e.g., renew when 5% of time remains)
    const urgentlyExpiring = await manager.getExpiringVtxos(5)

    Recover VTXOs that have been swept by the server or consolidate small amounts (subdust).

    // Recover swept VTXOs and preconfirmed subdust
    const txid = await manager.recoverVtxos((event) => {
    console.log('Settlement event:', event.type)
    })
    console.log('Recovered:', txid)
    // Check what's recoverable
    const balance = await manager.getRecoverableBalance()
    // Get transaction history
    const history = await wallet.getTransactionHistory()

    Collaborative exit or "offboarding" allows you to withdraw your virtual funds to an on-chain address:

    import { Ramps } from '@arkade-os/sdk'

    const exitTxid = await new Ramps(wallet).offboard(onchainAddress);

    Unilateral exit allows you to withdraw your funds from the Ark protocol back to the Bitcoin blockchain without requiring cooperation from the Ark server. This process involves two main steps:

    1. Unrolling: Broadcasting the transaction chain from off-chain back to on-chain
    2. Completing the exit: Spending the unrolled VTXOs after the timelock expires
    import { Unroll, OnchainWallet, SingleKey } from '@arkade-os/sdk'

    // Create an identity for the onchain wallet
    const onchainIdentity = SingleKey.fromHex('your_onchain_private_key_hex');

    // Create an onchain wallet to pay for P2A outputs in VTXO branches
    // OnchainWallet implements the AnchorBumper interface
    const onchainWallet = await OnchainWallet.create(onchainIdentity, 'regtest');

    // Unroll a specific VTXO
    const vtxo = { txid: 'your_vtxo_txid', vout: 0 };
    const session = await Unroll.Session.create(
    vtxo,
    onchainWallet,
    onchainWallet.provider,
    wallet.indexerProvider
    );

    // Iterate through the unrolling steps
    for await (const step of session) {
    switch (step.type) {
    case Unroll.StepType.WAIT:
    console.log(`Waiting for transaction ${step.txid} to be confirmed`);
    break;
    case Unroll.StepType.UNROLL:
    console.log(`Broadcasting transaction ${step.tx.id}`);
    break;
    case Unroll.StepType.DONE:
    console.log(`Unrolling complete for VTXO ${step.vtxoTxid}`);
    break;
    }
    }

    The unrolling process works by:

    • Traversing the transaction chain from the root (most recent) to the leaf (oldest)
    • Broadcasting each transaction that isn't already on-chain
    • Waiting for confirmations between steps
    • Using P2A (Pay-to-Anchor) transactions to pay for fees

    Once VTXOs are fully unrolled and the unilateral exit timelock has expired, you can complete the exit:

    // Complete the exit for specific VTXOs
    await Unroll.completeUnroll(
    wallet,
    [vtxo.txid], // Array of VTXO transaction IDs to complete
    onchainWallet.address // Address to receive the exit amount
    );

    Important Notes:

    • Each VTXO may require multiple unroll steps depending on the transaction chain length
    • Each unroll step must be confirmed before proceeding to the next
    • The completeUnroll method can only be called after VTXOs are fully unrolled and the timelock has expired
    • You need sufficient on-chain funds in the OnchainWallet to pay for P2A transaction fees

    Ultra-simplified setup! We handle all the complex service worker registration and identity management for you:

    // SIMPLE SETUP with identity! 🎉
    import { ServiceWorkerWallet, SingleKey } from '@arkade-os/sdk';

    // Create your identity
    const identity = SingleKey.fromHex('your_private_key_hex');
    // Or generate a new one:
    // const identity = SingleKey.fromRandomBytes();

    const wallet = await ServiceWorkerWallet.setup({
    serviceWorkerPath: '/service-worker.js',
    arkServerUrl: 'https://mutinynet.arkade.sh',
    identity
    });

    // That's it! Ready to use immediately:
    const address = await wallet.getAddress();
    const balance = await wallet.getBalance();

    You'll also need to create a service worker file:

    // service-worker.js
    import { Worker } from '@arkade-os/sdk'

    // Worker handles communication between the main thread and service worker
    new Worker().start()

    Choose the appropriate storage adapter for your environment:

    import { 
    SingleKey,
    Wallet,
    InMemoryStorageAdapter, // Works everywhere, data lost on restart
    } from '@arkade-os/sdk'

    // Import additional storage adapters as needed:
    import { LocalStorageAdapter } from '@arkade-os/sdk/adapters/localStorage' // Browser/PWA persistent storage
    import { IndexedDBStorageAdapter } from '@arkade-os/sdk/adapters/indexedDB' // Browser/PWA/Service Worker advanced storage
    import { AsyncStorageAdapter } from '@arkade-os/sdk/adapters/asyncStorage' // React Native persistent storage
    import { FileSystemStorageAdapter } from '@arkade-os/sdk/adapters/fileSystem' // Node.js file-based storage

    // Node.js
    const storage = new FileSystemStorageAdapter('./wallet-data')

    // Browser/PWA
    const storage = new LocalStorageAdapter()
    // or for advanced features:
    const storage = new IndexedDBStorageAdapter('my-app', 1)

    // React Native
    const storage = new AsyncStorageAdapter()

    // Service Worker
    const storage = new IndexedDBStorageAdapter('service-worker-wallet', 1)

    // Load identity from storage (simple pattern everywhere)
    const privateKeyHex = await storage.getItem('private-key')
    const identity = SingleKey.fromHex(privateKeyHex)

    // Create wallet (same API everywhere)
    const wallet = await Wallet.create({
    identity,
    arkServerUrl: 'https://mutinynet.arkade.sh',
    storage // optional
    })

    For React Native and Expo applications where standard EventSource and fetch streaming may not work properly, use the Expo-compatible providers:

    import { Wallet, SingleKey } from '@arkade-os/sdk'
    import { ExpoArkProvider, ExpoIndexerProvider } from '@arkade-os/sdk/adapters/expo'

    const identity = SingleKey.fromHex('your_private_key_hex')

    const wallet = await Wallet.create({
    identity: identity,
    esploraUrl: 'https://mutinynet.com/api',
    arkProvider: new ExpoArkProvider('https://mutinynet.arkade.sh'), // For settlement events and transactions streaming
    indexerProvider: new ExpoIndexerProvider('https://mutinynet.arkade.sh'), // For address subscriptions and VTXO updates
    })

    // use expo/fetch for streaming support (SSE)
    // All other wallet functionality remains the same
    const balance = await wallet.getBalance()
    const address = await wallet.getAddress()

    Both ExpoArkProvider and ExpoIndexerProvider are available as adapters following the SDK's modular architecture pattern. This keeps the main SDK bundle clean while providing opt-in functionality for specific environments:

    • ExpoArkProvider: Handles settlement events and transaction streaming using expo/fetch for Server-Sent Events
    • ExpoIndexerProvider: Handles address subscriptions and VTXO updates using expo/fetch for JSON streaming

    Install expo-crypto and polyfill crypto.getRandomValues() at the top of your app entry point:

    npx expo install expo-crypto
    
    // App.tsx or index.js - MUST be first import
    import * as Crypto from 'expo-crypto';
    if (!global.crypto) global.crypto = {} as any;
    global.crypto.getRandomValues = Crypto.getRandomValues;

    // Now import the SDK
    import { Wallet, SingleKey } from '@arkade-os/sdk';
    import { ExpoArkProvider, ExpoIndexerProvider } from '@arkade-os/sdk/adapters/expo';

    This is required for MuSig2 settlements and cryptographic operations.

    Access low-level data management through repositories:

    // VTXO management (automatically cached for performance)
    const addr = await wallet.getAddress()
    const vtxos = await wallet.walletRepository.getVtxos(addr)
    await wallet.walletRepository.saveVtxos(addr, vtxos)

    // Contract data for SDK integrations
    await wallet.contractRepository.setContractData('my-contract', 'status', 'active')
    const status = await wallet.contractRepository.getContractData('my-contract', 'status')

    // Collection management for related data
    await wallet.contractRepository.saveToContractCollection(
    'swaps',
    { id: 'swap-1', amount: 50000, type: 'reverse' },
    'id' // key field
    )
    const swaps = await wallet.contractRepository.getContractCollection('swaps')

    For complete API documentation, visit our TypeScript documentation.

    • pnpm - Package manager
    • nigiri - For running integration tests with a local Bitcoin regtest network
    1. Install dependencies:

      pnpm install
      pnpm format
      pnpm lint
    2. Install nigiri for integration tests:

      curl https://getnigiri.vulpem.com | bash
      
    # Run all tests
    pnpm test

    # Run unit tests only
    pnpm test:unit

    # Run integration tests with ark provided by nigiri
    nigiri start --ark
    pnpm test:setup # Run setup script for integration tests
    pnpm test:integration
    nigiri stop --delete

    # Run integration tests with ark provided by docker (requires nigiri)
    nigiri start
    pnpm test:up-docker
    pnpm test:setup-docker # Run setup script for integration tests
    pnpm test:integration-docker
    pnpm test:down-docker
    nigiri stop --delete

    # Watch mode for development
    pnpm test:watch

    # Run tests with coverage
    pnpm test:coverage
    # Build the TypeScript documentation
    pnpm docs:build
    # Open the docs in the browser
    pnpm docs:open
    # Release new version (will prompt for version patch, minor, major)
    pnpm release

    # You can test release process without making changes
    pnpm release:dry-run

    # Cleanup: checkout version commit and remove release branch
    pnpm release:cleanup

    MIT