WebSocket Event Listening on Somnia
This guide teaches developers how to create WebSocket connections to listen for smart contract events on the Somnia network in real-time. We'll use a simple Greeting contract as an example to demonstrate the core concepts.
Resources
Somnia Network RPC
https://dream-rpc.somnia.network
Somnia WebSocket
wss://dream-rpc.somnia.network/ws
Example Contract:
0xADA7b2953E7d670092644d37b6a39BAE3237beD7
Example Script
Prerequisites
Node.js installed (v14 or higher)
Basic understanding of JavaScript
A deployed smart contract on Somnia network
What are WebSockets?
WebSockets are a communication protocol that provides bidirectional communication channels over a single TCP connection. Unlike traditional HTTP requests, WebSockets maintain a persistent connection between the client and server.
How WebSockets Work
WebSockets begin with a connection establishment phase where the client initiates a WebSocket handshake through an HTTP upgrade request. Once established, the connection stays open as a persistent channel between client and server. This enables bidirectional communication where both client and server can send messages at any time without waiting for requests. The protocol maintains low latency since there's no need to establish new connections for each message exchange. This design is highly efficient with minimal protocol overhead compared to traditional HTTP polling approaches.
WebSocket Connection Lifecycle
Client Server
| |
|----Connection Request-->|
|<---Connection Accept----|
| |
|<===Open Connection====> |
| |
|----Send Message-------->|
|<---Receive Message------|
|<---Push Notification----|
|----Send Message-------->|
| |
|<===Open Connection====> |
| |
|----Close Connection---->|
WebSocket vs HTTP Polling
HTTP Polling Approach
// Inefficient: Constantly asking "Any updates?"
setInterval(async () => {
const response = await fetch('https://api.example.com/events');
const data = await response.json();
if (data.hasNewEvents) {
console.log('New event:', data.events);
}
}, 5000); // Check every 5 seconds
The problem with polling is that Polling wastes bandwidth by constantly checking for updates even when none exist, leading to unnecessary network traffic. This approach introduces delays of up to the polling interval (5 seconds in our example), meaning users might wait several seconds to see new events that have already occurred. Additionally, the server must process these unnecessary requests repeatedly, increasing computational load and infrastructure costs. For blockchain applications, this translates to higher costs for RPC providers who often charge based on the number of requests made.
WebSocket Approach
// Efficient: Server pushes updates immediately
const ws = new WebSocket('wss://api.example.com/events');
ws.on('message', (data) => {
console.log('New event:', data); // Instant notification
});
WebSockets provide real-time updates measured in milliseconds rather than seconds, ensuring users receive notifications instantly when events occur. This eliminates wasted requests since the server only sends data when there are actual updates, significantly reducing bandwidth usage. The reduced request frequency leads to lower server load and better resource utilization. For users, this translates to a superior experience with immediate feedback, while developers benefit from reduced infrastructure costs.
Blockchain Events and WebSockets
Smart contracts emit events when important state changes occur. These events are included in transaction receipts and stored in blockchain logs.
The event flow begins when a user calls a Smart Contract function through a Transaction. During execution, the contract updates its Internal State and emits Events containing relevant data about the changes. These Transactions and their associated Events are then included in a new block by Validators. Once the Block is finalized, Nodes across the network broadcast it to their peers. WebSocket connections instantly notify connected clients about these new events, enabling real-time reactions to Blockchain state changes.
Example Use Cases for WebSocket Event Listening
DeFi Applications
Monitor price updates on DEX swaps
NFT Marketplaces
Live bidding updates in auctions
Gaming DApps
Real-time game state updates
DAOs and Governance
Live voting updates
Supply Chain
Product status updates
Indexed Parameters in Events
When you mark an event parameter as indexed
in Solidity, it becomes part of the event's topics rather than the data section. This enables efficient filtering but changes how you access the data.
Non-Indexed String (accessible directly):
event MessageSent(string message); // Can read 'message' directly from logs
Indexed String (hashed for filtering):
event MessageSent(string indexed message); // 'message' is hashed, cannot read directly
Why Use Indexed Parameters?
Indexed
parameters enable efficient filtering by allowing nodes to quickly find specific events without scanning through all logs in a block. They provide gas optimization since topics are more gas-efficient for filtering operations compared to parsing event data. Additionally, nodes can index and search these parameters significantly faster, improving overall query performance when applications need to find specific events based on parameter values.
The Trade-off
For strings and bytes, indexing means:
Can filter events by this parameter efficiently
Cannot retrieve the actual value from the event log
Must query contract state to get the current value
This is why, in our example, when we receive a GreetingSet
event with indexed string parameters, we call contract.getGreeting() to retrieve the actual greeting text.
The pattern for listening to blockchain events via WebSocket follows these principles:
The process begins by establishing a connection to the blockchain node's WebSocket endpoint, ensuring a persistent communication channel. Once connected, you create a filter that defines which events from which contracts to monitor, allowing precise event targeting. Next, you set up listener functions that register callbacks to execute when specific events occur. When events are received, your handler functions process the event data according to your application's needs. Throughout the connection lifetime, you must maintain the connection with periodic activity to prevent timeouts. Finally, when your application terminates, ensure a clean shutdown by properly closing all connections and removing event listeners.
Code Breakdown
Connect to Somnia WebSocket
const wsUrl = 'wss://dream-rpc.somnia.network/ws';
const provider = new ethers.WebSocketProvider(wsUrl);
await provider._waitUntilReady();
The WebSocket URL for Somnia mainnet is wss://dream-rpc.somnia.network/ws
. This creates a persistent connection to the network.
Create Contract Instance
const contract = new ethers.Contract(contractAddress, abi, provider);
You need:
Contract address
: The deployed address on Somnia.ABI
: At minimum, include the events you want to listen for.Provider
: The WebSocket connection.
Define Event Filter
const filter = {
address: contractAddress,
topics: [ethers.id("GreetingSet(string,string)")]
};
The filter specifies:
Which contract to monitor
Which event signature to listen for
Set Up Event Listener
provider.on(filter, async (log) => {
// Handle the event
const greeting = await contract.getGreeting();
console.log(`New greeting: "${greeting}"`);
});
When an event is detected:
The callback receives the log data
Query the contract for the current state
Process/display the data as needed
Maintain Connection
setInterval(async () => {
await provider.getBlockNumber();
}, 30000);
This is because WebSocket connections can timeout. Send periodic requests to keep the connection alive.
Update the ABI
Include only the events and functions you need:
const abi = [
// Your event definition
{
"anonymous": false,
"inputs": [
// Your event parameters
],
"name": "YourEventName",
"type": "event"
},
// Any read functions you need
{
"inputs": [],
"name": "yourReadFunction",
"outputs": [/* outputs */],
"stateMutability": "view",
"type": "function"
}
];
Update the Event Filter
Change the event signature to match your event:
const filter = {
address: contractAddress,
topics: [ethers.id("YourEventName(type1,type2)")]
};
Handle Event Data
Process the event based on your needs:
provider.on(filter, async (log) => {
// For non-indexed parameters, you can parse the log
const parsedLog = contract.interface.parseLog(log);
// For indexed strings, query the contract state
const currentState = await contract.yourReadFunction();
// Process your data
console.log('Event detected:', currentState);
});
Common Patterns
Multiple Events
Listen for multiple events from the same contract:
// Listen for Event1
provider.on({
address: contractAddress,
topics: [ethers.id("Event1(...)")]
}, handleEvent1);
// Listen for Event2
provider.on({
address: contractAddress,
topics: [ethers.id("Event2(...)")]
}, handleEvent2);
Error Recovery
Add reconnection logic for production applications:
async function connectWithRetry() {
let retries = 0;
while (retries < 5) {
try {
await listen();
break;
} catch (error) {
console.log(`Retry ${++retries}/5...`);
await new Promise(r => setTimeout(r, 5000));
}
}
}
Event History
Get recent events on startup:
// Get last 100 blocks of events
const currentBlock = await provider.getBlockNumber();
const events = await contract.queryFilter('YourEventName', currentBlock - 100, currentBlock);
events.forEach(event => {
console.log('Historical event:', event);
});
Test Your WebSocket Connection
Deploy your Smart Contract to Somnia network.
Run the listener in one terminal:
node websocket-listener.js
Trigger events from another script or dApp
Observe real-time updates in your listener
Conclusion
WebSocket connections provide real-time event monitoring for smart contracts on Somnia. This guide demonstrated:
Connecting to Somnia's WebSocket endpoint
Listening for specific contract events
Handling indexed parameters correctly
Maintaining stable connections
Adapting the pattern for any smart contract
With this foundation, you can build responsive dApps that react instantly to blockchain events without polling.
Last updated