Blockchain

Manually Specifying Transaction Outputs: A Key Step in Designing BSV Applications

Learn how Bitcoin transaction outputs work by constructing transactions manually. This article covers output types, change rules, index ordering, and common pitfalls, helping you advance from “sending transactions” to “designing BSV applications.”

Ethan Lin

Ethan Lin

technical_editor

Published Jun 18, 20263 min read

In a Nutshell

Manually specifying outputs means explicitly deciding which new UTXOs this transaction will create, how many satoshis each output holds, and which locking script secures it.

If inputs are “spending old UTXOs,” then outputs are “creating new UTXOs.” The essence of a Bitcoin transaction is this cycle: spend old UTXOs, create new UTXOs.

Manually Specifying Transaction Outputs: A Key Step in Designing BSV Applications article cover

Outputs Determine the Transaction's Outcome

An output includes at least two core elements:

  • satoshis: The amount of BSV (in its smallest unit) this output carries.
  • locking script: Defines who can spend this output in the future, or whether it merely carries data.

A transaction can have multiple outputs. For example:

Code
1input:
2 spends a UTXO of 1000 satoshis
3
4outputs:
5 output 0: 300 satoshis to the recipient
6 output 1: 600 satoshis as change back to yourself
7 output 2: 0 satoshis OP_RETURN data
8 fee: 100 satoshis

Note that the fee is not an output. The fee is the difference between the total inputs and the total outputs.

Common Output Types

1. Regular Payment Output

Regular payments usually use P2PKH or similar scripts. The idea is that the future spender must provide the corresponding public key and a valid signature.

TypeScript
1tx.addOutput({
2 lockingScript: new P2PKH().lock(recipientAddress),
3 satoshis: 100
4})

2. Change Output

When the input amount exceeds the intended outputs and fees, the remainder is typically returned to an address you control. You can mark a change output with change: true so the SDK fills the change amount after fee calculation.

TypeScript
1tx.addOutput({
2 lockingScript: new P2PKH().lock(myAddress),
3 change: true
4})

3. Data Output

Data outputs often use OP_RETURN. They are not meant for future spending, but for recording data.

TypeScript
1tx.addOutput({
2 lockingScript: Script.fromASM('OP_RETURN 48656c6c6f20425356'),
3 satoshis: 0
4})

4. Application Protocol Output

Applications can place protocol-specific data or scripts inside an output, such as token states, credentials, orders, or supply chain events. The key is not just “being able to put it in,” but having a parsable, verifiable protocol format.

Output Index Matters

Outputs inside a transaction are ordered. The first is index 0, the second is index 1.

When you spend an output in the future, the input must reference:

Code
1txid + output index

Thus, the order of outputs feeds into subsequent transaction references.

If an application protocol requires a certain piece of data at output 0 and a token state at output 1, the order cannot be arbitrarily shuffled. Output randomization features in wallets or SDKs can be valuable for privacy, but must be used cautiously in protocol scenarios.

The Full Manual Output Flow

When constructing a transaction at a lower level, the typical sequence is:

  1. Create a Transaction
  2. Add one or more inputs
  3. Add target outputs
  4. Add a change output
  5. Calculate the fee
  6. Sign
  7. Serialize or broadcast

Translated into SDK concepts:

TypeScript
1const tx = new Transaction()
2
3tx.addInput({
4 sourceTransaction,
5 sourceOutputIndex: 0,
6 unlockingScriptTemplate: new P2PKH().unlock(privateKey)
7})
8
9tx.addOutput({
10 lockingScript: new P2PKH().lock(recipientAddress),
11 satoshis: 100
12})
13
14tx.addOutput({
15 lockingScript: new P2PKH().lock(myAddress),
16 change: true
17})
18
19await tx.fee()
20await tx.sign()

This is more verbose than using createAction(), but it makes the transaction construction process completely transparent.

Never Forget the Change Output

A critical mistake when manually constructing a transaction is forgetting the change output.

Suppose your input is 10000 satoshis and you only create an output of 100 satoshis. Without a change output, the remaining 9900 satoshis do not automatically return to your account; they become the fee.

Simplified formula:

Code
1fee = total inputs - total outputs

So if you under-specify outputs, the fee becomes abnormally high.

Where This Fits in the BSV Tech Stack

The business semantics of BSV applications are often reflected in outputs.

  • Payment applications: Focus on the recipient output and the change output.
  • Data applications: Focus on the protocol fields in the OP_RETURN output.
  • Token applications: Focus on the spendable outputs representing asset states.
  • Overlay services: May index application data based on output scripts, data formats, and protocol identifiers.

Therefore, understanding outputs manually is a key step from “sending transactions” to “designing BSV applications.”

Common Beginner Misconceptions

  • An output is not a memo field; it is the core result of a transaction.
  • A change output is not an optional detail; forgetting it leads to abnormally high fees.
  • An OP_RETURN data output and a P2PKH payment output are not the same kind of thing.
  • Output indices start at 0; when referencing an output later, you must specify the correct index.
  • Output order can affect application protocol parsing; random ordering is not suitable for all scenarios.

References

Recommended articles