Skip to main content

Live Streams (SSE)

The Explorer API provides real-time data streams using Server-Sent Events (SSE). These endpoints allow you to subscribe to blockchain events without polling.

Overview

SSE provides a persistent, one-way connection from server to client, perfect for:

  • Real-time block notifications
  • Live contract activity monitoring
  • Transaction stream subscriptions
  • Event-driven applications

Available Streams

EndpointEvent TypeDescription
/live/blocksblockNew blocks
/live/contract-actionscontractActionContract actions (filterable)
/live/zswap-ledger-eventszswapLedgerEventZSwap ledger events
/live/dust-ledger-eventsdustLedgerEventDust ledger events
/live/unshielded-transactionsunshieldedTransactionUnshielded transactions
/live/shielded-transactionsshieldedTransactionShielded transactions

Block Stream

GET/live/blocksLIVE

Streams new blocks as they are added to the chain.

Event Type: block

const eventSource = new EventSource(
'http://explorer.nocy.io:4000/live/blocks'
);

eventSource.addEventListener('block', (event) => {
const block = JSON.parse(event.data);
console.log('New block:', block.height);
console.log('Hash:', block.hash);
console.log('Transactions:', block.transactionCount);
});

eventSource.onerror = (error) => {
console.error('SSE Error:', error);
// Implement reconnection logic
};

// Clean up when done
// eventSource.close();

Event Data:

{
"height": 125001,
"hash": "0x1234abcd...",
"timestamp": "2024-12-13T12:00:10.000Z",
"transactionCount": 5,
"parentHash": "0x0000abcd..."
}

Contract Actions Stream

GET/live/contract-actionsLIVE

Streams contract actions, optionally filtered by contract address.

Event Type: contractAction

Query Parameters

ParameterTypeRequiredDescription
addressstringNoContract address to filter (hex format)
// Stream all contract actions
const allActions = new EventSource(
'http://explorer.nocy.io:4000/live/contract-actions'
);

// Stream actions for specific contract
const contractAddress = '0x1234abcd...';
const filteredActions = new EventSource(
`http://explorer.nocy.io:4000/live/contract-actions?address=${contractAddress}`
);

filteredActions.addEventListener('contractAction', (event) => {
const action = JSON.parse(event.data);
console.log('Contract action:', action.action);
console.log('Data:', action.data);
});

Event Data:

{
"id": "action-456",
"contractAddress": "0x1234abcd...",
"action": "transfer",
"blockHeight": 125001,
"transactionHash": "0xtx789...",
"timestamp": "2024-12-13T12:00:10.000Z",
"data": {
"from": "0xsender...",
"to": "0xreceiver...",
"amount": "1000"
}
}

ZSwap Ledger Events

GET/live/zswap-ledger-eventsLIVE

Streams ZSwap ledger events for swap activity monitoring.

Event Type: zswapLedgerEvent

const eventSource = new EventSource(
'http://explorer.nocy.io:4000/live/zswap-ledger-events'
);

eventSource.addEventListener('zswapLedgerEvent', (event) => {
const zswapEvent = JSON.parse(event.data);
console.log('ZSwap event:', zswapEvent);
});

Dust Ledger Events

GET/live/dust-ledger-eventsLIVE

Streams dust ledger events.

Event Type: dustLedgerEvent

const eventSource = new EventSource(
'http://explorer.nocy.io:4000/live/dust-ledger-events'
);

eventSource.addEventListener('dustLedgerEvent', (event) => {
const dustEvent = JSON.parse(event.data);
console.log('Dust event:', dustEvent);
});

Unshielded Transactions

GET/live/unshielded-transactionsLIVE

Streams unshielded (public) transactions.

Event Type: unshieldedTransaction

const eventSource = new EventSource(
'http://explorer.nocy.io:4000/live/unshielded-transactions'
);

eventSource.addEventListener('unshieldedTransaction', (event) => {
const tx = JSON.parse(event.data);
console.log('Unshielded tx:', tx.hash);
});

Shielded Transactions

GET/live/shielded-transactionsLIVE

Streams shielded (private) transactions.

Event Type: shieldedTransaction

const eventSource = new EventSource(
'http://explorer.nocy.io:4000/live/shielded-transactions'
);

eventSource.addEventListener('shieldedTransaction', (event) => {
const tx = JSON.parse(event.data);
console.log('Shielded tx:', tx);
});

Best Practices

Error Handling & Reconnection

class SSEManager {
constructor(url, eventType, onEvent) {
this.url = url;
this.eventType = eventType;
this.onEvent = onEvent;
this.eventSource = null;
this.reconnectAttempts = 0;
this.maxReconnectAttempts = 5;
this.reconnectDelay = 1000;
}

connect() {
this.eventSource = new EventSource(this.url);

this.eventSource.addEventListener(this.eventType, (event) => {
this.reconnectAttempts = 0; // Reset on successful event
try {
const data = JSON.parse(event.data);
this.onEvent(data);
} catch (error) {
console.error('Failed to parse event:', error);
}
});

this.eventSource.onerror = (error) => {
console.error('SSE error:', error);
this.eventSource.close();
this.attemptReconnect();
};
}

attemptReconnect() {
if (this.reconnectAttempts >= this.maxReconnectAttempts) {
console.error('Max reconnection attempts reached');
return;
}

this.reconnectAttempts++;
const delay = this.reconnectDelay * Math.pow(2, this.reconnectAttempts - 1);

console.log(`Reconnecting in ${delay}ms (attempt ${this.reconnectAttempts})`);
setTimeout(() => this.connect(), delay);
}

disconnect() {
if (this.eventSource) {
this.eventSource.close();
this.eventSource = null;
}
}
}

// Usage
const blockStream = new SSEManager(
'http://explorer.nocy.io:4000/live/blocks',
'block',
(block) => console.log('New block:', block.height)
);

blockStream.connect();

// Later: blockStream.disconnect();

React Hook Example

import { useEffect, useState, useCallback } from 'react';

function useSSE(url, eventType) {
const [data, setData] = useState(null);
const [error, setError] = useState(null);
const [isConnected, setIsConnected] = useState(false);

useEffect(() => {
const eventSource = new EventSource(url);

eventSource.addEventListener(eventType, (event) => {
try {
setData(JSON.parse(event.data));
setIsConnected(true);
} catch (e) {
setError(e);
}
});

eventSource.onerror = (e) => {
setError(e);
setIsConnected(false);
};

eventSource.onopen = () => {
setIsConnected(true);
setError(null);
};

return () => eventSource.close();
}, [url, eventType]);

return { data, error, isConnected };
}

// Usage in component
function BlockMonitor() {
const { data: block, isConnected } = useSSE(
'http://explorer.nocy.io:4000/live/blocks',
'block'
);

return (
<div>
<span>{isConnected ? '🟢 Connected' : '🔴 Disconnected'}</span>
{block && <p>Latest block: {block.height}</p>}
</div>
);
}

Tips

Best Practices
  1. Always handle errors - SSE connections can drop; implement reconnection logic
  2. Close connections - Call eventSource.close() when components unmount
  3. Parse JSON safely - Wrap JSON.parse() in try-catch
  4. Use specific event listeners - Use addEventListener(eventType, ...) instead of onmessage
  5. Implement backoff - Use exponential backoff for reconnection attempts