Skip to main content

Embedded Wallet Differences

The Bitcoin functions work with both external wallets (browser extensions such as Xverse, Unisat, OKX, and Phantom) and Dynamic embedded wallets. Most behave identically. A few accept options that an embedded wallet cannot act on, because an embedded wallet is a single MPC key rather than a wallet application managing several addresses. This page lists every difference. If a function is not mentioned here, it behaves the same for both wallet types.

Why the differences exist

An embedded Bitcoin wallet is one key at one fixed derivation path. Three consequences follow, and they explain every entry below. One address type per wallet. The address type is chosen when the wallet is created, either Native SegWit or Taproot, and cannot change afterwards. An external wallet can expose a payment address and a separate ordinals address at the same time, so its functions accept an addressType to select between them. An embedded wallet has only one address type, so there is nothing to select. No derived addresses. The derivation path is fixed per address type, with no account or index component, and a user has one Bitcoin embedded wallet. You cannot derive additional receiving addresses from it. One address, encoded per network. The same key produces a different address string on each network, because only the prefix and checksum differ:
These are the same wallet, not two addresses. Where a function takes an address, pass the encoding for the network you are signing on.

signPsbt and signPsbts

An embedded wallet signs only the inputs that belong to its own address. Inputs owned by other addresses in the PSBT are left untouched, so a PSBT can still be co-signed by another party afterwards. The one exception is a P2WSH multisig input that you explicitly allow with allowedWitnessScripts.
signature[].address must be your wallet’s own address for the active network. On testnet, use the tb1... encoding. An address the wallet does not own is rejected rather than ignored, so a mistyped or mainnet-encoded address returns an error instead of a PSBT with nothing signed. signature[].signingIndexes limits signing to a subset of your wallet’s inputs. Requesting an index the wallet does not own throws an error, and no input is signed. signature[].disableAddressValidation has no effect. On an external wallet the extension verifies key ownership itself before signing, so skipping Dynamic’s check still leaves a check in place. On an embedded wallet the ownership check is the only one, so it always runs. allowedSighash restricts which signature hash (sighash) types the wallet will sign. The type itself comes from the PSBT (BIP-174 PSBT_IN_SIGHASH_TYPE), so this option can only reject a type, never select one.

Defaults

Two separate settings determine what is signed: which inputs, and which sighash type is accepted. Omitting either one never extends signing beyond the inputs the wallet owns.
An empty allowedSighash means “accept any type”, not “use SIGHASH_ALL”. The type always comes from the PSBT. SIGHASH_ALL is the fallback only when the PSBT itself declares nothing, which is the common case for a PSBT built by sendBitcoin or a standard wallet.
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. Use allowedSighash to restrict signing to the types your application expects, for example [0x01, 0x81] for SIGHASH_ALL and SIGHASH_ALL | ANYONECANPAY.

Co-signing P2WSH multisig inputs

By default an embedded wallet signs only inputs that spend from its own address. 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 option is available for embedded wallets only, and requires @dynamic-labs-sdk/bitcoin 1.48.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.
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.
The returned PSBT is not finalized. It carries this wallet’s partial signature, and you merge the signatures from the other co-signers and finalize it yourself before broadcasting.
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.
Only a wallet whose address type is Native SegWit can co-sign P2WSH inputs. A Taproot wallet never signs a P2WSH input, even when its script is listed. The multisig script must contain the wallet’s compressed public key, which you can read from the publicKey of the Native SegWit entry in the wallet account’s addressesWithTypes.

signMessageWithCustomOptions

Both options are ignored for embedded wallets. The call succeeds and returns a valid signature over the message, using the wallet’s single address and BIP-322.
addressType has no effect. An embedded wallet has one address type, fixed at creation, so there is nothing to select. protocol is not supported. Embedded wallets sign messages with BIP-322 only, and Dynamic verifies the BIP-322 hash on the server. Requesting ecdsa does not change the signature that is produced. To sign a message with an embedded wallet, use signMessage, which needs neither option.

Functions that behave the same

Last modified on November 2, 2026