Marketplace has launched, further enabling blockchain developers! Learn more

How to Create Websocket Subscriptions to Solana Blockchain using Typescript

September 23, 2022

Overview

Creating event listeners is an effective way to alert your app or your users that something you're monitoring has changed. Solana has several built-in handy event listeners, also known as subscriptions, that make listening for changes on the Solana Blockchain a breeze. Not sure how you would use that? Here are a couple of examples where this might come in handy: a Discord bot that looks for interactions with an on-chain Program (e.g., sales bot), a dApp that checks for errors with a user's transaction, or a phone notification when the balance of a user's wallet changes.

What You Will Do

In this guide, you will learn how to use several Solana event listener methods and QuickNode's Websocket endpoints (WSS://) to listen for changes on chain. Specifically, you will create a simple typescript application to track for changes in an Account (or Wallet). Then you will learn how to use Solana's unsubscribe methods to remove a listener from your application. We'll also cover basics about some of Solana's other listener methods.

What You Will Need

  • Nodejs installed (version 16.15 or higher)
  • npm or yarn installed (We will be using yarn to initialize our project and install the necessary packages. Feel free to use npm instead if that’s your preferred package manager)
  • Typescript experience and ts-node installed
  • Solana Web3

Set Up Your Environment

Create a new project directory in your terminal with:

set up your environment

Copy
mkdir solana-subscriptions
cd solana-subscriptions

Create a file, app.ts:

set up your environment

Copy
echo > app.ts

Initialize your project with the "yes" flag to use default values for your new package: 

set up your environment

Copy
yarn init --yes
#or
npm init --yes

Install Solana Web3 dependencies:


set up your environment

Copy
yarn add @solana/web3.js
#or
npm install @solana/web3.js

Open app.ts in your preferred code editor and on line 1, import Connection, PublicKey, and LAMPORTS_PER_SOL from the Solana Web3 library: 

set up your environment

Copy
import { Connection, PublicKey, LAMPORTS_PER_SOL, } from "@solana/web3.js";

Alright! We're all ready to go. 

Set Up Your Quicknode Endpoint

To build on Solana, you'll need an API endpoint to connect with the network. You're welcome to use public nodes or deploy and manage your own infrastructure; however, if you'd like 8x faster response times, you can leave the heavy lifting to us. See why over 50% of projects on Solana choose QuickNode and sign up for a free account here.
We're going to use a Solana Devnet node. Copy the HTTP Provider and WSS Provider links:

New Solana Endpoint

Create two new variables on lines 3 and 4 of app.js to store these URLs: 

set up your quicknode endpoint

Copy
const WSS_ENDPOINT = 'wss://example.solana-devnet.quiknode.pro/000/'; // replace with your URL
const HTTP_ENDPOINT = 'https://example.solana-devnet.quiknode.pro/000/'; // replace with your URL

Establish a Connection to Solana

On line 5, create a new Connection to Solana: 

establish a connection to solana

Copy
const solanaConnection = new Connection(HTTP_ENDPOINT,{wsEndpoint:WSS_ENDPOINT});

If you've created Connection instances to Solana in the past, you may notice something different about our parameters, particularly the inclusion of {wsEndpoint:WSS_ENDPOINT}. Let's dig in a little deeper. 

The Connection class constructor allows us to pass an optional commitmentOrConfig. There are a few interesting options we can include with the ConnectionConfig, but today we're going to hone in on the optional parameter, wsEndpoint. This is an option for you to provide an endpoint URL to the full node JSON RPC PubSub Websocket Endpoint. In our case, that's our WSS Endpoint we defined earlier, WSS_ENDPOINT.

What happens if you don't pass a wsEndpoint? Well, Solana accounts for that with a function, makeWebsocketUrl that replaces your endpoint URL's https with wss or http with ws (source). Because all QuickNode HTTP endpoints have a corresponding WSS endpoint with the same authentication token, it is fine for you to omit this parameter unless you'd like to use a separate endpoint for your Websocket queries.

Let's create some subscriptions!

Create an Account Subscription

To track a wallet on Solana, we'll need to call the onAccountChange method on our solanaConnection. We will pass ACCOUNT_TO_WATCH, the public key of the wallet we'd like to search, and a callback function. We've created a simple log that alerts us that an event has been detected and log the new account balance. Add this snippet to your code after your solanaConnection declaration on line 7: 

create an account subscription

Copy
(async()=>{
    const ACCOUNT_TO_WATCH = new PublicKey('vines1vzrYbzLMRdu58ou5XTby4qAqVRLmqo36NKPTg'); // Replace with your own Wallet Address
    const subscriptionId = await solanaConnection.onAccountChange(
        ACCOUNT_TO_WATCH,
        (updatedAccountInfo) =>
            console.log(`---Event Notification for ${ACCOUNT_TO_WATCH.toString()}--- \nNew Account Balance:`, updatedAccountInfo.lamports / LAMPORTS_PER_SOL, ' SOL'),
        "confirmed"
    );
    console.log('Starting web socket, subscription ID: ', subscriptionId);
})()

Build a Simple Test

This code is ready to run as is, but we're going to add one more functionality to help us test that it's working properly. We'll do this by adding a sleep function (to add a time delay) and an airdrop request. Before your async code block on line 6, add: 

build a simple test

Copy
const sleep = (ms:number) => {
    return new Promise(resolve => setTimeout(resolve, ms));
}

And then, inside your async code block after the "Starting web socket" log, add this airdrop call:

build a simple test

Copy
    await sleep(10000); //Wait 10 seconds for Socket Testing
    await solanaConnection.requestAirdrop(ACCOUNT_TO_WATCH, LAMPORTS_PER_SOL);

Your code will effectively wait 10 seconds after the socket has initiated to request an airdrop to the wallet (note: this will only work on devnet and testnet). 

Our code now looks like this: 

build a simple test

Copy
import { Connection, PublicKey, LAMPORTS_PER_SOL, } from "@solana/web3.js";

const WSS_ENDPOINT = 'wss://example.solana-devnet.quiknode.pro/000/'; // replace with your URL
const HTTP_ENDPOINT = 'https://example.solana-devnet.quiknode.pro/000/'; // replace with your URL
const solanaConnection = new Connection(HTTP_ENDPOINT, { wsEndpoint: WSS_ENDPOINT });
const sleep = (ms:number) => {
    return new Promise(resolve => setTimeout(resolve, ms));
}

(async () => {
    const ACCOUNT_TO_WATCH = new PublicKey('vines1vzrYbzLMRdu58ou5XTby4qAqVRLmqo36NKPTg');
    const subscriptionId = await solanaConnection.onAccountChange(
        ACCOUNT_TO_WATCH,
        (updatedAccountInfo) =>
            console.log(`---Event Notification for ${ACCOUNT_TO_WATCH.toString()}--- \nNew Account Balance:`, updatedAccountInfo.lamports / LAMPORTS_PER_SOL, ' SOL'),
        "confirmed"
    );
    console.log('Starting web socket, subscription ID: ', subscriptionId);
    await sleep(10000); //Wait 10 seconds for Socket Testing
    await solanaConnection.requestAirdrop(ACCOUNT_TO_WATCH, LAMPORTS_PER_SOL);
})()

Start Your Socket!


Let's go ahead and test it out. In your terminal you can enter ts-node app.ts to start your web socket! After about 10 seconds, you should see a terminal callback log like this:

start your socket

Copy
solana-subscriptions % ts-node app.ts
Starting web socket, subscription ID:  0
---Event Notification for vines1vzrYbzLMRdu58ou5XTby4qAqVRLmqo36NKPTg--- 
New Account Balance: 88790.51694709  SOL

Excellent! You should notice that your application remains open even after the event notification. That's because our subscription is still listening for changes to our account. We need a way to unsubscribe to the listener. Hit Ctrl^C to stop the process.

Unsubscribe from Account Change Listener

Solana has created a built-in method that we can use to unsubscribe from our Account Change listener, removeAccountChangeListener. The method accepts a valid subscriptionId (number) as its only parameter. Inside your async block, add another sleep after your airdrop to allow time for the transaction to process and then call removeAccountChangeListener

unsubscribe from account change listener

Copy
    await sleep(10000); //Wait 10 for Socket Testing
    await solanaConnection.removeAccountChangeListener(subscriptionId);
    console.log(`Websocket ID: ${subscriptionId} closed.`);

Now run your code again, and you should see the same sequence followed by the closing of the Websocket: 

unsubscribe from account change listener

Copy
solana-subscriptions % ts-node app.ts
Starting web socket, subscription ID:  0
---Event Notification for vines1vzrYbzLMRdu58ou5XTby4qAqVRLmqo36NKPTg--- 
New Account Balance: 88791.51694709  SOL
Websocket ID: 0 closed.

Nice job! As you can see, this can be useful if you want to disable a listener after something has happened (e.g., time elapsed, certain threshold realized, number of notifications, etc.). 

We've published the final code for this script to our Github repo for your reference.

Other Solana Websocket Subscriptions

Solana has several other, similar Websocket subscribe/unsubscribe methods that are also useful. We'll describe them briefly in this section.
  • onProgramAccountChange: Register a callback to be invoked whenever accounts owned by the specified program change. Pass a Program's PublicKey and an optional array of account filters. Among other things, this can be handy for tracking changes to a user's Token Account. Unsubscribe with removeProgramAccountChangeListener.
  • onLogs: Registers a callback to be invoked whenever logs are emitted--can be used similar to onAccountChange by passing a valid PublicKey. The callback will return recent transaction ID. Unsubscribe with removeOnLogsListener
  • onSlotChange: Register a callback to be invoked upon slot changes. Only a callback function is passed into this method. Unsubscribe with removeSlotChangeListener
  • onSignature: Register a callback to be invoked upon signature updates by passing a valid Transaction Signature. The callback will return whether or not the transaction has experienced an error. Unsubscribe with removeSignatureListener. onRootChange: Register a callback to be invoked upon root changes. Only a callback function is passed into this method. Unsubscribe with removeRootChangeListener
*Note: all unsubscribe methods require a subscriptionId (number) parameter. You can find more information on these methods in our documentation at  quicknode.com/docs/solana. Feel free to experiment with each by making minor modifications to app.js to try some of these other subscriptions.  

Conclusion

Nice work! Now you should have a handle on how to use Websocket subscriptions on Solana. How are you using Solana Websockets? We'd love to see what you're creating! Share your app with us on Discord or Twitter. If you have any feedback or questions on this guide, we’d love to hear from you

To learn more, check out some of our other Solana tutorials here, and if you've had fun with Websocket subscriptions, consider subscribing to our newsletter.  

Related articles 33

Como crear un NFT en SOLANA
Published: Dec 27, 2021
Updated: Sep 23, 2022

¡Hola querido lector! Bienvenidos a una nueva guía de Solana.Solana es una blockchain que promete mucho a la hora de intentar resolver los problemas de escalabilidad que...

Continue reading
How to Get Transaction Logs on Solana
Published: Jun 24, 2022
Updated: Oct 27, 2022

Ever need to pull all the transactions associated with a Wallet? Want to see all of the mint transactions associated with a Candy Machine? Or maybe see transaction history of an NFT? Solana's...

Continue reading
How to Transfer SPL Tokens on Solana
Published: Sep 23, 2022
Updated: Sep 23, 2022

Sending Solana Program Library (SPL) Tokens is a critical mechanism for Solana development. Whether you are airdropping whitelist tokens to your community, bulk sending NFTs to another wallet,...

Continue reading
How to Mint an NFT on Solana
Published: Aug 27, 2021
Updated: Sep 23, 2022

Updated at: April 10, 2022Welcome to another QuickNode guide on Solana - the up-and-coming blockchain that seeks to solve the scalability issues of Ethereum. We will be walking through...

Continue reading
How to Send Bulk Transactions on Solana
Published: Aug 31, 2022
Updated: Oct 3, 2022

Are you running a batch process that has many transactions? Perhaps an airdrop to your community's NFT holders or a token distribution to early users of your dApp. Solana transaction...

Continue reading
Solana Fundamentals Reference Guide
Published: Oct 27, 2022
Updated: Oct 27, 2022

The Solana blockchain is a powerful tool, delivering thousands of transactions per second with almost no-cost transaction fees. If you are new to Web3 or have developed on EVM-based...

Continue reading