Skip to main content

Recommended: JavaScript SDK with React Hooks

For new React apps, we recommend the JavaScript SDK with React Hooks (@dynamic-labs-sdk/react-hooks) instead of the legacy React SDK documented here. The JS SDK comes with many benefits such as a much smaller bundle size and other optimizations. Use the React quickstart (JavaScript SDK) to get started.

Signing PSBTs (Partially Signed Bitcoin Transactions) for Bitcoin Embedded Wallets

This guide explains how to use the signPsbt method to sign Partially Signed Bitcoin Transactions (PSBTs) for Bitcoin embedded wallets. By default the method signs only the inputs related to the user’s wallet and returns a signed (but not finalized) PSBT for review and finalization. You can also opt in to co-signing a P2WSH multisig input.

Overview

PSBTs allow multiple parties to collaboratively build and sign Bitcoin transactions. The signPsbt method:
  • Signs only inputs that belong to the user’s wallet address, plus any P2WSH multisig input whose witness script you allow with allowedWitnessScripts
  • Does NOT finalize the PSBT, which remains partially signed for review
  • Returns a signed PSBT that can be reviewed, combined with other signatures, and finalized by the user

Method Signature

Type Definitions

For Embedded Wallets

signingIndexes, allowedSighash, and allowedWitnessScripts are available in @dynamic-labs/bitcoin 5.14.0 and later.
Important: Embedded wallets:
  • Only require unsignedPsbtBase64. All other fields are optional.
  • Read the sighash type from the PSBT. An input that declares none is signed with SIGHASH_ALL (0x01), or SIGHASH_DEFAULT (0x00) on a Taproot wallet.
  • Sign all inputs that belong to the wallet address, unless you limit them with signingIndexes.
  • Sign a P2WSH multisig input only when you list its witness script in allowedWitnessScripts.
  • Do not support the signature parameter

Important Notes

PSBT is NOT Finalized

The signPsbt method does NOT finalize the PSBT. It only signs the inputs that belong to the user’s wallet. The returned PSBT:
  • Contains signatures for the user’s inputs
  • Remains in PSBT format (not a finalized transaction)
  • Can be reviewed by the user before finalization
  • Can be combined with signatures from other parties
  • Must be finalized before broadcasting
By default the method signs all inputs that belong to the user’s wallet address. To sign only some of them, pass signingIndexes. Every index you list must belong to the wallet, otherwise the call throws an error and signs nothing. Inputs that belong to other addresses are left unsigned, unless they are P2WSH multisig inputs you allow with allowedWitnessScripts.

🔍 User Review and Finalization

After signing, the PSBT should be:
  1. Reviewed by the user to verify transaction details
  2. Combined with other signatures if it’s a multi-party transaction
  3. Finalized using a Bitcoin library (e.g., bitcoinjs-lib)
  4. Broadcast to the Bitcoin network

Usage Examples

Basic PSBT Signing for Embedded Wallets

This example signs all inputs that belong to the user’s embedded wallet:

Complete Example with Review and Finalization

This example shows the complete flow: signing, reviewing, and finalizing a PSBT:

Co-signing P2WSH multisig inputs

To co-sign a P2WSH multisig, for example a 2-of-3 that holds the embedded wallet’s key next to keys held elsewhere, pass the multisig’s witness script in allowedWitnessScripts. This requires @dynamic-labs/bitcoin 5.14.0 or later. allowedWitnessScripts is an array of hex-encoded witness scripts. The wallet signs a P2WSH input only when all of these hold:
  • The input’s witness script is in allowedWitnessScripts.
  • The SHA-256 of the witness script matches the input’s scriptPubKey.
  • The script is a standard m-of-n multisig.
  • The wallet’s own compressed public key is one of the keys in the script.
The returned PSBT carries this wallet’s partial signature and is not finalized. Merge the signatures from the other co-signers and finalize it yourself before broadcasting. Only a Native SegWit wallet can co-sign P2WSH inputs. A Taproot wallet never signs one, even when its script is listed. Build the multisig script with the wallet’s compressed public key, which is the publicKey on the wallet’s Native SegWit entry in primaryWallet.additionalAddresses. An input that fails any check is not signed. If you also pass signingIndexes naming that input, the call throws signingIndexes contains indexes that do not belong to the signing address.
allowedWitnessScripts is your application’s consent to co-sign that script. Build the list from scripts you constructed and trust, never from scripts taken from the PSBT or from user input, and keep allowedSighash set to the types you expect.

Signature Hash Types

Embedded wallets read the sighash type from the PSBT (BIP-174 PSBT_IN_SIGHASH_TYPE). An input that declares no type is signed with SIGHASH_ALL (0x01), or SIGHASH_DEFAULT (0x00) on a Taproot wallet. Use allowedSighash to restrict which declared types the wallet accepts. The option can only reject a type, never select one. If an input declares a type outside the list, the call throws an error and signs nothing. An omitted or empty allowedSighash accepts any declared type.
SIGHASH_NONE produces a signature that does not commit to the transaction outputs, and SIGHASH_SINGLE commits to only one output index. Anyone holding the signed PSBT can change the remaining outputs before broadcasting. Set allowedSighash to the types your application expects, for example [0x01, 0x81] for SIGHASH_ALL and SIGHASH_ALL | ANYONECANPAY.

Embedded Wallet Specific Notes

For embedded wallets:
  1. Simple default: Only unsignedPsbtBase64 is required:
  2. Input selection: The method signs all inputs that belong to the wallet address. Pass signingIndexes to sign a subset.
  3. Sighash: The type comes from the PSBT. Use allowedSighash to restrict which types are accepted.
  4. P2WSH multisig: Inputs of this kind are signed only when you list their witness script in allowedWitnessScripts.
  5. No Signature Parameter: The signature parameter is not supported (TypeScript will prevent you from using it).
  6. MFA Support: The signing process may require MFA authentication if enabled.

Error Handling

Common errors and how to handle them:

Invalid PSBT Format

Best Practices

  1. Always Review Before Finalization: Parse and display the PSBT details to the user before finalization
  2. Validate PSBT Format: Check that the PSBT is valid before attempting to sign
  3. Handle Errors Gracefully: Wrap signPsbt calls in try-catch blocks
  4. Restrict What You Sign: Set allowedSighash to the types you expect, and signingIndexes when you only want some inputs signed
  5. Don’t Finalize Automatically: Let the user review and finalize the PSBT manually
  6. Store Signed PSBTs Securely: Signed PSBTs contain sensitive information, so handle them securely

PSBT Workflow

The typical PSBT workflow is:
  1. Build PSBT: Create an unsigned PSBT with transaction details
  2. Sign PSBT: Use signPsbt to sign inputs belonging to the user
  3. Review PSBT: Parse and display transaction details for user review
  4. Combine Signatures: If multi-party, combine with other signatures
  5. Finalize PSBT: Convert the PSBT to a finalized transaction
  6. Broadcast Transaction: Send the finalized transaction to the Bitcoin network

Additional Resources

Last modified on November 2, 2026