Subscriptions¶
Subscriptions let
RpcClient receive a live feed
of node events. The node pushes events as they happen; the client
invokes the callbacks you registered.
The two-step pattern¶
Every subscription has two parts:
- A listener — a Python callback registered via
add_event_listener("<event>", callback). - A subscription —
await client.subscribe_<event>(...)tells the node to start streaming.
Both halves are required. A listener with no subscription receives nothing; a subscription with no listener silently drops events.
def on_utxo_change(event):
print("UTXO change:", event)
client.add_event_listener("utxos-changed", on_utxo_change)
await client.subscribe_utxos_changed([Address("kaspa:qz...")])
Available events¶
Each subscribe_* has a matching unsubscribe_* with the same
argument shape.
| Event name | Subscribe call | Arguments | Event payload |
|---|---|---|---|
utxos-changed |
subscribe_utxos_changed |
addresses: list[Address] |
UtxosChangedEvent |
block-added |
subscribe_block_added |
— | BlockAddedEvent |
virtual-chain-changed |
subscribe_virtual_chain_changed |
include_accepted_transaction_ids: bool |
VirtualChainChangedEvent |
virtual-daa-score-changed |
subscribe_virtual_daa_score_changed |
— | VirtualDaaScoreChangedEvent |
sink-blue-score-changed |
subscribe_sink_blue_score_changed |
— | SinkBlueScoreChangedEvent |
finality-conflict |
subscribe_finality_conflict |
— | FinalityConflictEvent |
finality-conflict-resolved |
subscribe_finality_conflict_resolved |
— | FinalityConflictResolvedEvent |
new-block-template |
subscribe_new_block_template |
— | NewBlockTemplateEvent |
pruning-point-utxo-set-override |
subscribe_pruning_point_utxo_set_override |
— | PruningPointUtxoSetOverrideEvent |
Event names also map to the
NotificationEvent enum
if you prefer typed variants over kebab-case strings.
The client also emits two control events that don't require a
subscribe_* call — just register a listener:
| Event name | Fires when | Event payload |
|---|---|---|
connect |
The WebSocket has connected (including after a reconnect). | ConnectEvent |
disconnect |
The WebSocket has dropped. | DisconnectEvent |
Use these to track connection state without polling client.is_connected.
Listening to all events¶
Pass the special "all" event name (or NotificationEvent.All) to
register one callback for every notification — node-pushed events plus
connect / disconnect. You still need subscribe_* for any
node-pushed event you want to receive; "all" only multiplexes
delivery, it doesn't subscribe on your behalf.
def on_any(event):
print(event["type"], event)
client.add_event_listener("all", on_any)
await client.subscribe_block_added()
await client.subscribe_virtual_daa_score_changed()
Event payload shape¶
Every callback receives a dict with a "type" key naming the
event. The remaining keys depend on the event.
utxos-changed¶
UtxosChangedEvent
is the only event that does not nest its body under "data" — it's
flattened so callbacks can read event["added"] directly. The
"added" and "removed" lists hold
RpcUtxosByAddressesEntry
items.
The "type" value is the PascalCase variant name ("UtxosChanged",
"BlockAdded", …) — the kebab-case form ("utxos-changed",
"block-added", …) is only used when registering listeners.
{
"type": "UtxosChanged",
"added": [
{
"address": "kaspa:qz...",
"outpoint": {"transactionId": "...", "index": 0},
"utxoEntry": {
"amount": 100000000,
"scriptPublicKey": {"version": 0, "script": "..."},
"blockDaaScore": 123456789,
"isCoinbase": False,
},
},
],
"removed": [],
}
All other node-pushed events¶
A "data" key holds the notification body. Each event has a wrapper
TypedDict (e.g.
BlockAddedEvent) and
a body TypedDict (e.g.
RpcBlockAddedNotification).
See the Available events table for the full list.
For example, a virtual-daa-score-changed callback receives:
connect / disconnect¶
ConnectEvent and
DisconnectEvent carry
a single "rpc" key holding the node URL as a string:
The bundled
Notification class wraps
notifications internally; for callback type hints, use the per-event
TypedDicts above.
Examples¶
Watching addresses for UTXO changes¶
Pass Address instances (or
strings parsed by Address(...)):
from kaspa import Address
addresses = [Address("kaspa:qz...")]
def on_change(event):
for added in event.get("added", []):
print("+", added["utxoEntry"]["amount"])
for removed in event.get("removed", []):
print("-", removed["utxoEntry"]["amount"])
client.add_event_listener("utxos-changed", on_change)
await client.subscribe_utxos_changed(addresses)
# ... later ...
await client.unsubscribe_utxos_changed(addresses)
To watch a managed-wallet account instead of raw addresses, use the
Wallet's Balance and Maturity events — see
Wallet → Events.
Block events¶
def on_block(event):
print("new block:", event["data"]["block"]["header"]["hash"])
client.add_event_listener("block-added", on_block)
await client.subscribe_block_added()
Virtual chain progression¶
def on_chain(event):
data = event["data"]
print("added:", data["addedChainBlockHashes"])
print("removed:", data["removedChainBlockHashes"])
client.add_event_listener("virtual-chain-changed", on_chain)
await client.subscribe_virtual_chain_changed(include_accepted_transaction_ids=True)
With include_accepted_transaction_ids=True, the payload doubles as a
confirmation feed — every accepted transaction id appears in
event["data"]["acceptedTransactionIds"]. For the one-shot equivalent,
see get_virtual_chain_from_block.
Connection state¶
def on_connect(event):
print("connected to", event["rpc"])
def on_disconnect(event):
print("disconnected from", event["rpc"])
client.add_event_listener("connect", on_connect)
client.add_event_listener("disconnect", on_disconnect)
No subscribe_* is needed — the client emits these itself when the
WebSocket transitions.
Listener bookkeeping¶
client.add_event_listener("block-added", callback) # add
client.add_event_listener("block-added", callback, extra) # forward extra arg
client.remove_event_listener("block-added", callback) # remove specific
client.remove_event_listener("block-added") # remove all for event
client.remove_all_event_listeners() # remove all globally
Listeners outlive a single subscription cycle. Re-subscribing after an
unsubscribe does not re-fire previously delivered events. To catch
up, do a one-shot
get_utxos_by_addresses (or equivalent)
before re-subscribing.
Where to next¶
- Calls — the request/response side of the API.
RpcClient— fullsubscribe_*/unsubscribe_*/add_event_listenerreference.UtxoProcessor(see UTXO Processor) andUtxoContext(see UTXO Context) — higher-level UTXO tracking built onutxos-changed.- Wallet → Events — the managed
Wallet's higher-level event surface.