bitcoinjs-lib
Derive address formats from network constants, sign messages, and round-trip PSBTs through the wallet.
bitcoinjs-lib is the long-standing Bitcoin transaction toolkit. It is a
builder, not an RPC client: it ships network constants and PSBT
construction, but no node connection. butr discovers and manages the wallet
and exposes signing; bitcoinjs-lib supplies the network definitions and
transaction encoding; a separate Esplora or Electrum client (not shown)
supplies UTXOs and broadcasts.
Get the wallet
useSelectedWallet("bitcoin") types the connector as a BitcoinAdapter, so
signTransaction takes PSBT bytes and sendTx a BitcoinTransfer:
import { useSelectedWallet } from "@usebutr/react";
const wallet = useSelectedWallet("bitcoin");Derive the address format from the chain
bitcoinjs-lib's networks carry the bech32 prefix per network. Map the
connected chain to a network to know which address format the wallet uses:
import { BITCOIN_CHAINS } from "@usebutr/core";
import type { Network } from "bitcoinjs-lib";
import { networks } from "bitcoinjs-lib";
const NETWORK_BY_CHAIN = new Map<string, Network>([
[BITCOIN_CHAINS.mainnet.id, networks.bitcoin],
[BITCOIN_CHAINS.testnet.id, networks.testnet],
[BITCOIN_CHAINS.testnet4.id, networks.testnet],
[BITCOIN_CHAINS.signet.id, networks.testnet], // both share testnet's prefixes
]);
const bech32Prefix = NETWORK_BY_CHAIN.get(wallet.account.chain.id)?.bech32 ?? "unknown";Sign a message
signMessage exists when the wallet can sign; check it first:
if (!wallet.connector.signMessage) {
throw new Error(`${wallet.connector.name} cannot sign messages`);
}
const message = new TextEncoder().encode("Hello from butr + bitcoinjs-lib");
const { signature } = await wallet.connector.signMessage(message, { account: wallet.account });Sign a PSBT
Not every Bitcoin wallet signs PSBTs, so signTransaction is optional: it
exists on Wallet Standard wallets that advertise bitcoin:signPsbt and on the
injected Unisat and Xverse adapters.
if (!wallet.connector.signTransaction) {
throw new Error(`${wallet.connector.name} does not sign PSBTs (bitcoin:signPsbt)`);
}
// Built with bitcoinjs-lib's Psbt class from real UTXOs in a production app.
const signed = await wallet.connector.signTransaction(psbt.toBuffer(), {
account: wallet.account,
});No broadcast. signTransaction resolves the signed PSBT; finalize it with bitcoinjs-lib
and POST the raw transaction to an Esplora, Electrum or Bitcoin Core endpoint yourself. For a
plain payment the wallet can broadcast itself, sendTx({ amount, recipient }) resolves the
txid, with amount in satoshis.
chain in the options targets one network. Wallet Standard wallets route it per call; Unisat and
Xverse switch the whole wallet first, and reject with a ChainMismatch ConnectionError when
they cannot. See the Bitcoin connector.
Source: apps/demo-with-bitcoin/src/app.tsx in the butr
repository. Run pnpm dev --filter=demo-with-bitcoin → https://usebutr.demo-with-bitcoin.localhost.