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.

JavaScript
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);
Python
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())
wscat
# 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

JSON
{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "subscribeSlots",
  "params": null
}

Subscribe Response

Returns the subscription ID (integer) used to identify future notifications and to unsubscribe.

JSON
{"jsonrpc": "2.0", "id": 1, "result": 1}

Notification

JSON
{
  "jsonrpc": "2.0",
  "method": "subscription",
  "params": {
    "subscription": 1,
    "result": { /* event-specific payload */ }
  }
}

Unsubscribe Request

JSON
{
  "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

JSON
{ "slot": 43 }

SDK Usage

JavaScript
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

JSON
{
  "slot": 43,
  "hash": "a1b2c3...",
  "parent_hash": "d4e5f6...",
  "transactions": 5,
  "timestamp": 1738800000
}

SDK Usage

JavaScript
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

JSON
{
  "signatures": ["a1b2c3..."],
  "instructions": 2,
  "recent_blockhash": "d4e5f6..."
}

SDK Usage

JavaScript
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

NameTypeRequiredDescription
pubkeystringYesBase58-encoded public key to watch

Notification Payload

JSON
{
  "pubkey": "7xKXtg2CW87d97TXJSDpbD5jBkheTqA83TZRuJosgAsU",
  "balance": 5000000000,
  "molt": 5
}

SDK Usage

JavaScript
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

NameTypeRequiredDescription
contractIdstring | nullNoContract pubkey to filter, or null for all

Notification Payload

JSON
{
  "contract": "ContractPubkey...",
  "message": "Transfer completed: 100 MOLT"
}

SDK Usage

JavaScript
// 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

JSON
{ "program": "ProgramPubkey...", "kind": "deploy" }

subscribeProgramCalls / unsubscribeProgramCalls

Stream program invocations. Optionally filter by a specific program.

Parameters

NameTypeRequiredDescription
programIdstring | nullNoProgram pubkey to filter, or null for all

Notification Payload

JSON
{ "program": "ProgramPubkey..." }

subscribeNftMints / unsubscribeNftMints

Receive a notification each time an NFT is minted. Optionally filter by collection.

Parameters

NameTypeRequiredDescription
collectionIdstring | nullNoCollection pubkey to filter, or null for all

Notification Payload

JSON
{ "collection": "CollectionPubkey..." }

subscribeNftTransfers / unsubscribeNftTransfers

Stream NFT transfer events. Optionally filter by collection.

Parameters

NameTypeRequiredDescription
collectionIdstring | nullNoCollection pubkey to filter, or null for all

Notification Payload

JSON
{ "collection": "CollectionPubkey..." }

subscribeMarketListings / unsubscribeMarketListings

Receive a notification each time an item is listed on the marketplace.

Parameters

None

Notification Payload

JSON
{
  "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

JavaScript
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

JSON
{
  "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

JavaScript
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

JSON
{
  "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

JSON
{
  "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

NameTypeDescription
signaturestringTransaction signature hex string

Notification Payload

JSON
{
  "event": "SignatureStatus",
  "signature": "abc123...",
  "status": "finalized",
  "slot": 1234,
  "error": null
}

SDK Usage

JavaScript
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

JSON
{
  "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

NameTypeDescription
ownerstringOwner public key (required)
mintstring?Token mint address (optional — omit for all tokens)

Notification Payload

JSON
{
  "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

JSON
{
  "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

JSON
{
  "event": "GovernanceEvent",
  "proposal_id": 7,
  "kind": "voted",
  "voter": "VoterPub...",
  "vote_weight": 50000000000,
  "slot": 1234
}

Event Payloads

Slot Event

Emitted on each new slot.

JSON
{ "slot": 43 }

Block Event

Emitted on each new block.

JSON
{
  "slot": 43,
  "hash": "a1b2c3d4e5f6...",
  "parent_hash": "f6e5d4c3b2a1...",
  "transactions": 5,
  "timestamp": 1738800000
}

Transaction Event

Emitted for each confirmed transaction.

JSON
{
  "signatures": ["a1b2c3d4..."],
  "instructions": 2,
  "recent_blockhash": "d4e5f6..."
}

AccountChange Event

Emitted when a watched account's balance changes.

JSON
{
  "pubkey": "7xKXtg2CW87d...",
  "balance": 5000000000,
  "molt": 5
}

Log Event

Emitted when a contract emits a log message.

JSON
{
  "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.

JSON
{
  "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>
JSON-RPC
{
  "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

  • all or markets — all markets
  • market:<market_id>
  • <market_id>
JSON-RPC
{
  "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:

JavaScript
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., subscribeLogs with 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.