Skip to content

Transaction Generator

The Generator is the SDK's built-in coin selector and fee calculator. You hand it UTXOs, a change address, and the outputs you want; it picks inputs, computes mass and fees, and yields one or more PendingTransactions ready to sign and submit.

Idiomatic: feed it a UtxoContext

In a long-running process, build a UtxoContext (see UTXO Context) once and pass it as entries. The generator pulls the current mature set on iteration — no manual UTXO snapshot, no stale data. PaymentOutput and NetworkId describe the destination.

gen = Generator(
    network_id=NetworkId("mainnet"),
    entries=context,                         # UtxoContext
    change_address=my_addr,
    outputs=[PaymentOutput(recipient, 100_000_000)],   # 1 KAS
)
for pending in gen:
    pending.sign([key])
    print("submitted:", await pending.submit(client))

A Generator is iterable — when the input set is too large for one transaction's mass budget, it yields a chain of consolidating transactions followed by the final payment. Loop and submit each.

For the full processor → context → generator wiring, see Wallet SDK → Overview.

One-shot: raw UTXO list

Without a context (one-shot scripts, ad-hoc tools), pass the entries list straight from get_utxos_by_addresses (see RPC → Calls):

import asyncio
from kaspa import (
    RpcClient, Resolver, Generator, PaymentOutput,
    PrivateKey, NetworkId,
)

async def main():
    client = RpcClient(resolver=Resolver(), network_id="mainnet")
    await client.connect()
    try:
        key = PrivateKey("<64-char hex>")
        my_addr = key.to_address("mainnet")

        utxos = await client.get_utxos_by_addresses({
            "addresses": [my_addr.to_string()],
        })

        gen = Generator(
            network_id=NetworkId("mainnet"),
            entries=utxos["entries"],
            change_address=my_addr,
            outputs=[PaymentOutput(my_addr, 100_000_000)],   # 1 KAS
        )
        for pending in gen:
            pending.sign([key])
            print("submitted:", await pending.submit(client))

        print(gen.summary().fees, gen.summary().transactions)
    finally:
        await client.disconnect()

asyncio.run(main())

Constructor options

gen = Generator(
    network_id=NetworkId("mainnet"),
    entries=utxos,                 # list[UtxoEntry] OR a UtxoContext
    change_address=my_addr,
    outputs=[PaymentOutput(recipient, amount)],

    # optional
    payload=b"optional-data",      # OP_RETURN-equivalent payload
    priority_fee=1000,             # extra fee in sompi
    priority_entries=priority,     # UTXOs to consume first
    sig_op_count=1,                # signature ops per input
    minimum_signatures=1,          # for multisig mass estimation
    fee_rate=2.0,                  # explicit sompi/gram override
)

entries accepts a UtxoContext directly — pass the context and it consumes from the mature set without you copying the list.

Estimate before signing

Two entry points, same answer. Use gen.estimate() if you already have a Generator; use estimate_transactions() to quote a hypothetical send without constructing one.

# You already have a Generator — most common case.
summary = gen.estimate()
print(summary.fees, summary.transactions, summary.utxos)

# Standalone — no Generator yet.
from kaspa import estimate_transactions

summary = estimate_transactions(
    network_id="mainnet",
    entries=utxos,
    change_address=my_addr,
    outputs=[{"address": recipient, "amount": amount}],
)

estimate() doesn't consume the generator — you can iterate it for real afterwards.

Pending transactions

Each yielded item exposes the proposed transaction's metadata:

for pending in gen:
    print(pending.id, pending.fee_amount, pending.mass)
    print(pending.payment_amount, pending.change_amount, pending.transaction_type)
    inputs = pending.get_utxo_entries()
    addrs = pending.addresses()
    raw_tx = pending.transaction

Use this to surface fee / mass to a user before they authorize a signature.

Signing

The everyday path: sign every input with one or more keys.

pending.sign([key])
pending.sign([key1, key2, key3])     # multisig

Advanced: per-input and custom scripts

Reach for these only when single-call signing isn't enough — usually mixed-key sets, hardware-signer integrations, or non-standard scripts. See examples/transactions/multisig.py for a full multisig run.

SighashType selects which parts of the transaction the signature commits to:

from kaspa import SighashType

# Per-input control with different keys
for i, _ in enumerate(pending.get_utxo_entries()):
    pending.sign_input(i, key)

# Custom signature scripts
sig = pending.create_input_signature(
    input_index=0,
    private_key=key,
    sighash_type=SighashType.All,
)
pending.fill_input(0, custom_script_bytes)

Submit

tx_id = await pending.submit(client)

# Or manually:
result = await client.submit_transaction({
    "transaction": pending.transaction,
    "allowOrphan": False,
})

pending.submit(client) is the right path. The manual route is for round-tripping the transaction through another system before submission. See Transactions → Submission for the allowOrphan semantics and confirmation states.

One-shot helpers

Two free functions for when the loop-and-submit pattern is more code than you need:

from kaspa import create_transaction, create_transactions

# Singular — one transaction.
tx = create_transaction(
    utxo_entry_source=utxos,
    outputs=[{"address": "kaspa:...", "amount": 100_000_000}],   # 1 KAS
    priority_fee=1000,
)

# Plural — Generator-equivalent.
result = create_transactions(
    network_id="mainnet",
    entries=utxos,
    change_address=my_addr,
    outputs=[{"address": "kaspa:...", "amount": 100_000_000}],   # 1 KAS
    priority_fee=1000,
)
for pending in result["transactions"]:
    pending.sign([key])
    await pending.submit(client)

print(result["summary"])

Where to next