How do you switch your bitcoinjs integrationfrom TransactionBuilder to transactions-psbt.js? the code below works:



const btc = require('bitcoinjs-lib');

var tx = new btc.TransactionBuilder(net);
inputs.forEach(o =&gt; tx.addInput(o.txId, o.vout));
outputs.forEach(o =&gt; tx.addOutput(o.address, o.value));
tx.sign(0, owner);
var body = tx.build().toHex();


but it's complaining that TransactionBuilder is deprecated... sigh...


  Deprecation Warning: TransactionBuilder will be removed in the future.
  (v6.x.x or later) Please use the Psbt class instead. Examples of usage
  are available in the transactions-psbt.js integration test file on our
  Github. A high level explanation is available in the psbt.ts and
  psbt.js files as well


and apparently the signing methodology has also changed (grr):


  DEPRECATED: TransactionBuilder sign method arguments will change in
  v6, please use the TxbSignArg interface


I've read the documentation on Psbt and understand the general concept but have not found a guide that helps with the transition.  I tried to simply change tx.sign() for tx.signInput() but it pukes

I've also looked at the test suite for the library but it's too complicated for me to understand.  How do I convert my code to the new mechanism?
You're only signing the first input? tx.sign(0, owner); Usually every input has to be signed somehow.

I also struggled with the switch to Psbt. Biggest difference was basically requirement to have a full transaction hex of every input for non-segwit inputs. Segwit inputs can be simpler but they can also be created using the full hex of the tx using non-segwit command so don't have to add extra logic differentiating between inputs. You can no longer just provide txid and vout, just add on the hex of tx that created each input for your object.

I really just followed examples https://github.com/bitcoinjs/bitcoinjs-lib#examples

So this is my take on a very general tx with psbt that I use for now:

import * as bitcoin from 'bitcoinjs-lib'

// not all parts of inputs have to be defined
// inputs - array of all data needed for inputs
// outputs - array of all data needed for outputs

const network = bitcoin.networks.testnet // or bitcoin.networks.bitcoin

const psbt = new bitcoin.Psbt({ network })
psbt.setVersion(2)    // default
psbt.setLocktime(0)   // default

// ************** create inputs **************

inputs.forEach(input=&gt; {
  psbt.addInput({
    hash: input.txid,  // txid number
    index: input.vout,  // output number
    sequence: input.sequence, // often 0xfffffffe    
    nonWitnessUtxo: Buffer.from(input.hex, 'hex'), // works for witness inputs too!
    redeemScript: input.redeemScript, // only if there's redeem script
    witnessScript: input.witnessScript // only if there's witness script
  }) 
})

// ************** create outputs **************

outputs.forEach(output =&gt; {

  psbt.addOutput({
    address: output.address, // only if not op_return type output (no address for those)
    value: output.value,
    script: output.data  // only if there's string (utf8) data to embed in op_return
      ? bitcoin.payments.embed({ data: [Buffer.from(output.data, 'utf8')] }).output 
      : undefined
  }) 
})

// ************** signing **************

inputs.forEach((input, index) =&gt; {

  // sign regular inputs that can be simply signed
  if (!input.redeemScript &amp;&amp; !input.witnessScript) {
    psbt.signInput(index, input.keyPair)  

    // give error if signature failed
    if (!psbt.validateSignaturesOfInput(index)) {
      throw new Error('Signature validation failed for input index ' + index.toString())
    }    
  }

})

// ************** finalizing inputs **************

inputs.forEach((input, index) =&gt; {

  // sign regular inputs that can be simply signed
  if (!input.redeemScript &amp;&amp; !input.witnessScript) {
    psbt.finalizeInput(index) 
  }

  // for p2sh or p2wsh script inputs
  if (input.redeemScript || input.witnessScript) {
    psbt.finalizeInput(
      index, 
      getFinalScripts({ 
        inputScript: input.inputScript, 
        network 
      })
    )
  }
})

// ************** make tx **************

const tx = psbt.extractTransaction()

const virtualSize = tx.virtualSize()
const txid = tx.getId()
const hex = tx.toHex()

console.log('tx virtualSize:', virtualSize)
console.log('tx txid:', txid)
console.log('tx hex:', hex)



And for some reason finalizeInput for custom scripts is not exactly built in yet but I found it in examples and just added as a function I import or put somewhere in same file.

function getFinalScripts ({ inputScript, network }) {
  return function (
    inputIndex,
    input,
    script,
    isSegwit,
    isP2SH,
    isP2WSH,
  ) {
    // Step 1: Check to make sure the meaningful script matches what you expect.

    // Step 2: Create final scripts
    let payment = {
      network,
      output: script,
      input: inputScript,
    };
    if (isP2WSH &amp;&amp; isSegwit)
      payment = bitcoin.payments.p2wsh({
        network,
        redeem: payment,
      });
    if (isP2SH)
      payment = bitcoin.payments.p2sh({
        network,
        redeem: payment,
      });

    function witnessStackToScriptWitness(witness) {
      let buffer = Buffer.allocUnsafe(0);

      function writeSlice(slice) {
        buffer = Buffer.concat([buffer, Buffer.from(slice)]);
      }

      function writeVarInt(i) {
        const currentLen = buffer.length;
        const varintLen = varuint.encodingLength(i);

        buffer = Buffer.concat([buffer, Buffer.allocUnsafe(varintLen)]);
        varuint.encode(i, buffer, currentLen);
      }

      function writeVarSlice(slice) {
        writeVarInt(slice.length);
        writeSlice(slice);
      }

      function writeVector(vector) {
        writeVarInt(vector.length);
        vector.forEach(writeVarSlice);
      }

      writeVector(witness);

      return buffer;
    }

    return {
      finalScriptSig: payment.input,
      finalScriptWitness:
        payment.witness &amp;&amp; payment.witness.length &gt; 0
          ? witnessStackToScriptWitness(payment.witness)
          : undefined,
    };
  }
}


I haven't tested every kind of input yet but this is basically what I use for p2wpkh and p2wsh inputs. I try to find examples spending exactly the type of input I need usually.