Transaction Builder
The CashScript-Py TransactionBuilder generalizes transaction building to allow complex transactions combining multiple
different smart contracts within a single transaction, or to create basic P2PKH transactions. The builder works by adding
inputs and outputs to fully specify the transaction shape.
Info: defining the inputs and outputs requires careful consideration because the difference in BCH value between inputs and outputs is paid in transaction fees to miners.
Instantiating a transaction builder
from cashscript_py import ElectrumNetworkProvider, TransactionBuilder
provider = ElectrumNetworkProvider("mainnet")
transaction_builder = TransactionBuilder(provider)
To start, instantiate a transaction builder and pass in a NetworkProvider instance and other options.
Constructor options
provider
The provider option specifies the network provider to use when broadcasting the transaction and querying for details.
maximum_fee_satoshis
The maximum_fee_satoshis option specifies the maximum fee for the transaction in satoshis. If this fee is exceeded, an
error is raised when building the transaction.
maximum_fee_sats_per_byte
The maximum_fee_sats_per_byte option specifies the maximum fee per byte for the transaction. If this fee is exceeded,
an error is raised when building the transaction.
allow_implicit_fungible_token_burn
The allow_implicit_fungible_token_burn option specifies whether implicit burning of fungible tokens is allowed
(default: False). If set to True, the transaction builder will not raise an error when burning fungible tokens.
Example
from cashscript_py import ElectrumNetworkProvider, TransactionBuilder
provider = ElectrumNetworkProvider("mainnet")
transaction_builder = TransactionBuilder(provider)
Transaction building
add_input()
transaction_builder.add_input(utxo, unlocker, sequence=None) -> TransactionBuilder
Adds a single input UTXO to the transaction that can be unlocked using the provided unlocker. The unlocker can be derived
from a SignatureTemplate or a Contract instance’s spending functions.
The sequence parameter can be used to specify the sequence number of the input (advanced usage).
Note: you can create custom unlockers by implementing the Unlocker interface, but most use cases are covered by
SignatureTemplate and Contract.
Example
contract_utxos = await contract.get_utxos()
alice_utxos = await provider.get_utxos(alice_address)
transaction_builder.add_input(contract_utxos[0], contract.unlock["spend"]())
transaction_builder.add_input(alice_utxos[0], alice_template.unlock_p2pkh())
add_inputs()
Overloads:
transaction_builder.add_inputs(utxos: list[Utxo], unlocker: Unlocker, sequence: int | None = None) -> TransactionBuildertransaction_builder.add_inputs(utxos: list[UnlockableUtxo]) -> TransactionBuilder
Adds a list of input UTXOs, either with a single shared unlocker or with individual unlockers per UTXO.
Example
contract_utxos = await contract.get_utxos()
alice_utxos = await provider.get_utxos(alice_address)
# Use a single unlocker for all inputs you're adding at a time
transaction_builder.add_inputs(contract_utxos, contract.unlock["spend"]())
transaction_builder.add_inputs(alice_utxos, alice_template.unlock_p2pkh())
# Or add UnlockableUtxo items (each UTXO already has an unlocker attached)
# unlockable_utxos = [UnlockableUtxo.from_utxo(...), ...]
# transaction_builder.add_inputs(unlockable_utxos)
add_output() & add_outputs()
transaction_builder.add_output(output: Output) -> TransactionBuilder
transaction_builder.add_outputs(outputs: list[Output]) -> TransactionBuilder
Adds a single output or a list of outputs to the transaction.
Output.to can be a CashAddress (str) or raw locking bytecode (bytes). Output.token can optionally describe CashTokens.
Example
from cashscript_py import Output
transaction_builder.add_output(Output(to=alice_address, amount=100_000))
transaction_builder.add_outputs(
[
Output(to=alice_address, amount=50_000),
Output(to=bob_address, amount=50_000),
]
)
add_op_return_output()
transaction_builder.add_op_return_output(chunks: list[str]) -> TransactionBuilder
Adds an OP_RETURN output to the transaction with the provided data chunks in string format. If a string is 0x-prefixed,
it is treated as hex; otherwise it is treated as UTF-8.
Example
# Post "Hello World!" to memo.cash
transaction_builder.add_op_return_output(["0x6d02", "Hello World!"])
set_locktime()
transaction_builder.set_locktime(locktime: int) -> TransactionBuilder
Sets the locktime for the transaction to set a transaction-level absolute timelock. The locktime can be set to a block height or a unix timestamp (per consensus rules).
Example
# Use current block height as locktime (common for CLTV contracts)
current_block_height = await provider.get_block_height()
transaction_builder.set_locktime(current_block_height)
Completing the transaction
send()
await transaction_builder.send(raw: bool = False) -> dict[str, str]
After completing a transaction, send() broadcasts it to the network and then polls the provider until the transaction
can be retrieved.
CashScript-Py currently returns:
{"txid": "...", "hex": "..."}whenraw=False(default){"hex": "..."}whenraw=True
Example
from cashscript_py import Output, TransactionBuilder
contract_utxos = await contract.get_utxos()
alice_utxos = await provider.get_utxos(alice_address)
maximum_fee_satoshis = 1000
tx_details: dict[str, str] = await (
TransactionBuilder(provider, maximum_fee_satoshis=maximum_fee_satoshis)
.add_input(contract_utxos[0], contract.unlock["spend"](alice_template, 1000))
.add_input(alice_utxos[0], alice_template.unlock_p2pkh())
.add_output(Output(to=bob_address, amount=100_000))
.add_op_return_output(["0x6d02", "Hello World!"])
.send()
)
print(tx_details)
build()
transaction_builder.build() -> str
After completing a transaction, build() constructs and signs the transaction and returns the signed transaction hex
string. This can then be imported into other tools or libraries as necessary.
Example
from cashscript_py import Output, TransactionBuilder
contract_utxos = await contract.get_utxos()
alice_utxos = await provider.get_utxos(alice_address)
maximum_fee_satoshis = 1000
tx_hex: str = (
TransactionBuilder(provider, maximum_fee_satoshis=maximum_fee_satoshis)
.add_input(contract_utxos[0], contract.unlock["spend"](alice_template, 1000))
.add_input(alice_utxos[0], alice_template.unlock_p2pkh())
.add_output(Output(to=bob_address, amount=100_000))
.add_op_return_output(["0x6d02", "Hello World!"])
.build()
)
print(tx_hex)
debug()
The upstream TypeScript SDK can locally evaluate and debug transactions via a debug() API. CashScript-Py does not
currently provide an equivalent.
For debugging in Python, use:
CASHSCRIPT_PY_DEBUG=1to print preimage/sighash/signature/script detailspytest+ chipnet E2E tests for integration coverage
get_bitauth_uri()
The upstream TypeScript SDK can generate a BitAuth IDE URI (getBitauthUri()). CashScript-Py does not currently provide
this helper.
Caution: in general, it is unsafe to debug mainnet transactions in web tooling, as private keys can be exposed.
generate_wc_transaction_object()
The upstream TypeScript SDK can generate a WalletConnect signing payload. CashScript-Py does not currently provide this feature.
Transaction errors
Transactions can fail for a number of reasons, including:
insufficient or excessive fees (fee caps exceeded)
invalid unlocking bytecode (bad signature, wrong script path, missing locktime/sequence constraints)
provider failures (broadcast rejected, server unavailable)
token-policy violations (implicit fungible token burn not allowed)
In CashScript-Py, these failures are raised as Python exceptions (e.g., ValueError for local validation failures, or
provider exceptions like BroadcastFailed for network rejection).