Skip to content

WebSocket Subscriptions

Ashen exposes a lightweight WebSocket endpoint for real-time notifications. It emits finalized block updates and storage change events from block execution. Use it to keep UIs and indexers in sync without polling.

Connect to the same host and port as the HTTP RPC server:

  • ws://<rpc-host>/ws
  • wss://<rpc-host>/ws (if your node is behind TLS)

The WebSocket route is not rate-limited and is not behind RPC auth. Operators should protect it at the network or reverse-proxy layer if needed.

Client messages are JSON objects with a method and params field:

{"method":"subscribe","params":{"event":"newBlock"}}
{"method":"unsubscribe","params":{"event":"newBlock"}}

Client request field names use camelCase (e.g. txHash, keyPrefix).

Server messages are JSON objects with a type field:

{"type":"subscribed","event":"newBlock","success":true}

Payload fields in notifications use snake_case (e.g. tx_hash, block_height).

Errors are sent as:

{"type":"error","message":"Subscription limit reached (100)"}

Emitted when a block is finalized (commit reached). Payload:

{
"type":"newBlock",
"hash":"0x...",
"height":123,
"timestamp":1700000000000,
"tx_count":42
}

Emitted for each storage write recorded during block execution. Payload:

{
"type":"storageChange",
"contract":"0xabc123...",
"key":"0xdeadbeef...",
"value":"0x01...",
"block_height":123,
"block_hash":"0x..."
}

value is omitted if the key was deleted. contract and key are hex-encoded byte strings (with or without a 0x prefix).

storageChange subscriptions require a contract address and support three filters:

  • Exact key (default): keyPrefix: false
  • Prefix: keyPrefix: true
  • All keys for a contract: omit key

If contract is missing, the server replies with an error message and does not add the subscription.

Examples:

{"method":"subscribe","params":{"event":"storageChange","contract":"0xabc...","key":"0x00ff"}}
{"method":"subscribe","params":{"event":"storageChange","contract":"0xabc...","key":"0x00","keyPrefix":true}}
{"method":"subscribe","params":{"event":"storageChange","contract":"0xabc..."}}

Defined for per-transaction status updates with these states: pending, included, finalized, failed, dropped.

Example payload shape:

{
"type":"txStatus",
"tx_hash":"0x...",
"status":"included",
"block_height":123,
"block_hash":"0x..."
}

Note: the node currently does not emit txStatus notifications. Use tx_by_hash / tx_receipt over HTTP for now.

  • newBlock is a single subscription per connection.
  • txStatus expects a txHash per subscription; omit to unsubscribe all (omit on subscribe and no events will match).
  • storageChange expects contract on subscribe; omit to unsubscribe all storage filters.

Unsubscribe examples:

{"method":"unsubscribe","params":{"event":"txStatus","txHash":"0x..."}}
{"method":"unsubscribe","params":{"event":"storageChange","contract":"0xabc...","key":"0x00","keyPrefix":true}}
  • Max 100 subscriptions per connection (across all event types).
  • The server sends periodic ping frames (every 30s) and closes the connection if no pong arrives within 10s.
  • Notifications are best-effort. If a client falls behind, messages may be dropped. Reconnect and backfill using HTTP RPC.

When metrics are enabled, the node exports:

  • ws_active_connections
  • ws_messages_sent_total

See /metrics on the node HTTP server.

const ws = new WebSocket("ws://127.0.0.1:8080/ws");
ws.onopen = () => {
ws.send(JSON.stringify({
method: "subscribe",
params: { event: "newBlock" },
}));
};
ws.onmessage = (event) => {
console.log("ws message", event.data);
};
  • /reference/rpc-api/ for HTTP RPC methods
  • /guides/tx-simulation/ for simulation endpoints used by indexers