Skip to content

Transactions

A Kaspa transaction has the same shape as on any UTXO chain: a list of inputs (each spending a previous output), a list of outputs, and a few metadata fields. The SDK exposes the underlying types — Transaction, TransactionInput, TransactionOutput, TransactionOutpoint, UtxoEntryReference — and helpers that build, sign, mass, and serialise them.

Most callers don't need this section

The Transaction Generator (and the managed Wallet (see Wallet) on top of it) handles UTXO selection, mass, signing, and submission for you. Reach for the primitives below only when you need custom lockup scripts, exact input ordering, payload data, or offline signing.

Anatomy

Transaction
  version, lock_time, subnetwork_id, gas, payload, mass
  inputs:  [TransactionInput, ...]
  outputs: [TransactionOutput, ...]

TransactionInput
  previous_outpoint: TransactionOutpoint(transaction_id, index)
  signature_script  (filled at sign time)
  sequence
  sig_op_count
  utxo: UtxoEntryReference

TransactionOutput
  value (sompi)
  script_public_key (lockup script)

What sets Kaspa apart from a Bitcoin-shaped chain:

  • Inputs carry their own UTXO context via UtxoEntryReference, so the signer doesn't have to re-fetch the spent output for its amount and lockup. See Inputs.
  • Mass replaces "byte size × rate" as the fee model. Compute mass on the transaction (including a storage component derived from input and output values), then multiply by the prevailing fee rate. See Mass & fees.
  • The atomic unit is the sompi: 1 KAS = 100_000_000 sompi. Every amount in the transaction surface is a sompi int. See kaspa_to_sompi and sompi_to_kaspa.

End-to-end (manual path)

This walks the manual flow using sign_transaction, pay_to_address_script, and update_transaction_mass:

from kaspa import (
    Transaction, TransactionInput, TransactionOutput, TransactionOutpoint,
    UtxoEntryReference, sign_transaction, pay_to_address_script,
    update_transaction_mass,
)

resp = await client.get_utxos_by_addresses({"addresses": [my_address]})
my_utxos = resp["entries"]

inputs = [
    TransactionInput(
        previous_outpoint=TransactionOutpoint(
            transaction_id=u["outpoint"]["transactionId"],
            index=u["outpoint"]["index"],
        ),
        signature_script="",          # filled at sign time
        sequence=0,
        sig_op_count=1,
        utxo=UtxoEntryReference(u),
    )
    for u in my_utxos
]

outputs = [
    TransactionOutput(value=amount,        script_public_key=pay_to_address_script(recipient)),
    TransactionOutput(value=change_amount, script_public_key=pay_to_address_script(change_addr)),
]

tx = Transaction(
    version=0, inputs=inputs, outputs=outputs,
    lock_time=0,
    subnetwork_id="0000000000000000000000000000000000000000",
    gas=0, payload="", mass=0,
)

update_transaction_mass("mainnet", tx)              # mass is signed over — fill before signing
signed = sign_transaction(tx, [private_key], verify_sig=True)

await client.submit_transaction({
    "transaction": signed,
    "allowOrphan": False,
})

This is what the Generator (see Transaction Generator) does internally — it picks UTXOs, computes mass, signs, and yields one or more ready-to-submit PendingTransactions.