> ## Documentation Index
> Fetch the complete documentation index at: https://docs.dynamic.xyz/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# BTC SDK Complete Example

> Complete example demonstrating all available BTC SDK functions with real API usage patterns

## Overview

This comprehensive example demonstrates all available functions in the Dynamic BTC SDK, showing the actual API usage patterns and how to handle common operations.

> [!NOTE]
>
> The SDK supports both **Bitcoin Mainnet** and **Testnet**. Use `BitcoinNetwork.MAINNET` for production and `BitcoinNetwork.TESTNET` for development and testing. The example below runs on testnet; change `NETWORK` to switch. See [Working with testnet](#working-with-testnet) for how addresses behave on each network.

## Complete Example Code

```typescript theme={"system"}
import { DynamicBtcWalletClient } from "@dynamic-labs-wallet/node-btc";
import { ThresholdSignatureScheme } from "@dynamic-labs-wallet/node";
import { BitcoinAddressType, BitcoinNetwork } from "@dynamic-labs-wallet/core";
import * as bitcoin from "bitcoinjs-lib";
import fs from "fs";

const NETWORK = BitcoinNetwork.TESTNET;

const bitcoinJsNetwork =
  NETWORK === BitcoinNetwork.TESTNET ? bitcoin.networks.testnet : bitcoin.networks.bitcoin;

const explorerApi =
  NETWORK === BitcoinNetwork.TESTNET
    ? "https://blockstream.info/testnet/api"
    : "https://blockstream.info/api";

const password = process.env.WALLET_PASSWORD!;

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

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

async function main() {
  try {
    const btcClient = await authenticatedBtcClient();
    console.log("BTC client authenticated successfully\n");

    // 1. Create a Native SegWit wallet
    const segwitWallet = await btcClient.createWalletAccount({
      thresholdSignatureScheme: ThresholdSignatureScheme.TWO_OF_TWO,
      password,
      onError: (error: Error) => {
        console.error("Native SegWit wallet creation error:", error.message);
      },
      backUpToDynamic: true,
      bitcoinConfig: {
        addressType: BitcoinAddressType.NATIVE_SEGWIT,
      },
    });
    const segwitMetadata = segwitWallet.walletMetadata;

    console.log("Native SegWit wallet created:", segwitMetadata.accountAddress);
    console.log("Public key hex:", segwitWallet.publicKeyHex);

    fs.writeFileSync(
      "btcWallet.json",
      JSON.stringify({ walletMetadata: segwitMetadata, publicKeyHex: segwitWallet.publicKeyHex }, null, 2),
    );

    // 2. Create a Taproot wallet
    const taprootWallet = await btcClient.createWalletAccount({
      thresholdSignatureScheme: ThresholdSignatureScheme.TWO_OF_TWO,
      password,
      onError: (error: Error) => {
        console.error("Taproot wallet creation error:", error.message);
      },
      backUpToDynamic: true,
      bitcoinConfig: {
        addressType: BitcoinAddressType.TAPROOT,
      },
    });
    const taprootMetadata = taprootWallet.walletMetadata;

    console.log("Taproot wallet created:", taprootMetadata.accountAddress);

    // 3. Derive the address for the network you are using
    const { accountAddress: segwitAddress } = btcClient.deriveAccountAddress({
      rawPublicKey: segwitWallet.rawPublicKey,
      addressType: BitcoinAddressType.NATIVE_SEGWIT,
      network: NETWORK,
    });
    console.log("Native SegWit address on this network:", segwitAddress);

    // 4. Sign a message (BIP-322)
    try {
      const signature = await btcClient.signMessage({
        message: "Hello, Bitcoin! This is a test message from Dynamic BTC wallet.",
        walletMetadata: segwitMetadata,
        network: NETWORK,
        password,
      });
      console.log("Message signed (BIP-322):", signature.substring(0, 50) + "...");
    } catch (error) {
      console.log("Message signing failed:", error instanceof Error ? error.message : String(error));
    }

    // 5. Create and sign a PSBT (example transaction)
    try {
      const psbt = new bitcoin.Psbt({ network: bitcoinJsNetwork });

      // Example input; use a real UTXO from your wallet's address in production
      psbt.addInput({
        hash: "11".repeat(32),
        index: 0,
        witnessUtxo: {
          script: bitcoin.address.toOutputScript(segwitAddress, bitcoinJsNetwork),
          value: 50_000n,
        },
      });
      psbt.addOutput({
        address: segwitAddress,
        value: 40_000n,
      });

      const signedPsbt = await btcClient.signTransaction({
        transaction: psbt.toBase64(),
        walletMetadata: segwitMetadata,
        network: NETWORK,
        password,
      });

      console.log("Signed PSBT (base64):", signedPsbt.substring(0, 50) + "...");
    } catch (error) {
      console.log("Transaction signing failed:", error instanceof Error ? error.message : String(error));
    }

    // 6. Export private key (WIF format)
    try {
      const wifPrivateKey = await btcClient.exportPrivateKey({
        walletMetadata: segwitMetadata,
        password,
      });
      console.log("Private key exported (WIF):", wifPrivateKey.substring(0, 4) + "...");
    } catch (error) {
      console.log("Private key export not available:", error instanceof Error ? error.message : String(error));
    }

    // 7. Check the balance of the address on this network
    try {
      const response = await fetch(`${explorerApi}/address/${segwitAddress}`);
      const data = await response.json();
      const balanceSats = data.chain_stats.funded_txo_sum - data.chain_stats.spent_txo_sum;
      console.log("Wallet balance:", balanceSats, "satoshis");
    } catch (error) {
      console.log("Balance check skipped");
    }

    // 8. Import a private key (set BTC_WIF_PRIVATE_KEY to try this)
    if (process.env.BTC_WIF_PRIVATE_KEY) {
      try {
        const importedWallet = await btcClient.importPrivateKey({
          privateKey: process.env.BTC_WIF_PRIVATE_KEY,
          chainName: "BTC",
          thresholdSignatureScheme: ThresholdSignatureScheme.TWO_OF_TWO,
          password,
          backUpToDynamic: true,
          bitcoinConfig: {
            addressType: BitcoinAddressType.NATIVE_SEGWIT,
          },
        });
        console.log("Private key imported:", importedWallet.walletMetadata.accountAddress);
      } catch (error) {
        console.log("Private key import failed:", error instanceof Error ? error.message : String(error));
      }
    }

    console.log("\nBTC Client Demo completed!");
    console.log(`   - Native SegWit wallet: ${segwitMetadata.accountAddress}`);
    console.log(`   - Native SegWit address on this network: ${segwitAddress}`);
    console.log(`   - Taproot wallet: ${taprootMetadata.accountAddress}`);
  } catch (error) {
    console.error("Error in BTC demo:", error instanceof Error ? error.message : String(error));
  }
}

main();
```

## Working with testnet

Wallet creation is the same on every network. What changes is how you pass the network and which address you use outside the SDK.

* **Pass `network` on every call.** `signMessage`, `signTransaction` and `deriveAccountAddress` all require `network` (`BitcoinNetwork.MAINNET` or `BitcoinNetwork.TESTNET`). Use the same value everywhere.
* **`createWalletAccount` returns the mainnet address.** `walletMetadata.accountAddress` is always the wallet's primary, mainnet-encoded address (`bc1q...` for Native SegWit, `bc1p...` for Taproot), whichever network you intend to use. Pass `walletMetadata` to the SDK as returned.
* **Derive the testnet address to receive funds.** Use `deriveAccountAddress` with `network: BitcoinNetwork.TESTNET` to get the `tb1q...` (or `tb1p...`) address for the same key. Fund that address from a testnet faucet, and use it when looking up UTXOs.
* **Build PSBTs with the matching bitcoinjs-lib network.** Use `bitcoin.networks.testnet` for testnet and `bitcoin.networks.bitcoin` for mainnet. Amounts are `bigint` in bitcoinjs-lib v7.

```typescript theme={"system"}
const { accountAddress: testnetAddress } = btcClient.deriveAccountAddress({
  rawPublicKey: wallet.rawPublicKey,
  addressType: BitcoinAddressType.NATIVE_SEGWIT,
  network: BitcoinNetwork.TESTNET,
});
// tb1q...
```

If `network` is omitted, or does not match the address you are signing for, you can see errors such as `Address verification failed: expected tb1q..., got bc1q...` from `signMessage`, or `No inputs found that belong to the account address` from `signTransaction`.

## Key API Patterns

### 1. Wallet Creation

```typescript theme={"system"}
const wallet = await btcClient.createWalletAccount({
  thresholdSignatureScheme: ThresholdSignatureScheme.TWO_OF_TWO,
  password: process.env.WALLET_PASSWORD!,
  onError: (error: Error) => {
    console.error("Wallet creation error:", error.message);
  },
  backUpToDynamic: true,
  bitcoinConfig: {
    addressType: BitcoinAddressType.NATIVE_SEGWIT,
  },
});

const { walletMetadata, rawPublicKey } = wallet;
```

> [!NOTE]
>
> A `password` is required when `backUpToDynamic` is `true`. With `backUpToDynamic: false` you hold the key shares yourself and pass them to later calls as `externalServerKeyShares`.

### 2. Message Signing (BIP-322)

```typescript theme={"system"}
const signature = await btcClient.signMessage({
  message: "Hello, Bitcoin!",
  walletMetadata,
  network: BitcoinNetwork.TESTNET,
  password: process.env.WALLET_PASSWORD!,
});
// Returns BIP-322 signature in base64 format
```

### 3. Transaction Signing (PSBT)

```typescript theme={"system"}
import * as bitcoin from "bitcoinjs-lib";

const network = bitcoin.networks.testnet;

const { accountAddress } = btcClient.deriveAccountAddress({
  rawPublicKey,
  addressType: BitcoinAddressType.NATIVE_SEGWIT,
  network: BitcoinNetwork.TESTNET,
});

const psbt = new bitcoin.Psbt({ network });

psbt.addInput({
  hash: "txid",
  index: 0,
  witnessUtxo: {
    script: bitcoin.address.toOutputScript(accountAddress, network),
    value: 50_000n,
  },
});

psbt.addOutput({
  address: "tb1q...recipient",
  value: 40_000n,
});

const signedPsbt = await btcClient.signTransaction({
  transaction: psbt.toBase64(),
  walletMetadata,
  network: BitcoinNetwork.TESTNET,
  password: process.env.WALLET_PASSWORD!,
});
// Returns signed PSBT in base64 format
```

### 4. Derive Address

```typescript theme={"system"}
// Synchronous - no await needed
const { accountAddress } = btcClient.deriveAccountAddress({
  rawPublicKey,
  addressType: BitcoinAddressType.NATIVE_SEGWIT,
  network: BitcoinNetwork.TESTNET,
});
```

### 5. Export Private Key

```typescript theme={"system"}
const wifPrivateKey = await btcClient.exportPrivateKey({
  walletMetadata,
  password: process.env.WALLET_PASSWORD!, // Only if wallet was created with password
});
// Returns private key in WIF format
```

> [!NOTE]
>
> `exportPrivateKey` returns the key in mainnet WIF format (starting with `K` or `L`), including for a wallet you use on testnet.

## Available Functions

The BTC SDK provides the following main functions:

* **`createWalletAccount()`**: Create new Bitcoin wallet
* **`importPrivateKey()`**: Import existing private key (WIF format)
* **`getBitcoinWallets()`**: Get all Bitcoin wallets
* **`deriveAccountAddress()`**: Derive Bitcoin address from a public key
* **`signMessage()`**: Sign messages using BIP-322
* **`signTransaction()`**: Sign PSBTs
* **`exportPrivateKey()`**: Export wallet private key in WIF format
* **`offlineExportPrivateKey()`**: Export private key from key shares offline

## Error Handling

Always implement proper error handling:

```typescript theme={"system"}
try {
  const result = await btcClient.someFunction(params);
  console.log("Operation successful:", result);
} catch (error) {
  console.error("Operation failed:", error instanceof Error ? error.message : String(error));
}
```

## Environment Setup

Make sure you have these environment variables:

```bash theme={"system"}
DYNAMIC_AUTH_TOKEN=your_auth_token_here
DYNAMIC_ENVIRONMENT_ID=your_environment_id_here
WALLET_PASSWORD=your_wallet_password_here
```

## Dependencies

Install required packages:

```bash theme={"system"}
bun add @dynamic-labs-wallet/node-btc @dynamic-labs-wallet/node @dynamic-labs-wallet/core bitcoinjs-lib
```

## Next Steps

* [Create Bitcoin wallets](https://docs.dynamic.xyz/docs/node/btc/create-wallet.md)
* [Sign transactions](https://docs.dynamic.xyz/docs/node/btc/sign-transactions.md)
* [Sign messages](https://docs.dynamic.xyz/docs/node/btc/sign-messages.md)
* [Import private keys](https://docs.dynamic.xyz/docs/node/btc/import-private-keys.md)


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.