Skip to content

UTXO Processor

A UtxoProcessor is the engine that drives live UTXO tracking: it owns the wRPC subscription and dispatches address-scoped UTXO events to one or more UtxoContexts (see UTXO Context). The managed Wallet (see Wallet) builds one internally; otherwise, you build one and bind contexts to it.

Read this page first, then UTXO Context.

Construction

Build a UtxoProcessor on top of an RpcClient and a NetworkId:

from kaspa import NetworkId, Resolver, RpcClient, UtxoProcessor

client = RpcClient(resolver=Resolver(), network_id="testnet-10")
await client.connect()

processor = UtxoProcessor(client, NetworkId("testnet-10"))
await processor.start()

# ...later...
await processor.stop()
await client.disconnect()

start() runs a short handshake against the node:

  1. Calls get_server_info, validates RPC version + network match, bails with UtxoIndexNotEnabled if the node has no UTXO index.
  2. Opens a single wRPC listener and subscribes to VirtualDaaScoreChanged only — no UtxosChanged subscription yet. UTXO subscriptions are opened later, per-address, when a UtxoContext calls track_addresses(...).
  3. Emits utxo-proc-start.

stop() is the matching shutdown. Without start(), bound contexts stay empty.

Properties

Property Meaning
processor.rpc The RpcClient it's reading from.
processor.network_id The NetworkId it was constructed with.
processor.is_active True after start(), False after stop() or before.

Events

The processor has its own event surface — a smaller, lower-level cousin of the managed Wallet's events.

def on_event(event):
    print(event["type"], event.get("data"))

# Single event
processor.add_event_listener("maturity", on_event)

# A list of events — supported here (the wallet's listener takes one
# event at a time)
processor.add_event_listener(
    ["utxo-proc-start", "utxo-proc-stop", "pending", "maturity",
     "reorg", "stasis", "discovery", "balance", "utxo-proc-error", "error"],
    on_event,
)

# Every event
processor.add_event_listener("all", on_event)
Event When it fires
utxo-proc-start / utxo-proc-stop Processor entered / left the active state.
pending A UTXO landed for a tracked address but isn't mature yet.
maturity A previously-pending UTXO crossed the maturity depth.
reorg, stasis A UTXO was unwound or coinbase-locked.
discovery A scan-time discovery hit.
balance A bound UtxoContext's balance changed.
utxo-proc-error, error Something went wrong.

UtxoProcessorEvent is the enum form if you prefer typed values over kebab strings.

Reconnects

The underlying RpcClient reconnects automatically when the WebSocket drops. The processor reacts:

  • On disconnect: emits utxo-proc-stop. Bound contexts stay alive — their address sets and in-memory UTXOs are kept.
  • On the next successful reconnect: re-runs the handshake, re-opens the DAA-score subscription, and re-registers UtxosChanged for every address that's still tracked across all bound contexts. Re-emits utxo-proc-start.

You don't need to rebuild contexts on reconnect. Gate work that needs fresh state on processor.is_active (or on utxo-proc-start).

Coordinating with asyncio

Listener callbacks may run on a background thread. To signal an asyncio.Event from one, bridge through the loop:

loop = asyncio.get_running_loop()
got_start = asyncio.Event()

def on_event(event):
    if event.get("type") == "utxo-proc-start":
        loop.call_soon_threadsafe(got_start.set)

processor.add_event_listener("utxo-proc-start", on_event)
await processor.start()
await got_start.wait()

This is the same pattern the managed Wallet uses internally.

Where to next