WebSocket Reference
MoltChain provides a WebSocket server for real-time event streaming. Subscribe to chain events — slots, blocks, transactions, account changes, contract logs, NFT activity, and marketplace events — and receive push notifications as they happen.
Connection
Default endpoint: ws://localhost:8900
The WebSocket server accepts standard WebSocket connections. All messages use JSON-RPC 2.0 format.
import { Connection } from '@moltchain/sdk';
const conn = new Connection(
'http://localhost:8899', // RPC
'ws://localhost:8900' // WebSocket
);
// Subscribe to new slots
const subId = await conn.onSlot((slot) => {
console.log('New slot:', slot);
});
// Later: unsubscribe
await conn.offSlot(subId);
import asyncio
from moltchain import Connection
async def main():
conn = Connection(
'http://localhost:8899',
ws_url='ws://localhost:8900'
)
sub_id = await conn.on_slot(lambda slot: print(f'New slot: {slot}'))
# Keep running to receive events
await asyncio.sleep(60)
await conn.off_slot(sub_id)
await conn.close()
asyncio.run(main())
# Install: npm install -g wscat
wscat -c ws://localhost:8900
# Subscribe to slots
> {"jsonrpc":"2.0","id":1,"method":"subscribeSlots","params":null}
< {"jsonrpc":"2.0","id":1,"result":1}
# Receive notifications
< {"jsonrpc":"2.0","method":"subscription","params":{"subscription":1,"result":{"slot":43}}}
Protocol Format
Subscribe Request
{
"jsonrpc": "2.0",
"id": 1,
"method": "subscribeSlots",
"params": null
}
Subscribe Response
Returns the subscription ID (integer) used to identify future notifications and to unsubscribe.
{"jsonrpc": "2.0", "id": 1, "result": 1}
Notification
{
"jsonrpc": "2.0",
"method": "subscription",
"params": {
"subscription": 1,
"result": { /* event-specific payload */ }
}
}
Unsubscribe Request
{
"jsonrpc": "2.0",
"id": 2,
"method": "unsubscribeSlots",
"params": 1
}
Subscription Methods
subscribeSlots / unsubscribeSlots
Receive a notification each time a new slot is produced.
Parameters
None
Notification Payload
{ "slot": 43 }
SDK Usage
const subId = await conn.onSlot((slot) => {
console.log('Slot:', slot);
});
await conn.offSlot(subId);
subscribeBlocks / unsubscribeBlocks
Receive a notification for every new block with header summary.
Parameters
None
Notification Payload
{
"slot": 43,
"hash": "a1b2c3...",
"parent_hash": "d4e5f6...",
"transactions": 5,
"timestamp": 1738800000
}
SDK Usage
const subId = await conn.onBlock((block) => {
console.log(`Block #${block.slot} — ${block.transactions} txs`);
});
await conn.offBlock(subId);
subscribeTransactions / unsubscribeTransactions
Stream every confirmed transaction in real-time.
Parameters
None
Notification Payload
{
"signatures": ["a1b2c3..."],
"instructions": 2,
"recent_blockhash": "d4e5f6..."
}
SDK Usage
const subId = await conn.onTransaction((tx) => {
console.log('TX:', tx.signatures[0]);
});
await conn.offTransaction(subId);
subscribeAccount / unsubscribeAccount
Receive a notification whenever an account's balance changes.
Parameters
| Name | Type | Required | Description |
|---|---|---|---|
| pubkey | string | Yes | Base58-encoded public key to watch |
Notification Payload
{
"pubkey": "7xKXtg2CW87d97TXJSDpbD5jBkheTqA83TZRuJosgAsU",
"balance": 5000000000,
"molt": 5
}
SDK Usage
const pubkey = new PublicKey('7xKXtg2CW87d...');
const subId = await conn.onAccountChange(pubkey, (account) => {
console.log('New balance:', account.molt, 'MOLT');
});
await conn.offAccountChange(subId);
subscribeLogs / unsubscribeLogs
Stream contract execution logs. Optionally filter by a specific contract address; pass null for all contracts.
Parameters
| Name | Type | Required | Description |
|---|---|---|---|
| contractId | string | null | No | Contract pubkey to filter, or null for all |
Notification Payload
{
"contract": "ContractPubkey...",
"message": "Transfer completed: 100 MOLT"
}
SDK Usage
// All logs
const subId = await conn.onLogs((log) => {
console.log(`[${log.contract}] ${log.message}`);
});
// Single contract
const contractKey = new PublicKey('ContractPub...');
const subId2 = await conn.onLogs((log) => {
console.log(log.message);
}, contractKey);
subscribeProgramUpdates / unsubscribeProgramUpdates
Receive notifications when programs are deployed or upgraded.
Parameters
None
Notification Payload
{ "program": "ProgramPubkey...", "kind": "deploy" }
subscribeProgramCalls / unsubscribeProgramCalls
Stream program invocations. Optionally filter by a specific program.
Parameters
| Name | Type | Required | Description |
|---|---|---|---|
| programId | string | null | No | Program pubkey to filter, or null for all |
Notification Payload
{ "program": "ProgramPubkey..." }
subscribeNftMints / unsubscribeNftMints
Receive a notification each time an NFT is minted. Optionally filter by collection.
Parameters
| Name | Type | Required | Description |
|---|---|---|---|
| collectionId | string | null | No | Collection pubkey to filter, or null for all |
Notification Payload
{ "collection": "CollectionPubkey..." }
subscribeNftTransfers / unsubscribeNftTransfers
Stream NFT transfer events. Optionally filter by collection.
Parameters
| Name | Type | Required | Description |
|---|---|---|---|
| collectionId | string | null | No | Collection pubkey to filter, or null for all |
Notification Payload
{ "collection": "CollectionPubkey..." }
subscribeMarketListings / unsubscribeMarketListings
Receive a notification each time an item is listed on the marketplace.
Parameters
None
Notification Payload
{
"event": "MarketListing",
"slot": 100,
"timestamp": 1738800000,
"kind": "list",
"program": "ProgramPub...",
"collection": "CollectionPub...",
"token_id": 42,
"price": 5000000000,
"price_molt": 5.0,
"seller": "SellerPub...",
"tx_signature": "abc123..."
}
SDK Usage
const subId = await conn.onMarketListings((event) => {
console.log(`Listed: token #${event.token_id} for ${event.price_molt} MOLT`);
});
await conn.offMarketListings(subId);
subscribeMarketSales / unsubscribeMarketSales
Receive a notification each time a marketplace sale is completed.
Parameters
None
Notification Payload
{
"event": "MarketSale",
"slot": 101,
"timestamp": 1738800400,
"kind": "sale",
"program": "ProgramPub...",
"collection": "CollectionPub...",
"token_id": 42,
"price": 5000000000,
"price_molt": 5.0,
"seller": "SellerPub...",
"buyer": "BuyerPub...",
"tx_signature": "def456..."
}
SDK Usage
const subId = await conn.onMarketSales((event) => {
console.log(`Sold: token #${event.token_id} for ${event.price_molt} MOLT`);
});
await conn.offMarketSales(subId);
subscribeBridgeLocks / unsubscribeBridgeLocks
Receive bridge lock events when assets are locked for cross-chain transfer.
Parameters
None
Notification Payload
{
"event": "BridgeLock",
"chain": "ethereum",
"asset": "MOLT",
"amount": 250000000,
"amount_display": 250.0,
"sender": "0xabc...",
"recipient": "RecipientPub..."
}
subscribeBridgeMints / unsubscribeBridgeMints
Receive bridge mint events when bridged assets are minted on MoltChain.
Parameters
None
Notification Payload
{
"event": "BridgeMint",
"chain": "ethereum",
"asset": "MOLT",
"amount": 250000000,
"amount_display": 250.0,
"recipient": "RecipientPub...",
"tx_hash": "0xdef..."
}
subscribeSignatureStatus / signatureSubscribe
Track the confirmation status of a specific transaction signature through processed → confirmed → finalized.
Parameters
| Name | Type | Description |
|---|---|---|
| signature | string | Transaction signature hex string |
Notification Payload
{
"event": "SignatureStatus",
"signature": "abc123...",
"status": "finalized",
"slot": 1234,
"error": null
}
SDK Usage
const subId = await conn.onSignatureStatus(txSig, (status) => {
console.log(`TX ${status.status} at slot ${status.slot}`);
});
await conn.offSignatureStatus(subId);
subscribeValidators / validatorSubscribe
Receive notifications when the validator set changes — new validators joining, leaving, becoming delinquent, or stake changes.
Parameters
None
Notification Payload
{
"event": "ValidatorUpdate",
"pubkey": "8GCKfyj...",
"kind": "joined",
"stake": 100000000000000,
"stake_display": 100000.0,
"slot": 500
}
subscribeTokenBalance / tokenBalanceSubscribe
Track token balance changes for a specific owner, optionally filtered by mint address.
Parameters
| Name | Type | Description |
|---|---|---|
| owner | string | Owner public key (required) |
| mint | string? | Token mint address (optional — omit for all tokens) |
Notification Payload
{
"event": "TokenBalanceChange",
"owner": "UserPub...",
"mint": "MintPub...",
"old_balance": 1000000000,
"new_balance": 2000000000,
"delta": 1000000000,
"slot": 789
}
subscribeEpochs / epochSubscribe
Receive a notification at each epoch boundary with epoch statistics.
Parameters
None
Notification Payload
{
"event": "EpochChange",
"epoch": 5,
"slot": 2160000,
"total_stake": 500000000000000,
"total_stake_display": 500000.0,
"validator_count": 42
}
subscribeGovernance / governanceSubscribe
Receive notifications for on-chain governance events: proposal creation, votes, execution, and cancellation.
Parameters
None
Notification Payload
{
"event": "GovernanceEvent",
"proposal_id": 7,
"kind": "voted",
"voter": "VoterPub...",
"vote_weight": 50000000000,
"slot": 1234
}
Event Payloads
Slot Event
Emitted on each new slot.
{ "slot": 43 }
Block Event
Emitted on each new block.
{
"slot": 43,
"hash": "a1b2c3d4e5f6...",
"parent_hash": "f6e5d4c3b2a1...",
"transactions": 5,
"timestamp": 1738800000
}
Transaction Event
Emitted for each confirmed transaction.
{
"signatures": ["a1b2c3d4..."],
"instructions": 2,
"recent_blockhash": "d4e5f6..."
}
AccountChange Event
Emitted when a watched account's balance changes.
{
"pubkey": "7xKXtg2CW87d...",
"balance": 5000000000,
"molt": 5
}
Log Event
Emitted when a contract emits a log message.
{
"contract": "ContractPubkey...",
"message": "Transfer completed: 100 MOLT"
}
MarketListing / MarketSale Event
Emitted for marketplace activity. Both events share the same structure with an event discriminator field.
{
"event": "MarketListing" | "MarketSale",
"slot": 100,
"timestamp": 1738800000,
"kind": "list" | "sale",
"program": "ProgramPub...",
"collection": "CollectionPub...",
"token": "TokenPub...",
"token_id": 42,
"price": 5000000000,
"price_molt": 5.0,
"seller": "SellerPub...",
"buyer": "BuyerPub...",
"function": "buyNow",
"tx_signature": "abc123..."
}
Additional Implemented Live Channels
These subscription families are implemented in the WebSocket server (`rpc/src/ws.rs`) and channel parsing in `rpc/src/dex_ws.rs`.
DEX Multiplexed Channels
subscribeDex / unsubscribeDex
Channel Param Format
orderbook:<pair_id>trades:<pair_id>ticker:<pair_id>candles:<pair_id>:<interval>orders:<trader_addr>positions:<trader_addr>
{
"jsonrpc": "2.0",
"id": 101,
"method": "subscribeDex",
"params": { "channel": "trades:1" }
}
{
"jsonrpc": "2.0",
"id": 102,
"method": "unsubscribeDex",
"params": { "subscription": 101 }
}
Prediction Multiplexed Channels
subscribePrediction / unsubscribePrediction
Aliases: subscribePredictionMarket / unsubscribePredictionMarket
Channel Param Format
allormarkets— all marketsmarket:<market_id><market_id>
{
"jsonrpc": "2.0",
"id": 201,
"method": "subscribePrediction",
"params": { "channel": "market:42" }
}
{
"jsonrpc": "2.0",
"id": 202,
"method": "unsubscribePrediction",
"params": { "subscription": 201 }
}
Keepalive (Ping/Pong)
The WebSocket server supports standard ping/pong frames for connection keepalive. If your client library supports automatic pong responses (most do), no action is needed. For manual implementations:
- The server sends a ping frame periodically (every 30s by default).
- Your client must respond with a pong frame within the timeout window.
- Connections that fail to respond to 3 consecutive pings are automatically closed.
Notification wrapper difference: Core subscriptions (slots, blocks, accounts, etc.) use "method": "subscription" in push messages. DEX and Prediction channel notifications use "method": "notification" instead. Check both wrappers when parsing events.
Best Practices
Reconnection Handling
WebSocket connections can drop due to network issues, server restarts, or idle timeouts. Implement automatic reconnection with exponential backoff:
class ResilientConnection {
constructor(rpcUrl, wsUrl) {
this.rpcUrl = rpcUrl;
this.wsUrl = wsUrl;
this.conn = null;
this.subscriptions = [];
this.retryDelay = 1000;
}
async connect() {
this.conn = new Connection(this.rpcUrl, this.wsUrl);
// Re-subscribe after reconnection
for (const sub of this.subscriptions) {
await sub.resubscribe(this.conn);
}
this.retryDelay = 1000; // reset on success
}
async reconnect() {
console.log(`Reconnecting in ${this.retryDelay}ms...`);
await new Promise(r => setTimeout(r, this.retryDelay));
this.retryDelay = Math.min(this.retryDelay * 2, 30000);
try {
await this.connect();
} catch (e) {
await this.reconnect();
}
}
}
Tip: Store subscription parameters so you can restore them after reconnection. The SDK's Connection class will automatically reconnect the WebSocket when you call an on* method.
Rate Limits & Subscription Limits
Max subscriptions per connection: 100
If you exceed this limit, the server returns error code -32005: "Subscription limit reached".
Best practices:
- Unsubscribe from channels you no longer need.
- Use filtered subscriptions (e.g.,
subscribeLogswith a contract ID) instead of subscribing to all events and filtering client-side. - For high-throughput scenarios, consider using multiple WebSocket connections and distributing subscriptions across them.
- The broadcast channel has a buffer of 1000 events. If your consumer falls behind, events may be dropped.