Overview
This guide shows you how to sign Bitcoin transactions using Dynamic’s Node SDK. Bitcoin uses PSBTs (Partially Signed Bitcoin Transactions) for signing. You create a PSBT, pass it tosignTransaction() as a base64 string, and receive the signed PSBT back.
Prerequisites
Step 1: Install Dependencies
These examples use
bitcoinjs-lib v7, where satoshi amounts are bigint values (for example 50_000n), not numbers.Step 2: Create and Sign a PSBT
The approach for signing transactions depends on how you created your wallet:With Automatic Backup (Recommended)
If you created your wallet withbackUpToDynamic: true, you can sign directly without retrieving key shares. walletMetadata is the object returned by createWalletAccount (or importPrivateKey), and password is the one you set at creation:
Signing on Testnet
For testnet, usebitcoin.networks.testnet for the PSBT and BitcoinNetwork.TESTNET for network. Build the PSBT with the wallet’s testnet address (tb1q...), which you get from deriveAccountAddress with network: BitcoinNetwork.TESTNET, and keep passing the walletMetadata from createWalletAccount unchanged:
network can make signing fail with No inputs found that belong to the account address. See Working with testnet.
With Manual Backup
If you created your wallet withbackUpToDynamic: false, you must retrieve and provide external key shares:
- If your wallet was created without a password, omit the
passwordparameter - If your wallet was created with a password, you must provide it for all operations
- Wallets created with
backUpToDynamic: truealways have a password, because one is required at creation
Step 3: Finalize and Broadcast
After signing, finalize the PSBT and extract the raw transaction for broadcasting:Complete Example: Send Bitcoin
Here’s a complete example of creating, signing, and broadcasting a Bitcoin transaction:Taproot Transaction Signing
For Taproot addresses, the signing process is the same, but uses Schnorr signatures internally:Taproot transaction signing requires
@dynamic-labs-wallet/node-btc 1.1.59 or later. In earlier versions it fails with Uint8Array expected.Multi-Input Transactions
The SDK only signs inputs that belong to the sender address. Other inputs remain unsigned. To co-sign a P2WSH multisig input, see Co-signing P2WSH Multisig Inputs:Co-signing P2WSH Multisig Inputs
By defaultsignTransaction signs only inputs that belong to the wallet’s own address. To co-sign a P2WSH multisig, such as a 2-of-3 that holds this wallet’s key next to keys held elsewhere, pass the witness script in allowedWitnessScripts. This requires @dynamic-labs-wallet/node-btc 1.1.59 or later and a Native SegWit wallet.
allowedWitnessScripts is an array of hex-encoded witness scripts. An input is signed only when all of these hold:
- Its witness script is in
allowedWitnessScripts. - The SHA-256 of the witness script matches the input’s
scriptPubKey. - The script is a standard
m-of-nmultisig. - The wallet’s own compressed public key is one of the keys in the script.
allowedWitnessScripts, or passing an empty array, disables P2WSH signing.
The wallet’s publicKeyHex is the uncompressed 65-byte key (it starts with 04). A multisig script needs the compressed 33-byte form, so compress it before building the script:
signingIndexes for an input whose witness script is not allowed, or does not contain the wallet’s key, signing fails with No inputs found that belong to the account address.