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:
TWO_OF_TWO (Recommended for most use cases)
- 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:
Native SegWit (P2WPKH) - Recommended
- 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);
}
}
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
- Password Security: Use strong, unique passwords for each Bitcoin wallet
- Error Handling: Always handle potential errors during wallet creation with the
onError callback
- Monitoring: Log wallet creation events for audit purposes
- Backup Strategy: Implement secure backup strategies for key shares with
backUpToClientShareService: true
- Address Type Selection: Choose Native SegWit for most use cases; use Taproot for advanced privacy needs
Bitcoin-Specific Considerations
Address Type Selection
| Type | Enum Value | Best For |
|---|
| Native SegWit | BitcoinAddressType.NATIVE_SEGWIT | General use, lower fees |
| Taproot | BitcoinAddressType.TAPROOT | Privacy, 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: