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:
- Calls
get_server_info, validates RPC version + network match, bails withUtxoIndexNotEnabledif the node has no UTXO index. - Opens a single wRPC listener and subscribes to
VirtualDaaScoreChangedonly — noUtxosChangedsubscription yet. UTXO subscriptions are opened later, per-address, when aUtxoContextcallstrack_addresses(...). - 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
UtxosChangedfor every address that's still tracked across all bound contexts. Re-emitsutxo-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¶
- UTXO Context — bind a context to this processor.
- Transaction Generator — pass a bound context as
entries. - Wallet → Sync State — the same handshake
surfaced one level up, with the full
SyncStatepayload reference.