Cancel an active WebSocket subscription on Hyperliquid EVM. Stop receiving notifications for a previously established subscription.
eth_unsubscribe
JSON-RPC method allows developers to cancel an active WebSocket subscription on the Hyperliquid EVM blockchain. This method is used to stop receiving notifications for subscriptions created with eth_subscribe
, helping to manage resources and control data flow in applications.
eth_subscribe
when the subscription was createdtrue
— the subscription was successfully cancelledfalse
— the subscription was not found or could not be cancellednpm install -g wscat
$ wscat -c wss://hyperliquid-mainnet.core.chainstack.com/4f8d8f4040bdacd1577bff8058438274/evm
# Wait for the connection to be established
Connected (press CTRL+C to quit)
# First create a subscription
> {"id":1,"jsonrpc":"2.0","method":"eth_subscribe","params":["newHeads"]}
< {"jsonrpc":"2.0","id":1,"result":"0x1234567890abcdef"}
# Now unsubscribe using the subscription ID
> {"id":2,"jsonrpc":"2.0","method":"eth_unsubscribe","params":["0x1234567890abcdef"]}
< {"jsonrpc":"2.0","id":2,"result":true}
const WebSocket = require('ws');
const CHAINSTACK_WSS_URL = 'wss://hyperliquid-mainnet.core.chainstack.com/YOUR_ENDPOINT/evm';
class SubscriptionManager {
constructor(wsUrl) {
this.wsUrl = wsUrl;
this.ws = null;
this.subscriptions = new Map();
this.requestId = 0;
}
connect() {
return new Promise((resolve, reject) => {
this.ws = new WebSocket(this.wsUrl);
this.ws.on('open', () => {
console.log('Connected to Hyperliquid EVM WebSocket');
resolve();
});
this.ws.on('message', (data) => {
this.handleMessage(JSON.parse(data));
});
this.ws.on('error', (error) => {
console.error('WebSocket error:', error);
reject(error);
});
this.ws.on('close', () => {
console.log('WebSocket connection closed');
this.subscriptions.clear();
});
});
}
async subscribe(type, params = []) {
const id = ++this.requestId;
return new Promise((resolve) => {
const request = {
id,
jsonrpc: '2.0',
method: 'eth_subscribe',
params: [type, ...params]
};
// Store resolver to handle response
this.subscriptions.set(`request_${id}`, {
type: 'request',
resolve,
subscriptionType: type
});
this.ws.send(JSON.stringify(request));
console.log(`Subscribing to ${type}...`);
});
}
async unsubscribe(subscriptionId) {
const id = ++this.requestId;
return new Promise((resolve) => {
const request = {
id,
jsonrpc: '2.0',
method: 'eth_unsubscribe',
params: [subscriptionId]
};
// Store resolver to handle response
this.subscriptions.set(`request_${id}`, {
type: 'unsubscribe',
resolve,
subscriptionId
});
this.ws.send(JSON.stringify(request));
console.log(`Unsubscribing from ${subscriptionId}...`);
});
}
handleMessage(message) {
// Handle subscription/unsubscription responses
if (message.id) {
const request = this.subscriptions.get(`request_${message.id}`);
if (request) {
if (request.type === 'request') {
// Store the subscription ID
this.subscriptions.set(message.result, {
type: 'subscription',
subscriptionType: request.subscriptionType,
handler: null
});
console.log(`Subscribed with ID: ${message.result}`);
request.resolve(message.result);
} else if (request.type === 'unsubscribe') {
// Clean up subscription
if (message.result === true) {
this.subscriptions.delete(request.subscriptionId);
console.log(`Successfully unsubscribed from ${request.subscriptionId}`);
} else {
console.log(`Failed to unsubscribe from ${request.subscriptionId}`);
}
request.resolve(message.result);
}
this.subscriptions.delete(`request_${message.id}`);
}
}
// Handle subscription notifications
if (message.method === 'eth_subscription') {
const subscription = this.subscriptions.get(message.params.subscription);
if (subscription && subscription.handler) {
subscription.handler(message.params.result);
}
}
}
setHandler(subscriptionId, handler) {
const subscription = this.subscriptions.get(subscriptionId);
if (subscription) {
subscription.handler = handler;
}
}
async disconnect() {
// Unsubscribe from all active subscriptions
const activeSubscriptions = Array.from(this.subscriptions.entries())
.filter(([_, sub]) => sub.type === 'subscription')
.map(([id, _]) => id);
for (const subId of activeSubscriptions) {
await this.unsubscribe(subId);
}
this.ws.close();
}
}
// Example usage
async function main() {
const manager = new SubscriptionManager(CHAINSTACK_WSS_URL);
try {
await manager.connect();
// Subscribe to new blocks
const blockSubId = await manager.subscribe('newHeads');
manager.setHandler(blockSubId, (block) => {
console.log(`New block: ${parseInt(block.number, 16)}`);
});
// Subscribe to logs
const logSubId = await manager.subscribe('logs', [{
address: '0x5555555555555555555555555555555555555555'
}]);
manager.setHandler(logSubId, (log) => {
console.log(`New log from ${log.address}`);
});
// Run for 30 seconds then clean up
setTimeout(async () => {
console.log('\nCleaning up subscriptions...');
// Unsubscribe from blocks
const blockResult = await manager.unsubscribe(blockSubId);
console.log(`Block unsubscribe result: ${blockResult}`);
// Unsubscribe from logs
const logResult = await manager.unsubscribe(logSubId);
console.log(`Log unsubscribe result: ${logResult}`);
// Disconnect
await manager.disconnect();
process.exit(0);
}, 30000);
} catch (error) {
console.error('Error:', error);
}
}
main();
import json
import asyncio
import websockets
from typing import Dict, Any, Optional
from contextlib import asynccontextmanager
class SubscriptionManager:
def __init__(self, ws_url: str):
self.ws_url = ws_url
self.websocket = None
self.subscriptions: Dict[str, Any] = {}
self.request_id = 0
self.pending_requests = {}
async def connect(self):
self.websocket = await websockets.connect(self.ws_url)
print("Connected to Hyperliquid EVM WebSocket")
async def disconnect(self):
if self.websocket:
await self.websocket.close()
print("Disconnected from WebSocket")
async def subscribe(self, subscription_type: str, params: Optional[Dict] = None) -> str:
self.request_id += 1
request = {
"id": self.request_id,
"jsonrpc": "2.0",
"method": "eth_subscribe",
"params": [subscription_type] + ([params] if params else [])
}
# Create a future to wait for response
future = asyncio.Future()
self.pending_requests[self.request_id] = future
await self.websocket.send(json.dumps(request))
print(f"Subscribing to {subscription_type}...")
# Wait for subscription ID
subscription_id = await future
self.subscriptions[subscription_id] = {
"type": subscription_type,
"params": params
}
print(f"Subscribed with ID: {subscription_id}")
return subscription_id
async def unsubscribe(self, subscription_id: str) -> bool:
self.request_id += 1
request = {
"id": self.request_id,
"jsonrpc": "2.0",
"method": "eth_unsubscribe",
"params": [subscription_id]
}
# Create a future to wait for response
future = asyncio.Future()
self.pending_requests[self.request_id] = future
await self.websocket.send(json.dumps(request))
print(f"Unsubscribing from {subscription_id}...")
# Wait for unsubscribe result
result = await future
if result:
del self.subscriptions[subscription_id]
print(f"Successfully unsubscribed from {subscription_id}")
else:
print(f"Failed to unsubscribe from {subscription_id}")
return result
async def listen(self, handler):
"""Listen for messages and handle them"""
async for message in self.websocket:
data = json.loads(message)
# Handle subscription/unsubscription responses
if "id" in data:
request_id = data["id"]
if request_id in self.pending_requests:
self.pending_requests[request_id].set_result(data.get("result"))
del self.pending_requests[request_id]
# Handle subscription notifications
elif data.get("method") == "eth_subscription":
subscription_id = data["params"]["subscription"]
if subscription_id in self.subscriptions:
await handler(subscription_id, data["params"]["result"])
async def cleanup(self):
"""Unsubscribe from all active subscriptions"""
print("\nCleaning up subscriptions...")
for sub_id in list(self.subscriptions.keys()):
await self.unsubscribe(sub_id)
@asynccontextmanager
async def managed_subscription(ws_url: str):
"""Context manager for automatic subscription cleanup"""
manager = SubscriptionManager(ws_url)
try:
await manager.connect()
yield manager
finally:
await manager.cleanup()
await manager.disconnect()
# Example usage
async def main():
ws_url = 'wss://hyperliquid-mainnet.core.chainstack.com/YOUR_ENDPOINT/evm'
async with managed_subscription(ws_url) as manager:
# Subscribe to different events
block_sub = await manager.subscribe("newHeads")
log_sub = await manager.subscribe("logs", {
"address": "0x5555555555555555555555555555555555555555"
})
sync_sub = await manager.subscribe("syncing")
# Handler for notifications
async def handle_notification(sub_id: str, result: Any):
if sub_id == block_sub:
print(f"New block: {int(result['number'], 16)}")
elif sub_id == log_sub:
print(f"New log from: {result['address']}")
elif sub_id == sync_sub:
if result is False:
print("Node is synchronized")
else:
print(f"Syncing: current block {int(result['currentBlock'], 16)}")
# Listen for 30 seconds
listen_task = asyncio.create_task(manager.listen(handle_notification))
await asyncio.sleep(30)
# Manual unsubscribe example (optional, cleanup will handle it anyway)
await manager.unsubscribe(block_sub)
listen_task.cancel()
print("All subscriptions cleaned up automatically")
# Run the example
asyncio.run(main())
// Complete subscription lifecycle with error handling
class RobustSubscriptionManager {
constructor(wsUrl, maxReconnectAttempts = 5) {
this.wsUrl = wsUrl;
this.maxReconnectAttempts = maxReconnectAttempts;
this.reconnectAttempts = 0;
this.subscriptions = new Map();
this.handlers = new Map();
}
async manageSubscription(type, params, duration, handler) {
console.log(`Starting ${type} subscription for ${duration}ms`);
try {
// Connect and subscribe
await this.connect();
const subId = await this.subscribe(type, params);
// Set up handler
this.handlers.set(subId, handler);
// Run for specified duration
await new Promise(resolve => setTimeout(resolve, duration));
// Cleanup
console.log(`Time's up, unsubscribing from ${type}...`);
const success = await this.unsubscribe(subId);
if (success) {
console.log(`✅ Successfully completed ${type} subscription lifecycle`);
} else {
console.log(`⚠️ Failed to cleanly unsubscribe from ${type}`);
}
} catch (error) {
console.error(`Error in subscription lifecycle: ${error.message}`);
} finally {
this.disconnect();
}
}
// ... rest of implementation similar to previous example
}
// Example: Monitor blocks for 1 minute
const manager = new RobustSubscriptionManager(CHAINSTACK_WSS_URL);
await manager.manageSubscription(
'newHeads',
{},
60000, // 1 minute
(block) => console.log(`Block ${parseInt(block.number, 16)}`)
);
eth_unsubscribe
method is essential for:
eth_unsubscribe
ensures efficient resource utilization and clean application shutdown, preventing memory leaks and unnecessary network traffic.Was this page helpful?