Skip to content

Fees and change

There is no fee field in a Transaction. Any coins in our Inputs that we don’t explicitly send to an Output are paid to the miner as the fee. That is rarely what we want. Instead, we add a Change Output which sends the remainder, less the fee, back to ourselves. The TransactionBuilder works out the fee and the change amount for us when we build.

The examples use the setup from the Transaction builder, in which Alice spends a 100,000-satoshi Output.

TransactionBuilder builder = new TransactionBuilder()
.spendFromTransaction(new TransactionSigner(SIGHASH, alice), fundingTx, 0,
TransactionInput.MAX_SEQ_NUMBER, new P2PKHUnlockBuilder(alice.getPublicKey()))
.spendTo(new P2PKHLockBuilder(bobAddress), BigInteger.valueOf(50_000))
.sendChangeTo(aliceAddress);
Transaction tx = builder.build(true);
// The Outputs we added with spendTo() come first, in order. The Change Output is always last.
TransactionOutput toBob = tx.getOutputs().get(0);
TransactionOutput change = tx.getOutputs().get(1);

sendChangeTo(address) makes a P2PKH Change Output. sendChangeTo(lockingScriptBuilder) takes any locking script builder instead, for change to a P2PK or a custom script.

The Outputs we add with spendTo() come first, in the order we added them, and the Change Output is always the last Output. Code that looks up a payment by its Output index can rely on that.

The builder works out the change when we call build(), from all the Inputs, Outputs and fee settings it holds at that moment. The order of our calls doesn’t matter, so sendChangeTo() may come before spendTo().

// The change is worked out when we build, so sendChangeTo() may come before spendTo().
Transaction tx = spendAlicesCoins()
.sendChangeTo(aliceAddress)
.spendTo(new P2PKHLockBuilder(bobAddress), BigInteger.valueOf(50_000))
.build(true);
assertEquals(BigInteger.valueOf(50_000), tx.getOutputs().get(0).getAmount());
assertEquals(BigInteger.valueOf(100_000 - 50_000 - 256), tx.getOutputs().get(1).getAmount());

The builder estimates the size of the signed Transaction, and multiplies it by the fee rate, which is 512 satoshis per kilobyte unless we call withFeePerKb(). The result is rounded up to the next satoshi, and is never less than 256 satoshis.

The estimate is made up of:

  • 26 bytes of overhead,
  • for each Input, the size of its Unlocking Script once signed: 149 bytes for P2PKH, and for P2PK and multisig, the size their Signatures will take,
  • the actual size of each Output, the Change Output included.

To see the builder’s numbers before we build, we first call calculateChange(), which adds the Change Output to the estimate. estimateSize() and getFee() then report what the build will use.

TransactionBuilder builder = spendAlicesCoins()
.spendTo(new P2PKHLockBuilder(bobAddress), BigInteger.valueOf(50_000))
.sendChangeTo(aliceAddress);
// calculateChange() works out the Change Output, so that the estimate includes it.
builder.calculateChange();
// 243 bytes at 512 satoshis per kilobyte is 125 satoshis, so the 256-satoshi minimum applies.
long estimatedBytes = builder.estimateSize();
BigInteger fee = builder.getFee();

Until the change is worked out, though, getFee() reports everything that we don’t send, as if there were no Change Output.

TransactionBuilder builder = spendAlicesCoins()
.spendTo(new P2PKHLockBuilder(bobAddress), BigInteger.valueOf(50_000))
.sendChangeTo(aliceAddress);
// Until the change is worked out, getFee() reports everything we don't send.
assertEquals(BigInteger.valueOf(50_000), builder.getFee());

At a higher rate, the size starts to count.

TransactionBuilder builder = spendAlicesCoins()
.spendTo(new P2PKHLockBuilder(bobAddress), BigInteger.valueOf(50_000))
.sendChangeTo(aliceAddress)
.withFeePerKb(50_000);
builder.calculateChange();
// 243 bytes at 50,000 satoshis per kilobyte, rounded up to the next satoshi.
assertEquals(BigInteger.valueOf(12_150), builder.getFee());
Transaction tx = builder.build(true);
assertEquals(BigInteger.valueOf(100_000 - 50_000 - 12_150), tx.getOutputs().get(1).getAmount());

Do note that today’s BSV miners accept fees well below 512 satoshis per kilobyte, and the 256-satoshi minimum is far above what a small Transaction needs. To pay less, we set the fee ourselves with setFee(), from our own size estimate and the fee policy of our miner or API.

setFee() replaces the estimate, and the Change Output takes whatever is left over. Like the other builder methods, it returns the builder, and it can be called at any point before build().

Transaction tx = spendAlicesCoins()
.spendTo(new P2PKHLockBuilder(bobAddress), BigInteger.valueOf(50_000))
.sendChangeTo(aliceAddress)
.setFee(BigInteger.valueOf(100))
.build(true);

Without sendChangeTo(), the fee is everything we don’t send. build(true) guards against losing coins this way. If the leftover is more than 150 times the estimated fee, it throws.

// Without sendChangeTo(), everything we don't send is paid as the fee.
Transaction tx = spendAlicesCoins()
.spendTo(new P2PKHLockBuilder(bobAddress), BigInteger.valueOf(99_700))
.build(true);
// build(true) refuses a fee far above the estimate when there is no Change Output.
assertThrows(TransactionException.class, () -> spendAlicesCoins()
.spendTo(new P2PKHLockBuilder(bobAddress), BigInteger.valueOf(50_000))
.build(true));
// Outputs below 256 satoshis are dust, and build(true) rejects them...
assertThrows(TransactionException.class, () -> spendAlicesCoins()
.spendTo(new P2PKHLockBuilder(bobAddress), BigInteger.valueOf(100))
.sendChangeTo(aliceAddress)
.build(true));
// ...as it does Outputs that add up to more than the Inputs.
assertThrows(TransactionException.class, () -> spendAlicesCoins()
.spendTo(new P2PKHLockBuilder(bobAddress), BigInteger.valueOf(200_000))
.build(true));

The dust limit is 256 satoshis per Output, change included. If the change would fall below it, we either raise the payment by that amount, or pay it as fee by leaving sendChangeTo() out.