Moonbeam WebSocket Guide
WebSocket connections provide real-time, bidirectional communication with the Moonbeam blockchain, enabling your applications to receive instant updates for new blocks, transactions, and smart contract events without polling.
Why Use WebSockets?
Advantages over HTTP Polling
- Real-time Updates: Instant notifications when events occur
- Lower Latency: No polling delays
- Reduced Bandwidth: Only receive data when it changes
- Better Performance: Persistent connection reduces overhead
- Event-Driven: React to blockchain events as they happen
Common Use Cases
- Trading Applications: Real-time price feeds and order book updates
- DeFi Dashboards: Live liquidity and yield changes
- NFT Marketplaces: Instant sale and listing notifications
- Wallet Applications: Balance updates and transaction confirmations
- Analytics Platforms: Real-time blockchain metrics
Connection Setup
WebSocket Endpoints
wss://api.blockeden.xyz/moonbeam/${accessKey}
wss://ethereum-sepolia.blockeden.xyz/<your-api-key>
wss://polygon-mainnet.blockeden.xyz/<your-api-key>
wss://arbitrum-mainnet.blockeden.xyz/<your-api-key>
Basic Connection
JavaScript (Browser/Node.js)
const ws = new WebSocket('wss://api.blockeden.xyz/moonbeam/${accessKey}');
ws.onopen = function(event) {
console.log('WebSocket connected');
};
ws.onmessage = function(event) {
const data = JSON.parse(event.data);
console.log('Received:', data);
};
ws.onerror = function(error) {
console.error('WebSocket error:', error);
};
ws.onclose = function(event) {
console.log('WebSocket closed:', event.code, event.reason);
};
Python
import asyncio
import websockets
import json
async def connect():
uri = "wss://api.blockeden.xyz/moonbeam/${accessKey}"
async with websockets.connect(uri) as websocket:
print("WebSocket connected")
# Subscribe to new heads
subscribe_msg = {
"jsonrpc": "2.0",
"method": "eth_subscribe",
"params": ["newHeads"],
"id": 1
}
await websocket.send(json.dumps(subscribe_msg))
# Listen for messages
async for message in websocket:
data = json.loads(message)
print(f"Received: {data}")
# Run the connection
asyncio.run(connect())
Go
package main
import (
"encoding/json"
"fmt"
"log"
"github.com/gorilla/websocket"
)
type SubscriptionRequest struct {
JSONRPC string `json:"jsonrpc"`
Method string `json:"method"`
Params []string `json:"params"`
ID int `json:"id"`
}
func main() {
url := "wss://api.blockeden.xyz/moonbeam/${accessKey}"
conn, _, err := websocket.DefaultDialer.Dial(url, nil)
if err != nil {
log.Fatal("dial:", err)
}
defer conn.Close()
// Subscribe to new heads
req := SubscriptionRequest{
JSONRPC: "2.0",
Method: "eth_subscribe",
Params: []string{"newHeads"},
ID: 1,
}
if err := conn.WriteJSON(req); err != nil {
log.Fatal("write:", err)
}
// Read messages
for {
var msg map[string]interface{}
if err := conn.ReadJSON(&msg); err != nil {
log.Fatal("read:", err)
}
fmt.Printf("Received: %+v\n", msg)
}
}
Subscription Types
1. New Block Headers (newHeads)
Subscribe to new block headers as they're mined.
const subscribeToNewHeads = () => {
const subscription = {
jsonrpc: '2.0',
method: 'eth_subscribe',
params: ['newHeads'],
id: 1
};
ws.send(JSON.stringify(subscription));
};
// Handle new block headers
ws.onmessage = function(event) {
const data = JSON.parse(event.data);
if (data.method === 'eth_subscription' && data.params.subscription) {
const blockHeader = data.params.result;
console.log('New block:', {
number: parseInt(blockHeader.number, 16),
hash: blockHeader.hash,
timestamp: parseInt(blockHeader.timestamp, 16),
gasUsed: parseInt(blockHeader.gasUsed, 16),
gasLimit: parseInt(blockHeader.gasLimit, 16)
});
}
};
2. Pending Transactions (newPendingTransactions)
Subscribe to new pending transactions in the mempool.
const subscribeToPendingTransactions = () => {
const subscription = {
jsonrpc: '2.0',
method: 'eth_subscribe',
params: ['newPendingTransactions'],
id: 2
};
ws.send(JSON.stringify(subscription));
};
// Handle pending transactions
ws.onmessage = function(event) {
const data = JSON.parse(event.data);
if (data.method === 'eth_subscription' && data.params.subscription) {
const txHash = data.params.result;
console.log('New pending transaction:', txHash);
// Optionally fetch full transaction details
fetchTransactionDetails(txHash);
}
};
3. Smart Contract Events (logs)
Subscribe to specific smart contract events using log filters.
const subscribeToContractEvents = (contractAddress, topics) => {
const subscription = {
jsonrpc: '2.0',
method: 'eth_subscribe',
params: [
'logs',
{
address: contractAddress,
topics: topics
}
],
id: 3
};
ws.send(JSON.stringify(subscription));
};
// Subscribe to ERC-20 Transfer events
const ERC20_TRANSFER_TOPIC = '0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef';
subscribeToContractEvents('0xA0b86a33E6c0e4A2a2a5FB1C6A6D6a30BF8b6B3a', [ERC20_TRANSFER_TOPIC]);
// Handle contract events
ws.onmessage = function(event) {
const data = JSON.parse(event.data);
if (data.method === 'eth_subscription' && data.params.subscription) {
const log = data.params.result;
console.log('Contract event:', {
address: log.address,
topics: log.topics,
data: log.data,
blockNumber: parseInt(log.blockNumber, 16),
transactionHash: log.transactionHash
});
}
};
4. Synchronized Blocks (syncing)
Subscribe to synchronization status updates.
const subscribeToSyncing = () => {
const subscription = {
jsonrpc: '2.0',
method: 'eth_subscribe',
params: ['syncing'],
id: 4
};
ws.send(JSON.stringify(subscription));
};
// Handle sync status
ws.onmessage = function(event) {
const data = JSON.parse(event.data);
if (data.method === 'eth_subscription' && data.params.subscription) {
const syncStatus = data.params.result;
if (syncStatus === false) {
console.log('Node is fully synchronized');
} else {
console.log('Sync status:', {
startingBlock: parseInt(syncStatus.startingBlock, 16),
currentBlock: parseInt(syncStatus.currentBlock, 16),
highestBlock: parseInt(syncStatus.highestBlock, 16)
});
}
}
};
Advanced Usage Patterns
Managing Multiple Subscriptions
class MoonbeamWebSocketManager {
constructor(endpoint) {
this.endpoint = endpoint;
this.ws = null;
this.subscriptions = new Map();
this.messageHandlers = new Map();
this.reconnectAttempts = 0;
this.maxReconnectAttempts = 5;
}
connect() {
return new Promise((resolve, reject) => {
this.ws = new WebSocket(this.endpoint);
this.ws.onopen = () => {
console.log('WebSocket connected');
this.reconnectAttempts = 0;
resolve();
};
this.ws.onmessage = (event) => {
this.handleMessage(JSON.parse(event.data));
};
this.ws.onerror = (error) => {
console.error('WebSocket error:', error);
reject(error);
};
this.ws.onclose = (event) => {
console.log('WebSocket closed:', event.code, event.reason);
this.handleReconnection();
};
});
}
subscribe(type, params, handler) {
const id = Date.now();
const subscription = {
jsonrpc: '2.0',
method: 'eth_subscribe',
params: [type, ...(params || [])],
id: id
};
this.ws.send(JSON.stringify(subscription));
this.messageHandlers.set(id, handler);
return id;
}
unsubscribe(subscriptionId) {
const unsubscribe = {
jsonrpc: '2.0',
method: 'eth_unsubscribe',
params: [subscriptionId],
id: Date.now()
};
this.ws.send(JSON.stringify(unsubscribe));
this.subscriptions.delete(subscriptionId);
}
handleMessage(data) {
if (data.method === 'eth_subscription') {
// Handle subscription data
const subscriptionId = data.params.subscription;
const handler = this.subscriptions.get(subscriptionId);
if (handler) {
handler(data.params.result);
}
} else if (data.id && this.messageHandlers.has(data.id)) {
// Handle subscription response
const handler = this.messageHandlers.get(data.id);
this.messageHandlers.delete(data.id);
if (data.result) {
this.subscriptions.set(data.result, handler);
}
}
}
handleReconnection() {
if (this.reconnectAttempts < this.maxReconnectAttempts) {
this.reconnectAttempts++;
const delay = Math.pow(2, this.reconnectAttempts) * 1000;
setTimeout(() => {
console.log(`Reconnecting... Attempt ${this.reconnectAttempts}`);
this.connect().then(() => {
// Resubscribe to all previous subscriptions
this.resubscribeAll();
});
}, delay);
}
}
resubscribeAll() {
// Implementation depends on your subscription tracking
console.log('Resubscribing to all previous subscriptions');
}
}
// Usage
const wsManager = new MoonbeamWebSocketManager('wss://api.blockeden.xyz/moonbeam/${accessKey}');
wsManager.connect().then(() => {
// Subscribe to new blocks
wsManager.subscribe('newHeads', null, (blockHeader) => {
console.log('New block:', blockHeader);
});
// Subscribe to USDC transfers
wsManager.subscribe('logs', [{
address: '0xA0b86a33E6c0e4A2a2a5FB1C6A6D6a30BF8b6B3a',
topics: ['0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef']
}], (log) => {
console.log('USDC transfer:', log);
});
});
Real-time Trading Dashboard
class TradingDashboard {
constructor(wsEndpoint) {
this.wsManager = new MoonbeamWebSocketManager(wsEndpoint);
this.priceFeeds = new Map();
this.orderBookUpdates = new Map();
}
async start() {
await this.wsManager.connect();
// Subscribe to DEX events
this.subscribeToUniswapV3();
this.subscribeToSushiSwap();
this.subscribeToNewBlocks();
}
subscribeToUniswapV3() {
// Uniswap V3 Swap events
const SWAP_TOPIC = '0xc42079f94a6350d7e6235f29174924f928cc2ac818eb64fed8004e115fbcca67';
this.wsManager.subscribe('logs', [{
address: '0x1F98431c8aD98523631AE4a59f267346ea31F984', // Uniswap V3 Factory
topics: [SWAP_TOPIC]
}], (log) => {
this.handleUniswapSwap(log);
});
}
subscribeToSushiSwap() {
// SushiSwap Swap events
const SWAP_TOPIC = '0xd78ad95fa46c994b6551d0da85fc275fe613ce37657fb8d5e3d130840159d822';
this.wsManager.subscribe('logs', [{
topics: [SWAP_TOPIC]
}], (log) => {
this.handleSushiSwap(log);
});
}
subscribeToNewBlocks() {
this.wsManager.subscribe('newHeads', null, (blockHeader) => {
this.updateBlockInfo(blockHeader);
});
}
handleUniswapSwap(log) {
// Parse Uniswap V3 swap data
const poolAddress = log.address;
const data = this.parseSwapData(log.data);
this.updatePriceFeed('uniswap', poolAddress, data);
}
handleSushiSwap(log) {
// Parse SushiSwap swap data
const poolAddress = log.address;
const data = this.parseSwapData(log.data);
this.updatePriceFeed('sushiswap', poolAddress, data);
}
updatePriceFeed(exchange, pool, swapData) {
const key = `${exchange}:${pool}`;
const currentFeed = this.priceFeeds.get(key) || { prices: [], volume: 0 };
currentFeed.prices.push({
price: swapData.price,
timestamp: Date.now(),
volume: swapData.amount
});
// Keep only last 100 prices
if (currentFeed.prices.length > 100) {
currentFeed.prices.shift();
}
currentFeed.volume += swapData.amount;
this.priceFeeds.set(key, currentFeed);
// Emit update to UI
this.emitPriceUpdate(exchange, pool, currentFeed);
}
parseSwapData(data) {
// Implementation depends on the specific DEX
// This is a simplified example
return {
price: parseFloat(data.slice(0, 66)),
amount: parseFloat(data.slice(66, 130))
};
}
emitPriceUpdate(exchange, pool, feed) {
// Send to frontend/dashboard
console.log(`Price update for ${exchange}:${pool}`, feed);
}
updateBlockInfo(blockHeader) {
const blockInfo = {
number: parseInt(blockHeader.number, 16),
timestamp: parseInt(blockHeader.timestamp, 16),
gasUsed: parseInt(blockHeader.gasUsed, 16),
gasLimit: parseInt(blockHeader.gasLimit, 16)
};
console.log('New block:', blockInfo);
}
}
// Usage
const dashboard = new TradingDashboard('wss://api.blockeden.xyz/moonbeam/${accessKey}');
dashboard.start();
NFT Marketplace Monitor
class NFTMarketplaceMonitor {
constructor(wsEndpoint) {
this.wsManager = new MoonbeamWebSocketManager(wsEndpoint);
this.marketplaces = {
opensea: '0x00000000006c3852cbEf3e08E8dF289169EdE581',
looksrare: '0x59728544B08AB483533076417FbBB2fD0B17CE3a',
x2y2: '0x74312363e45DCaBA76c59ec49a7Aa8A65a67EeD3'
};
}
async start() {
await this.wsManager.connect();
Object.entries(this.marketplaces).forEach(([name, address]) => {
this.subscribeToMarketplace(name, address);
});
}
subscribeToMarketplace(name, address) {
// Subscribe to all events from marketplace contract
this.wsManager.subscribe('logs', [{
address: address
}], (log) => {
this.handleMarketplaceEvent(name, log);
});
}
handleMarketplaceEvent(marketplace, log) {
const eventSignature = log.topics[0];
switch (eventSignature) {
case '0x9d9af8e38d66c62e2c12f0225249fd9d721c54b83f48d9352c97c6cacdcb6f31': // Sale
this.handleSale(marketplace, log);
break;
case '0x3cbb63f144840e5b1b0a38a7c19211d2e89de4d7c5faf8b2d3c1776c302d1d33': // Listing
this.handleListing(marketplace, log);
break;
default:
console.log(`Unknown event from ${marketplace}:`, eventSignature);
}
}
handleSale(marketplace, log) {
// Parse sale data
const saleData = this.parseSaleData(log);
console.log(`