Skip to content

WebSocket

The SDK provides real-time event subscriptions via WebSocket.

Connecting

import { createClient } from "@kaizen-core/sdk";
 
const client = createClient({
  rpcUrl: "http://localhost:8546", // Via proxy (recommended)
  wsUrl: "ws://localhost:8546/ws",
});
 
// Or connect directly to a node:
// rpcUrl: "http://localhost:8545",   // write-node
// wsUrl: "ws://localhost:8545/ws",
// rpcUrl: "http://localhost:8547",   // read-node-aggressive
// wsUrl: "ws://localhost:8547/ws",
 
// Connect to WebSocket
await client.connectWebSocket();
 
// Check connection status
if (client.isWebSocketConnected) {
  console.log("WebSocket connected!");
}
 
// Disconnect when done
client.disconnectWebSocket();

Subscriptions

All subscription methods return an unsubscribe function.

Subscribe to Blocks

const unsubscribe = client.subscribeBlocks((event) => {
  if (event.type === "block") {
    console.log({
      height: event.block.height,
      timestamp: event.block.timestamp,
      txCount: event.block.txCount,
    });
  }
});
 
// Later: stop receiving updates
unsubscribe();

Subscribe to User Theses

const unsubscribe = client.subscribeUserTheses("0xUserAddress...", (event) => {
  if (event.type === "thesis") {
    console.log({
      thesisId: event.thesis.id,
      status: event.thesis.status,
      eventType: event.eventType, // "created" | "active" | "settled" | "cancelled"
    });
  }
});

Subscribe to My Theses

Convenience method that uses the connected signer's address.

client.connectSigner("0x...");
 
const unsubscribe = client.subscribeMyTheses((event) => {
  if (event.type === "thesis") {
    console.log(`My thesis ${event.thesis.id}: ${event.eventType}`);
  }
});

Subscribe to Solver Theses

For solver implementations.

const unsubscribe = client.subscribeSolverTheses(
  "0xSolverAddress...",
  (event) => {
    if (event.type === "thesis") {
      // Handle theses where this solver is involved
    }
  }
);

Subscribe to Oracle Prices

const unsubscribe = client.subscribeOraclePrices(
  {
    base: "0x0000000000000000000000000000000000000001", // BTC
    quote: "0x0000000000000000000000000000000000000002", // USDC
  },
  (event) => {
    if (event.type === "oraclePrice") {
      console.log({
        price: event.price.price,
        timestamp: event.price.timestamp,
      });
    }
  }
);

Using WebSocketClient Directly

For more control, use WebSocketClient directly.

import { WebSocketClient } from "@kaizen-core/sdk";
 
const ws = new WebSocketClient("ws://localhost:8546/ws");
 
// Set event handlers
ws.setOnOpen(() => {
  console.log("Connected!");
});
 
ws.setOnError((error) => {
  console.error("WebSocket error:", error);
});
 
ws.setOnClose((code, reason) => {
  console.log(`Disconnected: ${code} - ${reason}`);
});
 
// Connect
await ws.connect();
 
// Subscribe with custom channel/params
const unsubscribe = ws.subscribe("userTheses", (event) => console.log(event), {
  address: "0x...",
});
 
// Check state
console.log(`Connected: ${ws.isConnected}`);
console.log(`Subscriptions: ${ws.subscriptionCount}`);
 
// Disconnect
ws.disconnect();

Debug Mode

Enable debug logging to troubleshoot WebSocket issues:

import { WebSocketClient } from "@kaizen-core/sdk";
 
// Enable debug logging at construction
const ws = new WebSocketClient("ws://localhost:3030/ws", {
  debug: "verbose", // "none" | "info" | "verbose"
});
 
// Or change at runtime
ws.setDebugLevel("info");
 
// Check connection stats
const stats = ws.getStats();
console.log(stats);
// {
//   messagesReceived: 42,
//   bytesReceived: 12345,
//   deserializeErrors: 0,
//   lastMessageAt: 1701234567890
// }
 
// Reset stats
ws.resetStats();

Debug Levels

LevelDescription
noneNo logging (default)
infoConnection events, subscriptions, errors
verboseAll messages with hex dumps for troubleshooting

Custom Logger

const ws = new WebSocketClient("ws://localhost:8546/ws", {
  debug: "verbose",
  logger: {
    info: (msg, data) => myLogger.info(`[WS] ${msg}`, data),
    verbose: (msg, data) => myLogger.debug(`[WS] ${msg}`, data),
    error: (msg, error) => myLogger.error(`[WS] ${msg}`, error),
  },
});

Binary Protocol

WebSocket messages use Borsh binary encoding for efficient data transfer. The SDK handles serialization/deserialization automatically - you always work with typed JavaScript objects.

:::info Why Binary?

  • Smaller payload: ~40-60% smaller than JSON for typical messages
  • Faster parsing: No string parsing overhead
  • Type-safe: Schema enforced at protocol level :::

If you need to inspect raw messages for debugging, use verbose debug mode which logs hex dumps.

Event Types

import type {
  WebSocketEvent,
  BlockEvent,
  ThesisEvent,
  OraclePriceEvent,
} from "@kaizen-core/sdk";
 
// Type guard for events
function handleEvent(event: WebSocketEvent) {
  switch (event.type) {
    case "block":
      handleBlock(event);
      break;
    case "thesis":
      handleThesis(event);
      break;
    case "oraclePrice":
      handlePrice(event);
      break;
  }
}
 
function handleBlock(event: BlockEvent) {
  const { height, timestamp, stateRoot, txCount, transactions } = event.block;
  // ...
}
 
function handleThesis(event: ThesisEvent) {
  const { id, user, solver, status, betAmount, payout } = event.thesis;
  const eventType = event.eventType; // "created" | "active" | ...
  // ...
}
 
function handlePrice(event: OraclePriceEvent) {
  const { base, quote, price, timestamp } = event.price;
  // ...
}

Subscription Channels

ChannelParametersDescription
newBlocksNoneNew block notifications
newTransactions{ address } (opt)All or address-filtered TX updates
userTheses{ address }Theses for a specific user
solverTheses{ address }Theses for a specific solver
oraclePrices{ base, quote } (opt)All or pair-specific price updates
allEventsNoneAll events (use sparingly)

Automatic Reconnection

The WebSocket client automatically handles reconnection with exponential backoff:

  • Max reconnection attempts: 5
  • Base delay: 1 second
  • Backoff: 2^attempt seconds (1s, 2s, 4s, 8s, 16s)

Subscriptions are automatically restored after reconnection.

Example: Real-time Thesis Monitoring

import { createClient } from "@kaizen-core/sdk";
 
async function monitorTheses() {
  const client = createClient({
    rpcUrl: "http://localhost:8546",
    wsUrl: "ws://localhost:8546/ws",
  });
 
  client.connectSigner("0x...");
  await client.connectWebSocket();
 
  // Track thesis states
  const thesisStates = new Map<bigint, string>();
 
  client.subscribeMyTheses((event) => {
    if (event.type === "thesis") {
      const { id, status } = event.thesis;
      const prevStatus = thesisStates.get(id);
 
      if (prevStatus !== status) {
        console.log(`Thesis ${id}: ${prevStatus ?? "new"} -> ${status}`);
        thesisStates.set(id, status);
 
        // Take action based on status change
        if (status === "active") {
          console.log("Thesis is now active, monitoring price...");
        } else if (status === "settled") {
          console.log("Thesis settled!");
        }
      }
    }
  });
 
  // Keep running
  process.on("SIGINT", () => {
    client.disconnectWebSocket();
    process.exit(0);
  });
}
 
monitorTheses();

Example: Price Alert

import { createClient, parsePrice, formatPrice } from "@kaizen-core/sdk";
 
async function priceAlert(targetPrice: bigint) {
  const client = createClient({
    rpcUrl: "http://localhost:8546",
    wsUrl: "ws://localhost:8546/ws",
  });
 
  await client.connectWebSocket();
 
  const BTC = "0x0000000000000000000000000000000000000001";
  const USDC = "0x0000000000000000000000000000000000000002";
 
  const unsubscribe = client.subscribeOraclePrices(
    { base: BTC, quote: USDC },
    (event) => {
      if (event.type === "oraclePrice") {
        const currentPrice = event.price.price;
 
        if (currentPrice >= targetPrice) {
          console.log(
            `🎯 Price alert! BTC reached ${formatPrice(currentPrice)}`
          );
          unsubscribe();
          client.disconnectWebSocket();
        }
      }
    }
  );
 
  console.log(`Waiting for BTC to reach ${formatPrice(targetPrice)}...`);
}
 
priceAlert(parsePrice("100000.00"));