Polygon zkEVM WebSocket Guide
WebSocket connections provide real-time, bidirectional communication with the Polygon zkEVM 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/polygon_zkevm/${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/polygon_zkevm/${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/polygon_zkevm/${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/polygon_zkevm/${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 Polygon zkEVMWebSocketManager {
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 Polygon zkEVMWebSocketManager('wss://api.blockeden.xyz/polygon_zkevm/${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 Polygon zkEVMWebSocketManager(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/polygon_zkevm/${accessKey}');
dashboard.start();
NFT Marketplace Monitorโ
class NFTMarketplaceMonitor {
constructor(wsEndpoint) {
this.wsManager = new Polygon zkEVMWebSocketManager(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(`