Documentation Index
Fetch the complete documentation index at: https://docs.chainstack.com/llms.txt
Use this file to discover all available pages before exploring further.
This method is available on Chainstack. Not all Hyperliquid methods are available on Chainstack, as the open-source node implementation does not support them yet — see Hyperliquid methods for the full availability breakdown.
The 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.
Get your own node endpoint todayStart for free and get your app to production levels immediately. No credit card required.You can sign up with your GitHub, X, Google, or Microsoft account.
Parameters
- subscription ID (string, required): The subscription identifier returned by
eth_subscribe when the subscription was created
Response
The method returns a boolean value indicating whether the unsubscribe operation was successful.
Response structure
true — the subscription was successfully cancelled
false — the subscription was not found or could not be cancelled
Usage example
Basic implementation
Note that subscriptions require a WebSocket connection. Install WebSocket cat for testing:
$ 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}
JavaScript implementation with subscription management
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();
Python implementation with context manager
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())
Subscription lifecycle management
// 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)}`)
);
Use cases
The eth_unsubscribe method is essential for:
- Resource management: Free up server and client resources by canceling unused subscriptions
- Dynamic subscription control: Change subscription parameters by unsubscribing and resubscribing
- Application lifecycle management: Clean up subscriptions when components unmount or disconnect
- Rate limiting compliance: Manage the number of active subscriptions to stay within limits
- Cost optimization: Reduce bandwidth and processing costs by removing unnecessary subscriptions
- Error recovery: Cancel problematic subscriptions that are causing issues
- Testing and development: Clean up test subscriptions during development
- User preference changes: Update subscriptions when users change monitoring preferences
- Performance optimization: Remove subscriptions that are no longer needed to improve performance
- Connection management: Properly clean up before reconnecting or switching endpoints
Proper use of eth_unsubscribe ensures efficient resource utilization and clean application shutdown, preventing memory leaks and unnecessary network traffic.