Creating Bitcoin PSBTs
PSBTs or partially signed Bitcoin transactions is a standard that allows unsigned or partially signed transactions to be transported for signing by multiple parties.
Here we will show examples of creating partially signed Bitcoin transactions using the light-weight and audited Bitcoin javascript library scure-btc-signer.
Creating a transaction with a wrapped-segwit (P2SH-P2WPHK) address
import * as btc from '@scure/btc-signer'
import { hex, base64 } from '@scure/base'
// You can use any public Bitcoin API to retrieve unspent outputs
const output = {
"tx_hash": "f39d37ec885de70c598648a2f80f103e1cdf34f7021ddfcb22216b7076169226",
"block_height": 780179,
"tx_input_n": -1,
"tx_output_n": 1,
"value": 300000,
"ref_balance": 22681,
"spent": false,
"confirmations": 123,
"confirmed": "2023-03-10T10:02:21Z",
"double_spend": false
}
// in the below, use btc.TEST_NETWORK for testnet/signet or btc.NETWORK for mainnet
const publicKey = hex.encode("02818b7ff740a40f311d002123087053d5d9e0e1546674aedb10e15a5b57fd3985")
const p2wpkh = btc.p2wpkh(publicKey, btc.TEST_NETWORK);
const p2sh = btc.p2sh(p2wpkh, btc.TEST_NETWORK);
tx.addInput({
txid: output.tx_hash,
index: output.tx_output_n,
witnessUtxo: {
script: p2sh.script,
amount: BigInt(output.value),
},
redeemScript: p2sh.redeemScript,
})
// You can add more inputs here as necessary
// Add outputs
const recipient = "tb1pzwa68q3udj0f7g5xtdakgecvf45dvssu66ry7y3at22w7vus20vq3sgw62"
const changeAddress = "2NBfRKCUpafbatj5gV9T82uau3igdSf9BXJ"
tx.addOutputAddress(recipient, BigInt(200000), btc.TEST_NETWORK)
tx.addOutputAddress(changeAddress, BigInt(80000), btc.TEST_NETWORK)
// Generate the base64 encoded PSBT that can be
// passed to a compatible wallet for signing
const psbt = tx.toPSBT(0)
const psbtB64 = base64.encode(psbt0)
Creating a transaction with a native-segwit (P2WPHK) address
import * as btc from '@scure/btc-signer'
import { hex, base64 } from '@scure/base'
// You can use any public Bitcoin API to retrieve unspent outputs
const output = {
"tx_hash": "f39d37ec885de70c598648a2f80f103e1cdf34f7021ddfcb22216b7076169226",
"block_height": 780179,
"tx_input_n": -1,
"tx_output_n": 1,
"value": 300000,
"ref_balance": 22681,
"spent": false,
"confirmations": 123,
"confirmed": "2023-03-10T10:02:21Z",
"double_spend": false
}
// in the below, use btc.TEST_NETWORK for testnet/signet or btc.NETWORK for mainnet
const publicKey = hex.encode("02818b7ff740a40f311d002123087053d5d9e0e1546674aedb10e15a5b57fd3985")
const p2wpkh = btc.p2wpkh(publicKey, btc.TEST_NETWORK);
tx.addInput({
txid: output.tx_hash,
index: output.tx_output_n,
witnessUtxo: {
script: p2wpkh.script,
amount: BigInt(output.value),
},
})
// You can add more inputs here as necessary
// Add outputs
const recipient = "tb1pzwa68q3udj0f7g5xtdakgecvf45dvssu66ry7y3at22w7vus20vq3sgw62"
const changeAddress = "tb1q8dq9ql8p7lvh9m5w5njqa2x424fqydw64fxxen"
tx.addOutputAddress(recipient, BigInt(200000), btc.TEST_NETWORK)
tx.addOutputAddress(changeAddress, BigInt(80000), btc.TEST_NETWORK)
// Generate the base64 encoded PSBT that can be
// passed to a compatible wallet for signing
const psbt = tx.toPSBT(0)
const psbtB64 = base64.encode(psbt0)
Creating a transaction with a Taproot (P2TR) address
import * as btc from '@scure/btc-signer'
import { hex, base64 } from '@scure/base'
// in the below, use btc.TEST_NETWORK for testnet/signet or btc.NETWORK for mainnet
const internalPubKey =
hex.decode("b9907521ddb85e0e6a37622b7c685efbdc8ae53a334928adbd12cf204ad4e717")
const p2tr = btc.p2tr(internalPubKey, undefined, btc.TEST_NETWORK)
tx.addInput({
txid: output.tx_hash,
index: output.tx_output_n,
witnessUtxo: {
script: p2tr.script,
amount: BigInt(output.value),
},
tapInternalKey: internalPubKey,
sighashType: btc.SigHash.ALL
})
// You can add more inputs here as necessary
// Add outputs
const recipient = "tb1pzwa68q3udj0f7g5xtdakgecvf45dvssu66ry7y3at22w7vus20vq3sgw62"
const changeAddress = "2NBfRKCUpafbatj5gV9T82uau3igdSf9BXJ"
tx.addOutputAddress(recipient, BigInt(200000), btc.TEST_NETWORK)
tx.addOutputAddress(changeAddress, BigInt(80000), btc.TEST_NETWORK)
// Generate the base64 encoded PSBT that can be
// passed to a compatible wallet for signing
const psbt = tx.toPSBT(0)
const psbtB64 = base64.encode(psbt0)
Last updated