Skip to main content

HoosatEventManager API Reference

Complete API reference for HoosatEventManager - real-time event streaming and WebSocket management.

Overview

HoosatEventManager provides real-time blockchain event streaming via gRPC:

  • UTXO changes: Monitor address balance changes
  • Block notifications: Track new blocks
  • Virtual DAG changes: Monitor consensus updates
  • Automatic reconnection: Handle network interruptions
  • Multi-subscription: Subscribe to multiple event types

Accessing the Event Manager

The event manager is accessed through HoosatClient:

const client = new HoosatClient({
host: '54.38.176.95',
port: 42420
});

// Access event manager
const events = client.events;

Configuration

Event Manager Config

interface EventManagerConfig {
autoReconnect?: boolean; // Auto-reconnect on disconnect (default: true)
reconnectDelay?: number; // Delay between reconnect attempts in ms (default: 5000)
maxReconnectAttempts?: number; // Max reconnection attempts (default: Infinity)
debug?: boolean; // Enable debug logging (default: false)
}

const client = new HoosatClient({
host: '54.38.176.95',
port: 42420,
eventManagerConfig: {
autoReconnect: true,
reconnectDelay: 5000,
maxReconnectAttempts: 10,
debug: false
}
});

Subscriptions

subscribeToUtxoChanges(addresses: string[])

Subscribe to UTXO changes for specific addresses.

Parameters:

  • addresses - Array of Hoosat addresses to monitor

Returns: Promise<void>

Example:

// Subscribe to single address
await client.events.subscribeToUtxoChanges([
'hoosat:qz7ulu8z6sj9m7pdwm0g4tyjd3j2pycnf2q9nly9zmvnr6uqxdv4jqqruch02'
]);

// Subscribe to multiple addresses
await client.events.subscribeToUtxoChanges([
'hoosat:qz7ulu8z6sj9m7pdwm0g4tyjd3j2pycnf2q9nly9zmvnr6uqxdv4jqqruch02',
'hoosat:qz95mwas8ja7ucsernv9z335rdxxqswff7wvzenl29qukn5qs3lsqfsa4pd74',
'hoosat:qzk8h2q7wn9p3j5m6x4r8t5v3w9y2k4m7p8q6r9t3v5w8x2z4b7c9d'
]);

// Listen for changes
client.events.on(EventType.UtxoChange, (notification) => {
console.log('UTXO changed for:', notification.address);
console.log('Added:', notification.added.length);
console.log('Removed:', notification.removed.length);
});

Event payload:

interface UtxoChangeNotification {
address: string;
added: UtxoEntry[];
removed: UtxoEntry[];
}

unsubscribeFromUtxoChanges(addresses: string[])

Unsubscribe from UTXO changes.

Parameters:

  • addresses - Array of addresses to stop monitoring

Returns: Promise<void>

Example:

await client.events.unsubscribeFromUtxoChanges([
'hoosat:qz7ulu8z6sj9m7pdwm0g4tyjd3j2pycnf2q9nly9zmvnr6uqxdv4jqqruch02'
]);

console.log('Unsubscribed from address');

subscribeToBlockAdded()

Subscribe to new block notifications.

Returns: Promise<void>

Example:

await client.events.subscribeToBlockAdded();

client.events.on(EventType.BlockAdded, (notification) => {
console.log('New block added!');
console.log('Hash:', notification.blockHash);
console.log('Timestamp:', notification.blockHeader.timestamp);
console.log('DAA Score:', notification.blockHeader.daaScore);
});

Event payload:

interface BlockAddedNotification {
blockHash: string;
blockHeader: {
version: number;
hashMerkleRoot: string;
acceptedIdMerkleRoot: string;
utxoCommitment: string;
timestamp: string;
bits: number;
nonce: string;
daaScore: string;
blueScore: string;
blueWork: string;
pruningPoint: string;
};
}

unsubscribeFromBlockAdded()

Unsubscribe from block notifications.

Returns: Promise<void>

Example:

await client.events.unsubscribeFromBlockAdded();

subscribeToVirtualDagChanged()

Subscribe to virtual DAG changes.

Returns: Promise<void>

Example:

await client.events.subscribeToVirtualDagChanged();

client.events.on(EventType.VirtualDagChanged, (notification) => {
console.log('Virtual DAG changed');
// Trigger balance refresh or other actions
});

unsubscribeFromVirtualDagChanged()

Unsubscribe from virtual DAG changes.

Returns: Promise<void>

unsubscribeFromAll()

Unsubscribe from all events and close connection.

Returns: Promise<void>

Example:

// Cleanup on shutdown
process.on('SIGINT', async () => {
console.log('Shutting down...');
await client.events.unsubscribeFromAll();
client.disconnect();
process.exit(0);
});

Event Listeners

on(eventType: EventType, callback: Function)

Register event listener.

Parameters:

  • eventType - Type of event to listen for
  • callback - Function to call when event occurs

Returns: void

Event Types:

enum EventType {
UtxoChange = 'utxo_change',
BlockAdded = 'block_added',
VirtualDagChanged = 'virtual_dag_changed',
Connected = 'connected',
Disconnected = 'disconnected',
Reconnecting = 'reconnecting',
Error = 'error'
}

Examples:

// UTXO changes
client.events.on(EventType.UtxoChange, (notification) => {
console.log('UTXO change:', notification);
});

// Block notifications
client.events.on(EventType.BlockAdded, (notification) => {
console.log('New block:', notification.blockHash);
});

// Connection events
client.events.on('connected', () => {
console.log('Connected to node');
});

client.events.on('disconnected', () => {
console.log('Disconnected from node');
});

client.events.on('reconnecting', (attempt: number) => {
console.log(`Reconnecting... attempt ${attempt}`);
});

client.events.on('error', (error) => {
console.error('Event stream error:', error);
});

off(eventType: EventType, callback?: Function)

Remove event listener.

Parameters:

  • eventType - Type of event
  • callback - Specific callback to remove (optional)

Returns: void

Examples:

// Remove specific handler
const handler = (notification) => {
console.log('UTXO changed');
};

client.events.on(EventType.UtxoChange, handler);

// Later...
client.events.off(EventType.UtxoChange, handler);

// Remove all handlers for event type
client.events.off(EventType.UtxoChange);

once(eventType: EventType, callback: Function)

Register one-time event listener.

Parameters:

  • eventType - Type of event
  • callback - Function to call once

Returns: void

Example:

// Wait for single block
client.events.once(EventType.BlockAdded, (notification) => {
console.log('First block received:', notification.blockHash);
// Handler is automatically removed after this
});

Connection Management

connect()

Manually connect to event stream.

Returns: Promise<void>

Example:

try {
await client.events.connect();
console.log('Connected to event stream');
} catch (error) {
console.error('Connection failed:', error);
}

Note: Connection is automatically established when subscribing to events.

disconnect()

Disconnect from event stream.

Returns: void

Example:

client.events.disconnect();
console.log('Disconnected');

reconnect()

Manually trigger reconnection.

Returns: Promise<void>

Example:

try {
await client.events.reconnect();
console.log('Reconnected');
} catch (error) {
console.error('Reconnection failed:', error);
}

isConnected()

Check connection status.

Returns: boolean - True if connected

Example:

if (client.events.isConnected()) {
console.log('Event stream is connected');
} else {
console.log('Event stream is disconnected');
}

Complete Examples

Balance Monitor

import {
HoosatClient,
HoosatCrypto,
HoosatUtils,
EventType
} from 'hoosat-sdk';

async function monitorBalance() {
const client = new HoosatClient({
host: '54.38.176.95',
port: 42420
});

const wallet = HoosatCrypto.importKeyPair(
process.env.WALLET_PRIVATE_KEY!,
'mainnet'
);

console.log('Monitoring:', wallet.address);

// Get initial balance
const balanceResult = await client.getBalance(wallet.address);
let currentBalance = balanceResult.ok
? BigInt(balanceResult.result.balance)
: 0n;

console.log('Initial balance:', HoosatUtils.sompiToAmount(currentBalance), 'HTN\n');

// Subscribe to changes
await client.events.subscribeToUtxoChanges([wallet.address]);

// Handle UTXO changes
client.events.on(EventType.UtxoChange, (notification) => {
console.log('[UTXO Change]', new Date().toISOString());

let netChange = 0n;

// Process added UTXOs
for (const utxo of notification.added) {
const amount = BigInt(utxo.utxoEntry.amount);
netChange += amount;

console.log(` + ${HoosatUtils.sompiToAmount(amount)} HTN`);
console.log(` TX: ${utxo.outpoint.transactionId}`);
}

// Process removed UTXOs
for (const utxo of notification.removed) {
const amount = BigInt(utxo.utxoEntry.amount);
netChange -= amount;

console.log(` - ${HoosatUtils.sompiToAmount(amount)} HTN`);
console.log(` TX: ${utxo.outpoint.transactionId}`);
}

// Update balance
currentBalance += netChange;

console.log(` New balance: ${HoosatUtils.sompiToAmount(currentBalance)} HTN`);
console.log();
});

// Handle connection events
client.events.on('connected', () => {
console.log('[Status] Connected');
});

client.events.on('disconnected', () => {
console.log('[Status] Disconnected');
});

client.events.on('reconnecting', (attempt: number) => {
console.log(`[Status] Reconnecting... attempt ${attempt}`);
});

client.events.on('error', (error) => {
console.error('[Error]', error.message);
});

// Graceful shutdown
process.on('SIGINT', async () => {
console.log('\nShutting down...');
await client.events.unsubscribeFromAll();
client.disconnect();
process.exit(0);
});

console.log('Monitoring started. Press Ctrl+C to stop.\n');
}

monitorBalance();

Payment Detection System

class PaymentDetector {
private client: HoosatClient;
private address: string;
private onPayment: (txId: string, amount: string) => void;

constructor(
client: HoosatClient,
address: string,
onPayment: (txId: string, amount: string) => void
) {
this.client = client;
this.address = address;
this.onPayment = onPayment;
}

async start(): Promise<void> {
// Subscribe to address
await this.client.events.subscribeToUtxoChanges([this.address]);

// Handle UTXO changes
this.client.events.on(EventType.UtxoChange, (notification) => {
if (notification.address === this.address) {
this.handleUtxoChange(notification);
}
});

console.log('Payment detector started for:', this.address);
}

private handleUtxoChange(notification: UtxoChangeNotification): void {
// Only process incoming payments (added UTXOs)
for (const utxo of notification.added) {
const amount = utxo.utxoEntry.amount;
const txId = utxo.outpoint.transactionId;

console.log('Payment detected!');
console.log('Amount:', HoosatUtils.sompiToAmount(amount), 'HTN');
console.log('TX ID:', txId);

// Trigger callback
try {
this.onPayment(txId, amount);
} catch (error) {
console.error('Payment callback error:', error);
}
}
}

async stop(): Promise<void> {
await this.client.events.unsubscribeFromAll();
console.log('Payment detector stopped');
}
}

// Usage
const detector = new PaymentDetector(
client,
merchantAddress,
(txId, amount) => {
console.log(`Processing payment of ${HoosatUtils.sompiToAmount(amount)} HTN`);

// Update order status
// Send confirmation email
// Trigger fulfillment
}
);

await detector.start();

Multi-Address Monitor

class MultiAddressMonitor {
private client: HoosatClient;
private addresses: Map<string, AddressInfo> = new Map();

constructor(client: HoosatClient) {
this.client = client;
}

addAddress(name: string, address: string): void {
this.addresses.set(address, {
name,
balance: 0n,
lastUpdate: new Date()
});
}

async start(): Promise<void> {
const addresses = Array.from(this.addresses.keys());

// Subscribe to all addresses
await this.client.events.subscribeToUtxoChanges(addresses);

// Get initial balances
for (const address of addresses) {
await this.refreshBalance(address);
}

// Handle changes
this.client.events.on(EventType.UtxoChange, (notification) => {
this.handleChange(notification);
});

console.log(`Monitoring ${addresses.length} addresses`);
this.printStatus();
}

private handleChange(notification: UtxoChangeNotification): void {
const info = this.addresses.get(notification.address);
if (!info) return;

let netChange = 0n;

for (const utxo of notification.added) {
netChange += BigInt(utxo.utxoEntry.amount);
}

for (const utxo of notification.removed) {
netChange -= BigInt(utxo.utxoEntry.amount);
}

info.balance += netChange;
info.lastUpdate = new Date();

console.log(`\n[${info.name}] Balance changed`);
this.printStatus();
}

private async refreshBalance(address: string): Promise<void> {
const result = await this.client.getBalance(address);

if (result.ok) {
const info = this.addresses.get(address)!;
info.balance = BigInt(result.result.balance);
info.lastUpdate = new Date();
}
}

private printStatus(): void {
console.log('\n=== Wallet Status ===');

let total = 0n;

for (const [address, info] of this.addresses) {
console.log(`${info.name}:`);
console.log(` Balance: ${HoosatUtils.sompiToAmount(info.balance)} HTN`);
console.log(` Last update: ${info.lastUpdate.toISOString()}`);

total += info.balance;
}

console.log(`\nTotal: ${HoosatUtils.sompiToAmount(total)} HTN`);
console.log('====================\n');
}

async stop(): Promise<void> {
await this.client.events.unsubscribeFromAll();
}
}

interface AddressInfo {
name: string;
balance: bigint;
lastUpdate: Date;
}

// Usage
const monitor = new MultiAddressMonitor(client);

monitor.addAddress('Main Wallet', 'hoosat:qz7ulu...');
monitor.addAddress('Savings', 'hoosat:qpm4n7...');
monitor.addAddress('Trading', 'hoosat:qzk8h2...');

await monitor.start();

Block Monitor with Statistics

class BlockMonitor {
private client: HoosatClient;
private blockCount: number = 0;
private startTime: Date = new Date();
private lastBlockTime: Date = new Date();

constructor(client: HoosatClient) {
this.client = client;
}

async start(): Promise<void> {
await this.client.events.subscribeToBlockAdded();

this.client.events.on(EventType.BlockAdded, (notification) => {
this.handleBlock(notification);
});

console.log('Block monitor started\n');
}

private handleBlock(notification: BlockAddedNotification): void {
this.blockCount++;
const now = new Date();

const timeSinceStart = (now.getTime() - this.startTime.getTime()) / 1000;
const timeSinceLastBlock = (now.getTime() - this.lastBlockTime.getTime()) / 1000;

this.lastBlockTime = now;

console.log('=== New Block ===');
console.log('Hash:', notification.blockHash.substring(0, 16) + '...');
console.log('Time:', now.toISOString());
console.log('DAA Score:', notification.blockHeader.daaScore);
console.log('Blue Score:', notification.blockHeader.blueScore);
console.log('Time since last:', timeSinceLastBlock.toFixed(2), 's');
console.log('\nStatistics:');
console.log('Total blocks:', this.blockCount);
console.log('Average rate:', (this.blockCount / timeSinceStart).toFixed(2), 'blocks/s');
console.log('=================\n');
}

async stop(): Promise<void> {
await this.client.events.unsubscribeFromAll();
}
}

// Usage
const monitor = new BlockMonitor(client);
await monitor.start();

Error Handling

Connection Errors

client.events.on('error', (error) => {
console.error('Event stream error:', error);

if (error.message.includes('ECONNREFUSED')) {
console.error('Node is not reachable');
} else if (error.message.includes('timeout')) {
console.error('Connection timeout');
}
});

// Handle reconnection failures
let reconnectAttempts = 0;

client.events.on('reconnecting', (attempt: number) => {
reconnectAttempts = attempt;
console.log(`Reconnect attempt ${attempt}`);

if (attempt > 5) {
console.error('Multiple reconnect failures - check node status');
}
});

Event Handler Errors

// Wrap handlers in try-catch
client.events.on(EventType.UtxoChange, async (notification) => {
try {
await processUtxoChange(notification);
} catch (error) {
console.error('Error processing UTXO change:', error);
// Log error but don't crash the event listener
}
});

Best Practices

1. Always Unsubscribe on Shutdown

process.on('SIGINT', async () => {
await client.events.unsubscribeFromAll();
client.disconnect();
process.exit(0);
});

process.on('SIGTERM', async () => {
await client.events.unsubscribeFromAll();
client.disconnect();
process.exit(0);
});

2. Limit Subscription Count

const MAX_ADDRESSES = 100;

if (addresses.length > MAX_ADDRESSES) {
console.warn(`Too many addresses, limiting to ${MAX_ADDRESSES}`);
addresses = addresses.slice(0, MAX_ADDRESSES);
}

await client.events.subscribeToUtxoChanges(addresses);

3. Handle Reconnection

client.events.on('connected', async () => {
// Refresh state after reconnection
await refreshBalances();
console.log('State refreshed');
});

4. Implement Health Checks

setInterval(async () => {
if (!client.events.isConnected()) {
console.warn('Event stream disconnected');
try {
await client.events.reconnect();
} catch (error) {
console.error('Reconnection failed:', error);
}
}
}, 60000); // Check every minute

Next Steps