- Docs
- Developers
- Using websockets
Using Websockets
arch3.js is a wrapper of cosmjs which makes it easier to interact with Archway's native modules.
In this section we will look into how to use the websocket functionality provided by cosmjs to subscribe to events from the Archway network. More specifically, we will explain how to use WebSockets with the @cosmjs/tendermint-rpc package, which is available within the arch3.js.
Dependencies
- Install npm and node
- Install @archwayhq/arch3.js.
Create a new project and install dependencies
Make sure to have npm and node installed, then create a new node project by launching:
npm init -y
Install @archwayhq/arch3.js by launching:
npm install --save @archwayhq/arch3.js
And install the uuid package with:
npm install uuid
Create an index.js
Within your project directory, create an index.js and paste the following code:
// Import the WebSocket library for real-time bidirectional communication.const WebSocket = require('ws');// Import the UUID library to generate a unique ID for each subscription request.const { v4: uuidv4 } = require('uuid');// Define the address that is checked in the transactions.const address = 'archway1cc0kxd3y4670mzhgz5j34s5euzppwhgp4cljjc';// Initialize websocket and wsQuery variables.let websocket;let wsQuery;// This function initiates a WebSocket connection and sends a subscription request to track transactions that fulfill certain conditions.const queryForBalanceUpdate = () => { try { // Open a new WebSocket connection to the specified URL. websocket = new WebSocket('wss://rpc.constantine.archway.io:443/websocket'); // Define the subscription request. It asks for transactions where the recipient address, and checks for transactions to be published. wsQuery = { jsonrpc: '2.0', method: 'subscribe', id: uuidv4().toString(), params: { query: `tm.event = 'Tx' AND transfer.recipient CONTAINS '${address}'`, }, }; // When the WebSocket connection is established, send the subscription request. websocket.on('open', () => { websocket.send(JSON.stringify(wsQuery)); }); // When a message (i.e., a matching transaction) is received, log the transaction and close the WebSocket connection. websocket.on('message', (event) => { const eventData = JSON.parse(event); if (eventData && eventData.result && eventData.result.data) { console.log('Matching transaction found' + JSON.stringify(eventData.result.data)); disconnectFromWebsocket(); } }); // If an error occurs with the WebSocket, log the error and close the WebSocket connection. websocket.on('error', (error) => { console.error(error); disconnectFromWebsocket(); }); } catch (err) { // If an error occurs when trying to connect or subscribe, log the error and close the WebSocket connection. console.error(err); disconnectFromWebsocket(); }};// This function closes the WebSocket connection and resets the websocket and wsQuery variables.const disconnectFromWebsocket = () => { // If the WebSocket isn't open, exit the function. if (!websocket || websocket.readyState !== WebSocket.OPEN) return; // Send an 'unsubscribe' message to the server. websocket.send(JSON.stringify({ ...wsQuery, method: 'unsubscribe' })); // Close the WebSocket connection. websocket.close(); // Reset the websocket and wsQuery variables. websocket = null; wsQuery = null;};// When the process is exiting, close the WebSocket connection if it's still open.process.on('exit', () => { disconnectFromWebsocket();});// Start the process by calling the queryForBalanceUpdate function.queryForBalanceUpdate();
The above code sets up a WebSocket client to connect to a Tendermint RPC server and subscribes to transaction events. We are using the @cosmjs/tendermint-rpc package to interact with the Archway network via WebSocket, and the code outputs the details of transactions as soon as they occur as a JSON.
The essential part of the script is the queryForBalanceUpdate function, which establishes a WebSocket connection using the WebsocketClient class from the @cosmjs/tendermint-rpc package. It then creates a subscription using the listen method and the specified query parameters.
The query parameters can be set to subscribe to transactions where the recipient's address, the IBC destination channel, and the fungible token denom match the specified values. In this case we are listening to all the transactions for a specific archway address ${address}
**`. The WebSocket connection is closed by calling the disconnect method.
Note that we are using the wss://rpc.constantine.archway.io:443/websocket endpoint for testnet, and the wss:///rpc.mainnet.archway.io:443 for mainnet. Make sure to check the networks page for details on the available RPC endpoints.
The provided code sets up a WebSocket client to connect to a Tendermint RPC server and subscribes to transaction events by using the native WebSocket object from the ws Node.js library to interact with the Archway network via WebSocket. This code logs the details of transactions that match the subscription query as they occur.
The core of the script is the queryForBalanceUpdate function, which establishes a WebSocket connection to the wss://rpc.constantine.archway.io:443/websocket endpoint. It then sends a JSON-RPC 2.0 request with a method of subscribe and a specific query to listen for matching transactions.
The query parameters are set to subscribe to transactions where the recipient's address, the IBC destination channel, and the fungible token denom all match the specified values. Specifically, it listens for transactions where the recipient's address is ${address}
, the IBC destination channel is ${channel}
, and the fungible token denom is ${externalDenom}
.
When the WebSocket connection is open, it sends the subscription request to the server. If a transaction matches the subscription query, the server sends a message to our client. The client logs the matching transaction data and disconnects from the WebSocket server by sending an 'unsubscribe' message, closing the WebSocket connection, and setting the websocket and wsQuery variables to null.
If an error occurs during the WebSocket communication or if an error is caught during the execution of the queryForBalanceUpdate function, the error is logged, and the WebSocket connection is closed in the same manner. Additionally, if the process is exiting, the WebSocket connection is properly closed if it's still open.
Run the WebSocket
To start listening to events, launch:
node index.js
Now that the websocket connection is established, you can send a transaction to the hardcoded address (just make sure to set your own). For simplicity, you can use the Discord faucet to do it.
Subscribe to events
Now, you can check how to subscribe to the WebSockets events , and look at the available queries Tendermint documentation.
Finding events
The easiest way to find the list of events linked to a particular transaction is to check the events logs within a block explorer. These event logs will also show fields that can be used as additional filters. For example the following transaction https://www.mintscan.io/archway/tx/A2EFABA14F892C0E559205F5B0B3FB0D1F9533E086BEDAF513954EE2EAC63F69?height=1628545 has the coin_received field and you can also use the receiver field as a filter. The final filter would be tm.event = 'Tx' AND coin_received.receiver CONTAINS 'archway1du5canv32dd7qsp5kz470ancmcn6r6650qdn9z7wp9ke7wnhpycqztx9js'.
Listening to IBC packets
For example, you can modify the index.js to declare a channel and externalDenom variables, and then listen to IBC packets sent to a specific address:
const externalDenom = 'uaxl';const channel = 'channel-8';
And modifiying the line:
query: `tm.event = 'Tx' AND transfer.recipient CONTAINS '${address}'`,
into:
query: `tm.event = 'Tx' AND transfer.recipient CONTAINS '${address}' AND write_acknowledgement.packet_dst_channel = '${channel}' AND fungible_token_packet.denom = '${externalDenom}'`,
So that your index.js would become:
// Import the WebSocket library for real-time bidirectional communication.const WebSocket = require('ws');// Import the UUID library to generate a unique ID for each subscription request.const { v4: uuidv4 } = require('uuid');// Define the address that is checked in the transactions.const address = 'archway1cc0kxd3y4670mzhgz5j34s5euzppwhgp4cljjc';// Define the denomination of the token to track.const externalDenom = 'uaxl';// Initialize websocket and wsQuery variables.let websocket;let wsQuery;// Define the channel that is used for the transactions on the Axelar testnet.const channel = 'channel-2';// This function initiates a WebSocket connection and sends a subscription request to track transactions that fulfill certain conditions.const queryForBalanceUpdate = () => { try { // Open a new WebSocket connection to the specified URL. websocket = new WebSocket('wss:////rpc.mainnet.archway.io:443/websocket'); // Define the subscription request. It asks for transactions where the recipient address, and checks for transactions to be published. wsQuery = { jsonrpc: '2.0', method: 'subscribe', id: uuidv4().toString(), params: { query: `tm.event = 'Tx' AND transfer.recipient CONTAINS '${address}'`, }, }; // When the WebSocket connection is established, send the subscription request. websocket.on('open', () => { websocket.send(JSON.stringify(wsQuery)); }); // When a message (i.e., a matching transaction) is received, log the transaction and close the WebSocket connection. websocket.on('message', (event) => { const eventData = JSON.parse(event); if (eventData && eventData.result && eventData.result.data) { console.log('Matching transaction found' + JSON.stringify(eventData.result.data)); disconnectFromWebsocket(); } }); // If an error occurs with the WebSocket, log the error and close the WebSocket connection. websocket.on('error', (error) => { console.error(error); disconnectFromWebsocket(); }); } catch (err) { // If an error occurs when trying to connect or subscribe, log the error and close the WebSocket connection. console.error(err); disconnectFromWebsocket(); }};// This function closes the WebSocket connection and resets the websocket and wsQuery variables.const disconnectFromWebsocket = () => { // If the WebSocket isn't open, exit the function. if (!websocket || websocket.readyState !== WebSocket.OPEN) return; // Send an 'unsubscribe' message to the server. websocket.send(JSON.stringify({ ...wsQuery, method: 'unsubscribe' })); // Close the WebSocket connection. websocket.close(); // Reset the websocket and wsQuery variables. websocket = null; wsQuery = null;};// When the process is exiting, close the WebSocket connection if it's still open.process.on('exit', () => { disconnectFromWebsocket();});// Start the process by calling the queryForBalanceUpdate function.queryForBalanceUpdate();
Make sure to check the IBC channels page for details on the updated IBC channels.
Listening to wasm events
You can listen to wasm events by querying the data inside the events within the Cosmos-SDK transaction.
To filter transactions that have a specific event type, like wasm, you use a query like:
query: `tm.event = 'Tx' AND wasm EXISTS`,
This tells the system to look for transactions that contain the wasm event.
If you want to find transactions related to a particular contract address, you can add an extra filter like:
`wasm._contract_address = '${address}'`
This fetches transactions where the wasm event has the specified contract address.