Skip to main content

Overview

This guide shows you how to sign messages with your Bitcoin wallet. Message signing uses the BIP-322 standard, which provides a unified way to sign messages across all Bitcoin address types. The signature is returned as a base64 string.

Prerequisites

Basic Message Signing

import { authenticatedBtcClient } from './client';
import { BitcoinNetwork } from '@dynamic-labs-wallet/core';

const btcClient = await authenticatedBtcClient();

const signature = await btcClient.signMessage({
  message: 'Hello, Bitcoin!',
  accountAddress: 'bc1q...your-wallet-address',
  network: BitcoinNetwork.MAINNET,
});

console.log('Message signed (base64):', signature);

Simple Message Signing

Whether you need to provide externalServerKeyShares depends on how you created your wallet: If you created your wallet with backUpToClientShareService: true, you don’t need to provide external key shares:
import { BitcoinNetwork } from '@dynamic-labs-wallet/core';

// Simple signing - no externalServerKeyShares needed
const signature = await btcClient.signMessage({
  message: 'Hello, Bitcoin!',
  accountAddress: 'bc1q...your-wallet-address',
  network: BitcoinNetwork.MAINNET,
});

console.log('Message signed (base64):', signature);

With Manual Backup

If you created your wallet with backUpToClientShareService: false, you must provide external key shares:
// First, retrieve your stored key shares
const keyShares = await retrieveStoredKeyShares('bc1q...your-wallet-address');

// Must provide externalServerKeyShares
const signature = await btcClient.signMessage({
  message: 'Hello, Bitcoin!',
  accountAddress: 'bc1q...your-wallet-address',
  network: BitcoinNetwork.MAINNET,
  externalServerKeyShares: keyShares, // Required for manual backup!
  password: 'your-password', // Only if wallet was created with password
});

console.log('Message signed (base64):', signature);
Password Handling Notes:
  • If your wallet was created without a password, omit the password parameter
  • If your wallet was created with a password, you must provide it for all operations
  • The password parameter is always optional in the API, but required if the wallet is password-protected

BIP-322 Message Signing

The SDK automatically uses BIP-322 for message signing, which works with both Native SegWit and Taproot addresses:

Native SegWit (P2WPKH)

// Native SegWit address (bc1q...)
const signature = await btcClient.signMessage({
  message: 'Verify my ownership',
  accountAddress: 'bc1qxy2kgdygjrsqtzq2n0yrf2493p83kkfjhx0wlh',
  network: BitcoinNetwork.MAINNET,
});
// Returns BIP-322 signature in base64 format

Taproot (P2TR)

// Taproot address (bc1p...)
const signature = await btcClient.signMessage({
  message: 'Verify my ownership',
  accountAddress: 'bc1p5d7rjq7g6rdk2yhzks9smlaqtedr4dekq08ge8ztwac72sfr9rusxg3297',
  network: BitcoinNetwork.MAINNET,
});
// Returns BIP-322 signature with Schnorr (BIP-340) in base64 format

Common Use Cases

Authentication

const nonce = Date.now().toString();
const message = `Sign this message to authenticate: ${nonce}`;
const signature = await btcClient.signMessage({
  message,
  accountAddress: 'bc1q...your-wallet-address',
  network: BitcoinNetwork.MAINNET,
});
// signature is a BIP-322 encoded signature in base64

Data Integrity

const data = { userId: 123, action: 'transfer', amount: '100000' };
const message = JSON.stringify(data);
const signature = await btcClient.signMessage({
  message,
  accountAddress: 'bc1q...your-wallet-address',
  network: BitcoinNetwork.MAINNET,
});

Proof of Ownership

const proofMessage = `I own this Bitcoin address: ${walletAddress}`;
const signature = await btcClient.signMessage({
  message: proofMessage,
  accountAddress: walletAddress,
  network: BitcoinNetwork.MAINNET,
});

Understanding BIP-322

BIP-322 (Generic Signed Message Format) provides a standardized way to sign and verify messages for all Bitcoin address types:
  • Native SegWit (P2WPKH): Uses ECDSA signatures
  • Taproot (P2TR): Uses Schnorr signatures (BIP-340)
The signature format encodes the signed message in a way that can be verified by any BIP-322 compatible verifier.

Error Handling

try {
  const signature = await btcClient.signMessage({
    message: 'Test message',
    accountAddress: walletAddress,
    network: BitcoinNetwork.MAINNET,
  });
  console.log('Signature:', signature);
} catch (error) {
  console.error('Message signing failed:', error.message);
}

Next Steps