Introduction
In September 2024, Solana introduced a major update to its JavaScript library, now known as Solana Kit (formerly Solana Web3.js 2.0). This release brings significant improvements for developers building applications on the Solana blockchain. One of the key enhancements is a new, more robust system for handling WebSocket subscriptions to monitor on-chain events in real time.
This guide walks you through the process of implementing account monitoring using Solana Kit's updated subscription API. You'll learn how to track balance changes, handle errors effectively, and manage subscriptions with modern JavaScript patterns. Whether you're building a trading bot, a dashboard, or any application that requires real-time blockchain data, this approach offers better type safety and reliability than previous methods.
Key Improvements in Solana Kit
Solana Kit introduces several important upgrades over the previous Web3.js 1.0 library:
Enhanced Type Safety: The new API utilizes TypeScript generics and strict typing throughout, reducing runtime errors and improving developer experience.
Modern Async Iteration: Instead of callback functions, Solana Kit uses for await...of loops that conform to the modern async iterator protocol, making code more readable and maintainable.
Abort Controller Integration: Built-in support for subscription cleanup using AbortController allows you to easily cancel asynchronous operations when they're no longer needed.
Improved Error Handling: The library provides better error types and handling mechanisms, helping you build more resilient applications.
Prerequisites
Before you begin, make sure you have the following installed:
- Node.js (version 20.0 or higher recommended)
- npm or yarn package manager
- TypeScript and ts-node installed globally or in your project
Setting Up Your Development Environment
Create a New Project Directory
Start by creating a new directory for your project and navigating into it:
mkdir solana-subscriptions-v2 && cd solana-subscriptions-v2Initialize a New npm Project
Initialize a new npm project with default settings:
npm init -yInstall Required Dependencies
Install the Solana Kit library:
npm install @solana/kitInstall the development dependencies if you don't have them globally:
npm install --save-dev typescript ts-node @types/nodeConfigure TypeScript
Create a TypeScript configuration file (tsconfig.json) with the following content:
{
"compilerOptions": {
"module": "NodeNext",
"moduleResolution": "NodeNext",
"noEmit": true,
"target": "ESNext"
}
}Building the Account Monitor
Create the Main Application File
Create a new file called app.ts in your project directory. This file will contain all the code for monitoring Solana accounts.
Import Required Dependencies
Add the following imports to your app.ts file:
import {
createSolanaRpcSubscriptions,
RpcSubscriptions,
SolanaRpcSubscriptionsApi,
address,
Address
} from '@solana/kit';Define Constants
Below your imports, add the following constants:
const WSS_PROVIDER_URL = 'wss://your-quicknode-endpoint.example';
const LAMPORTS_PER_SOL = 1_000_000_000;
const PUMP_FUN_FEE_ACCOUNT = address("CebN5WGQ4jvEPvsVU4EoHEpgzq1VV7AbicfhtW4xC9iM");To build on Solana, you'll need an API endpoint to connect with the network. We're going to use a Solana Mainnet endpoint. Copy the WSS Provider Link from your provider and update your WSS_PROVIDER_URL constant to match the link.
We'll be monitoring the Pump.fun Fee Account for balance changes, but you can use any Solana account you prefer. Note that the @solana/kit library requires that we use the Address type from the libraryโwe create an Address from a string using the address function.
Create Helper Functions
Add the following helper function to format lamports as SOL:
const lamportsToSolString = (lamports: number, includeUnit = true): string => {
const solAmount = lamports / LAMPORTS_PER_SOL;
return `${solAmount.toLocaleString('en-US', {
minimumFractionDigits: 2,
maximumFractionDigits: 2
})}${includeUnit ? ' SOL' : ''}`;
};This function converts lamports to SOL with two decimal places and optionally includes the unit in the output string.
Define Interface for Tracking Arguments
Create an interface to define the arguments for our tracking function:
interface TrackAccountArgs {
rpcSubscriptions: RpcSubscriptions;
accountAddress: Address;
abortSignal: AbortSignal;
}In the new Solana Kit API, methods that were previously exposed through the Connection class are now available via two classes: Rpc for HTTP requests and RpcSubscriptions for WebSockets. The RpcSubscriptions class provides access to WebSocket methods for tracking on-chain events such as account changes, program changes, logs, and slots.
Implement the Account Tracking Function
Add the following function to your app.ts file:
async function trackAccount({ rpcSubscriptions, accountAddress, abortSignal }: TrackAccountArgs) {
let lastLamports: number | null = null;
try {
const accountNotifications = await rpcSubscriptions
.accountNotifications(accountAddress, { commitment: 'confirmed' })
.subscribe({ abortSignal });
try {
for await (const notification of accountNotifications) {
const { slot } = notification.context;
const currentLamports = Number(notification.value.lamports);
const delta = lastLamports !== null ? currentLamports - lastLamports : 0;
const sign = delta > 0 ? '+' : delta < 0 ? '-' : ' ';
console.log(` Account change detected at slot ${slot.toLocaleString()}. New Balance: ${lamportsToSolString(currentLamports)} (${sign}${lamportsToSolString(Math.abs(delta))})`);
lastLamports = currentLamports;
}
} catch (error) {
throw error;
}
} catch (error) {
throw error;
}
}Let's break down how this function works:
- We define a variable called
lastLamportsand set it tonullto store the last known balance of the account - We create a
try/catchblock to handle errors when creating our subscription - Inside the
tryblock, we call theaccountNotificationsmethod on therpcSubscriptionsobject - We create another
try/catchblock to handle errors when processing notifications - We use a
for await...ofloop to iterate over notifications received from the subscription - For each notification, we extract the slot information and current lamports balance
- We calculate the balance change and format the output for display
Create the Entry Point Function
Add the following function to execute your tracking script:
async function main() {
console.log(`๐ Tracking Pump.fun Fee Account: ${PUMP_FUN_FEE_ACCOUNT} ๐`);
const rpcSubscriptions = createSolanaRpcSubscriptions(WSS_PROVIDER_URL);
const abortController = new AbortController();
try {
await trackAccount({
rpcSubscriptions,
accountAddress: PUMP_FUN_FEE_ACCOUNT,
abortSignal: abortController.signal
});
} catch (e) {
console.log('Subscription error', e);
} finally {
abortController.abort();
}
}
main();This function serves as the entry point for our script. It creates an instance of the RpcSubscriptions class using our WebSocket provider URL, sets up an AbortController for subscription management, and calls our trackAccount function with the appropriate parameters.
Running the Account Monitor
When you're ready to start monitoring, run your script using ts-node:
npx ts-node app.tsThe monitor will begin tracking changes to the specified account and display balance changes in this format:
Account change detected at slot 301,428,932. New Balance: 265,598.16 SOL (+0.14 SOL)๐ Explore real-time monitoring tools to enhance your blockchain development workflow.
Understanding Billing and Optimization
When using WebSocket subscriptions, it's important to understand how billing works. Credits are typically based on the number of responses received rather than the number of subscriptions created. For example, if you open an accountNotifications subscription and receive 100 responses, you might be billed based on your provider's pricing structure.
To optimize your subscriptions and minimize costs:
- Use AbortController or similar mechanisms to cancel subscriptions when they're no longer needed
- Implement filters where available to receive only relevant data
- Monitor your usage patterns and adjust subscription parameters accordingly
- Consider the frequency of updates needed for your specific use case
Alternative Solutions for Real-Time Data
While WebSockets provide a direct connection to Solana nodes for real-time updates, there are several alternative approaches for accessing blockchain data:
WebSockets: Ideal for simple applications and rapid development, offering direct connections to Solana nodes with low latency.
gRPC Interfaces: Solutions like Yellowstone Geyser gRPC provide powerful streaming capabilities with built-in filtering and historical data support.
Managed Streaming Services: Fully managed solutions that process and route Solana data to multiple destinations, often with additional features like built-in filtering and historical data access.
Each approach has its strengths depending on your specific requirements for scalability, complexity, and maintenance overhead.
Frequently Asked Questions
What is Solana Kit and how does it differ from Web3.js?
Solana Kit is the updated version of Solana Web3.js, featuring improved type safety, modern async iteration patterns, and better error handling. It represents a significant upgrade over the previous library with more robust APIs for blockchain interaction.
Do I need a special endpoint for WebSocket connections?
Yes, you typically need a WebSocket endpoint (wss://) from your node provider. Regular HTTP endpoints won't work for real-time subscriptions. Most providers offer separate endpoints for WebSocket connections.
How can I monitor multiple accounts simultaneously?
You can create multiple subscriptions using the same RpcSubscriptions instance. However, be mindful of rate limits and potential performance implications when monitoring a large number of accounts.
What happens if my WebSocket connection drops?
The Solana Kit library includes improved error handling, but you should implement reconnection logic in your application to handle network interruptions gracefully. The abort controller pattern helps with cleaning up stale connections.
Can I filter the types of account changes I receive?
The basic accountNotifications subscription sends all changes, but you can implement your own filtering logic in the notification handler. Some alternative solutions offer more advanced filtering capabilities.
How does this approach compare to polling for account changes?
WebSocket subscriptions are more efficient than polling because they push changes only when they occur, reducing unnecessary network traffic and latency. Polling requires frequent requests regardless of whether changes have happened.
Conclusion
Solana Kit provides a more robust, type-safe approach to handling Solana WebSocket subscriptions compared to previous libraries. The new API simplifies subscription management, error handling, and resource cleanup, making it easier to build reliable applications that monitor blockchain events in real time.
By following the patterns outlined in this guide, you can implement account monitoring that is both efficient and maintainable. The modern JavaScript features and improved developer experience make Solana Kit an excellent choice for new projects and a worthwhile upgrade for existing applications.
As you continue building on Solana, remember to optimize your subscriptions based on your specific needs and monitor your usage to ensure cost-effective operation. The flexibility of the Solana Kit API allows you to create sophisticated monitoring solutions that can scale with your application's requirements.
๐ Discover advanced monitoring strategies to take your Solana development to the next level.