Skip to main content

Overview

This guide walks you through creating Bitcoin wallets using Dynamic’s Node SDK. You’ll learn how to set up different threshold signature schemes, choose address types, and understand the security implications for Bitcoin blockchain operations.

Prerequisites

Before you begin, make sure you have:

Step 1: Choose Your Security Model

Dynamic supports two threshold signature schemes for Bitcoin wallets:
  • Security: Highest - requires both your server and Dynamic’s infrastructure
  • Availability: Lower - both parties must be online
  • Use case: High-value transactions, maximum security

TWO_OF_THREE

  • Security: High - requires 2 out of 3 shares
  • Availability: Medium - can tolerate one party being offline
  • Use case: Balanced security and availability

Step 2: Choose Your Address Type

Bitcoin supports multiple address types:
  • Prefix: bc1q...
  • Algorithm: ECDSA
  • Benefits: Lower transaction fees, widely supported
  • Use case: General purpose, most applications

Taproot (P2TR)

  • Prefix: bc1p...
  • Algorithm: Schnorr (BIP-340)
  • Benefits: Enhanced privacy, smart contract capabilities
  • Use case: Advanced applications, privacy-focused

Step 3: Create Your First Bitcoin Wallet

Here’s a complete example of creating a Bitcoin wallet:
import { DynamicBtcWalletClient } from '@dynamic-labs-wallet/node-btc';
import { ThresholdSignatureScheme } from '@dynamic-labs-wallet/node';
import { BitcoinAddressType } from '@dynamic-labs-wallet/core';

// Create authenticated client
export const authenticatedBtcClient = async () => {
  const client = new DynamicBtcWalletClient({
    environmentId: process.env.DYNAMIC_ENVIRONMENT_ID!,
  });

  await client.authenticateApiToken(process.env.DYNAMIC_AUTH_TOKEN!);
  return client;
};

export const createBtcWallet = async ({
  thresholdSignatureScheme = ThresholdSignatureScheme.TWO_OF_TWO,
  addressType = BitcoinAddressType.NATIVE_SEGWIT,
  password,
  onError,
}: {
  thresholdSignatureScheme?: ThresholdSignatureScheme;
  addressType?: BitcoinAddressType;
  password?: string;
  onError?: (error: Error) => void;
}) => {
  const btcClient = await authenticatedBtcClient();

  const wallet = await btcClient.createWalletAccount({
    thresholdSignatureScheme,
    password,
    onError,
    backUpToClientShareService: true,
    bitcoinConfig: {
      addressType,
    },
  });

  return {
    accountAddress: wallet.accountAddress,
    publicKeyHex: wallet.publicKeyHex,
    rawPublicKey: wallet.rawPublicKey,
    externalServerKeyShares: wallet.externalServerKeyShares,
  };
};

// Usage example - Native SegWit wallet
const segwitWallet = await createBtcWallet({
  thresholdSignatureScheme: ThresholdSignatureScheme.TWO_OF_TWO,
  addressType: BitcoinAddressType.NATIVE_SEGWIT,
  password: 'your-secure-password',
  onError: (error: Error) => {
    console.error('BTC wallet creation error:', error);
  },
});

console.log('Native SegWit wallet created:', segwitWallet.accountAddress);
// Output: bc1q...

// Usage example - Taproot wallet
const taprootWallet = await createBtcWallet({
  addressType: BitcoinAddressType.TAPROOT,
});

console.log('Taproot wallet created:', taprootWallet.accountAddress);
// Output: bc1p...

Step 4: Handle Errors Gracefully

Always implement proper error handling for Bitcoin wallet creation:
try {
  const wallet = await createBtcWallet({
    thresholdSignatureScheme: ThresholdSignatureScheme.TWO_OF_TWO,
    addressType: BitcoinAddressType.NATIVE_SEGWIT,
    onError: (error: Error) => {
      console.error('Wallet creation error:', error);
    },
  });
  console.log('BTC wallet created successfully:', wallet.accountAddress);
} catch (error) {
  if (error.message.includes('invalid session')) {
    console.error('Invalid session ID - please re-authenticate');
  } else {
    console.error('BTC wallet creation failed:', error.message);
  }
}

Step 5: Store Wallet Information Securely

After creating a Bitcoin wallet, you’ll receive important information that should be stored securely:
const wallet = await btcClient.createWalletAccount({
  thresholdSignatureScheme: ThresholdSignatureScheme.TWO_OF_TWO,
  backUpToClientShareService: true,
  bitcoinConfig: {
    addressType: BitcoinAddressType.NATIVE_SEGWIT,
  },
});

// Store these securely in your database
const walletData = {
  accountAddress: wallet.accountAddress,
  publicKeyHex: wallet.publicKeyHex,
  thresholdScheme: ThresholdSignatureScheme.TWO_OF_TWO,
  addressType: BitcoinAddressType.NATIVE_SEGWIT,
  chainName: 'BTC',
  createdAt: new Date().toISOString(),
  externalServerKeyShares: wallet.externalServerKeyShares,
};

// Never store externalServerKeyShares in plain text
// They should be encrypted and stored securely

Step 6: Derive the Bitcoin Address

You can also derive the Bitcoin address from a public key using deriveAccountAddress():
const { accountAddress } = btcClient.deriveAccountAddress({
  rawPublicKey: wallet.rawPublicKey,
  addressType: BitcoinAddressType.NATIVE_SEGWIT,
});

console.log('Derived BTC address:', accountAddress); // e.g. bc1q...

Best Practices

  1. Password Security: Use strong, unique passwords for each Bitcoin wallet
  2. Error Handling: Always handle potential errors during wallet creation with the onError callback
  3. Monitoring: Log wallet creation events for audit purposes
  4. Backup Strategy: Implement secure backup strategies for key shares with backUpToClientShareService: true
  5. Address Type Selection: Choose Native SegWit for most use cases; use Taproot for advanced privacy needs

Bitcoin-Specific Considerations

Address Type Selection

TypeEnum ValueBest For
Native SegWitBitcoinAddressType.NATIVE_SEGWITGeneral use, lower fees
TaprootBitcoinAddressType.TAPROOTPrivacy, smart contracts

Network Support

The SDK currently supports Bitcoin Mainnet only. Testnet support is planned for a future release.

Satoshis

Bitcoin uses satoshis as the smallest unit: 1 BTC = 100,000,000 satoshis.

Next Steps

Now that you’ve created a Bitcoin wallet, you can: