Blockchain

Getting started with BSV transactions: the right way to manually specify inputs

In BSV's UTXO model, manually specifying transaction inputs is a must-have skill for advanced development. This article explains the essence of inputs, the required information, code examples, and common pitfalls, helping you avoid the mental trap of "debiting an address."

Ethan Lin

Ethan Lin

technical_editor

Published Jun 18, 20264 min read

In a Nutshell

Manually specifying inputs means telling the transaction exactly: "I want to spend this specific output from that historical transaction."

After the stage where your wallet automatically picks inputs, you'll eventually need to construct transactions by hand. At that point, an input isn't just any address you throw in; it's a reference to an existing, unspent UTXO.

Getting started with BSV transactions: the right way to manually specify inputs article cover

Inputs Don't Reference Addresses

The most common beginner mistake is thinking an input says "deduct from this address." In Bitcoin's UTXO model, there is no account-balance subtraction layer.

An input references an outpoint, which consists of two parts:

  • The txid of the previous transaction
  • The output index in that transaction

For example:

TEXT
1txid: 9f...ab
2output index: 0

This means: I want to spend output #0 of transaction 9f...ab.

An address merely helps the wallet generate a locking script in typical payment scenarios. What's really being spent is an output, not some address-based account.

What You Need to Manually Build an Input

To construct an input by hand, you typically need at least the following:

  1. The previous transaction itself
    You need to know which transaction the UTXO being spent came from. Low-level SDK examples often show passing sourceTransaction – the complete raw data of that previous transaction.

  2. The output index
    A transaction can have multiple outputs. Knowing just the txid isn't enough; you must specify exactly which output you're spending.

  3. The locking script of that previous output
    You need to know the original spending conditions for signing and verification. A P2PKH output is completely different from an OP_RETURN output.

  4. The amount of that previous output
    Fee calculation and signature verification may both need the amount.

  5. How to unlock it
    For P2PKH, unlocking typically requires a signature and public key. The SDK can create an unlocking template with new P2PKH().unlock(privateKey).

Expressing Inputs with the SDK

In the official low-level transaction examples, manually constructed inputs look like this:

TypeScript
1import { Transaction, PrivateKey, P2PKH } from '@bsv/sdk'
2
3const privateKey = PrivateKey.fromWif('your_private_key_here')
4const sourceTxHex = '...' // hex of the previous transaction containing the spendable output
5
6const tx = new Transaction()
7
8tx.addInput({
9 sourceTransaction: Transaction.fromHex(sourceTxHex),
10 sourceOutputIndex: 0,
11 unlockingScriptTemplate: new P2PKH().unlock(privateKey)
12})

What this code expresses:

  • I want to spend output #0 from sourceTransaction.
  • If that output is a P2PKH output, use privateKey to generate the corresponding unlocking script.

Note that this is only the input part. The transaction isn't finished yet – it still needs outputs, fees, signing, and broadcasting.

Why You Need the Previous Transaction

A signature isn't just a random signature on "the current transaction." The signature proves that you have the right to spend the referenced previous output, and that you endorse the current transaction's output arrangement.

Verifiers need to know the locking script of that previous output so they can combine it with the unlocking script provided in the current input and execute them together.

Simplified flow:

TEXT
1Current input provides the unlocking script
2Previous output provides the locking script
3The two are combined and executed
4If the result is true -> the input's spending condition is satisfied

If you only provide a txid but lack the previous output's script and amount, many low-level operations cannot be performed correctly.

Input Count Affects Transaction Size

A single input typically contains:

  • Previous transaction hash
  • Output index
  • Unlocking script
  • Sequence

The more inputs you have, the larger the transaction, and the higher the fee usually is.

That's also why automatic wallet input selection isn't just about simply gathering enough funds. It also considers transaction size, change, fees, privacy, and UTXO management.

You Can't Spend the Same UTXO Twice

Once a UTXO has been spent by a valid transaction, it cannot be used by another transaction.

If two transactions both reference the same UTXO, they conflict, and the network and miners can only eventually accept one of them. This problem is the basis of the double-spend risk.

So when manually specifying inputs, you must confirm that the UTXO is indeed unspent. If you use a stale UTXO list, it's easy to construct invalid transactions.

Where This Fits in the BSV Tech Stack

Application protocols on BSV often tie business state to specific UTXOs.

For example, a token's state may be represented by a particular output. When transferring a token, you don't just find any UTXO of the same amount; you must spend the exact UTXO that represents that token.

As another example, a contract-like state machine may require you to spend the output of the previous state, then create the output of the next state.

That's why advanced BSV applications cannot rely forever on a wallet's default input selection. Default selection works for ordinary payments, but application protocols often need precise selection.

Common Beginner Misunderstandings

  • A txid alone isn't enough: you also need the output index.
  • Inputs are not addresses: an input references a specific output of a previous transaction.
  • A UTXO that's already been spent can't be used again: reusing it will cause conflicting transactions.
  • Manually specifying inputs brings responsibility: you must ensure the amounts, scripts, unlocking methods, and state are all correct.
  • OP_RETURN data outputs are generally not spendable: don't treat every output as a usable input.

References

Recommended articles