Batching Transaction (1 input, 5 outputs) - Legacy P2PKH

To follow along this tutorial

  • Clone the Github repository

  • cd code

  • npm install or yarn install

  • Execute the transaction code by typing node tx_filename.js

  • Alternatively you can enter the commands step-by-step by cd into ./code then type node in a terminal to open the Node.js REPL

  • Open the Bitcoin Core GUI console or use bitcoin-cli for the Bitcoin Core commands

  • Use bx aka Libbitcoin-explorer as a handy complement

Let’s create a batching transaction, also called distributing transaction.

It is a transaction that distributes one input to multiple outputs representing multiple recipients. This type of transaction is sometimes used by commercial entities to distribute funds, such as when processing payroll payments to multiple employees.

For this example we will spend a legacy P2PKH UTXO and distribute it to five different P2PKH addresses.

Create a UTXO to spend

First we need to create a previous transaction in order to have an UTXO at our disposal.

Send 1.001 BTC to alice_1 P2PKH address (0.001 will be spent on the mining fees).
sendtoaddress n4SvybJicv79X1Uc4o3fYXWGwXadA53FSq 1.001 (1)
1 Check out your wallets.json file in the code directory. Replace the address if necessary.

We have now a UTXO locked with alice_1 public key hash. In order to spend it, we refer to it with the transaction id (txid) and the output index (vout), also called outpoint.

Get the output indexes of the five transactions, so that we have their outpoint (txid / vout).
gettransaction TX_ID
Find the output index (or vout) under details  vout.

Creating the batching transaction

Now let’s spend the UTXO with BitcoinJS.

Import libraries, test wallets and set the network
const bitcoin = require('bitcoinjs-lib')
const { alice, bob, carol, dave, eve, mallory } = require('./wallets.json')
const network = bitcoin.networks.regtest
Create a BitcoinJS keypair for alice_1, the spender of our new UTXO.
const keyPairAlice1 = bitcoin.ECPair.fromWIF(alice[1].wif, network)
Create the PSBT by filling TX_ID, TX_OUT and TX_HEX.
const psbt = new bitcoin.Psbt({network})
  .addInput({
    hash: 'TX_ID',
    index: TX_OUT,
    nonWitnessUtxo: Buffer.from('TX_HEX', 'hex')
  }) (1)
  .addOutput({
    address: bob[1].p2pkh,
    value: 2e7,
  }) (2)
  .addOutput({
    address: carol[1].p2pkh,
    value: 2e7,
  })
  .addOutput({
    address: dave[1].p2pkh,
    value: 2e7,
  })
  .addOutput({
    address: eve[1].p2pkh,
    value: 2e7,
  })
  .addOutput({
    address: mallory[1].p2pkh,
    value: 2e7,
  })
1 Input referencing the 1.001 BTC UTXO just created
2 Outputs distributing 0.2 BTC to each five addresses

The miner fee is calculated by subtracting the outputs from the inputs.
100 100 000 - (20 000 000 + 20 000 000 + 20 000 000 + 20 000 000 + 20 000 000) = 100 000 100 000 satoshis equals 0,001 BTC, this is the miner fee.

Alice_1 signs the transaction that we just built with her private key.
psbt.signInput(0, keyPairAlice1)
psbt.validateSignaturesOfInput(0)
Finalize the PSBT.
psbt.finalizeAllInputs()

BitcoinJS will automatically place the signature into the scriptSig field of the input 0.

Finally we can extract the transaction and get the raw hex serialization.
console.log('Transaction hexadecimal:')
console.log(psbt.extractTransaction().toHex())
Inspect the raw transaction with Bitcoin Core CLI, check that everything is correct.
decoderawtransaction TX_HEX

Broadcasting the transaction

It’s time to broadcast the transaction via Bitcoin Core CLI.
sendrawtransaction TX_HEX
Inspect the transaction.
getrawtransaction TX_ID true

Observations

We note that we have five outputs, locking 0.2 BTC each to five different public key hash / addresses.