11 min read
Overview
Building real-time applications on Solana, such as trading bots, analytics dashboards, or notification services, requires fast and reliable access to onchain events. Decoding raw transaction data is often slow and complex, creating a significant development hurdle.
This is where the LYS Labs' Structured Real-Time Solana Data add-on comes in. It provides a simple WebSocket stream of decoded, human-readable Solana transaction events, allowing you to focus on your application's core logic instead of data parsing. This guide will show you how to build a script that tracks all new token mints on Solana, as well as token migrations from Pump.fun, providing a foundation for more complex applications.
What You Will Do
- Activate the Structured Real-Time Solana Data add-on and get your API key with your QuickNode account
- Set up a Node.js project to connect to a WebSocket API
- Subscribe to and filter for specific onchain events, like new token mints
- Process real-time transaction data from the LYS Labs API
What You Will Need
- A QuickNode account
- A Solana QuickNode endpoint with the LYS Labs' Structured Real-Time Solana Data add-on enabled
- Basic understanding of Solana
- Node.js installed
- A code editor like Visual Studio Code
- A Unix-like terminal environment such as macOS, Linux, or Windows with WSL
Structured Real-Time Solana Data Add-On
Available through the QuickNode Marketplace, this add-on provides a real-time data solution for the Solana blockchain. It captures events as they occur and delivers them instantly via a WebSocket connection.
The main features are:
- Real-Time Event Streaming: Get sub-second latency on onchain events.
- Actionable Trading Data: Get instant access to decoded events from DEXs like Raydium, Meteora, and Pump.fun. The data includes token swap summaries, precision volume insights, and real-time liquidity shifts.
- Simple WebSocket API: Easily subscribe to a stream of events with just a few lines of code.
In this guide, you’ll learn how to use these features by building a Node.js script to filter and log new SPL token mints as they occur.
Let's dive into the development process.
Low Latency Solana Event Tracker
Prerequisites
First things first, you'll need an API key to communicate with the LYS Labs. You can get this directly from the QuickNode Marketplace.
- Create a QuickNode account if you haven't already. You can sign up here.
- Set up a Solana Mainnet endpoint on QuickNode.
- Add the Structured Real-Time Solana Data add-on to your endpoint.
- Once added, click the "Sign in to Dashboard" button on the add-on card to access your LYS Labs dashboard and get your API key.
Keep your API key handy, as you'll need it to connect to the WebSocket API.
Setting Up the Project
Now that you have your API key, you can start building your monitoring script.
Open your terminal and run the following commands to create a project folder and initialize a Node.js project:
mkdir lys-solana-tracker && cd lys-solana-tracker
npm init -y
Next, open the newly created package.json
file and add "type": "module"
to enable ES Module syntax. This makes handling imports and exports cleaner. We'll also add a dev
script to easily run our TypeScript file.
{
// ...existing package.json content
"type": "module",
"scripts": {
"dev": "tsx index.ts"
}
}
Now, install the necessary dependencies. We'll use ws
for WebSocket connections, dotenv
to manage our API key securely, and tsx
to run TypeScript code directly.
npm install ws dotenv
npm install --save-dev typescript tsx @types/node @types/ws
Finally, create two new files in your project directory: .env
to store your API key and index.ts
for your application code.
touch .env index.ts
In the .env
file, add your API key like so:
LYS_API_KEY=your_api_key_here
Always add your .env
file to a .gitignore
file to prevent accidentally committing your secret API key to a public repository.
Connecting to the WebSocket API
With the project set up, you can now write the code to connect to the LYS WebSocket endpoint.
Open your index.ts
file and add the following code. This snippet imports the required libraries, loads your API key from the .env
file, and establishes a connection to the LYS WebSocket server.
import WebSocket from 'ws'
import 'dotenv/config'
// 1. Load the API key from the .env file
const API_KEY = process.env.LYS_API_KEY || null
if (!API_KEY) {
throw new Error('LYS_API_KEY is not defined in the .env file')
}
// 2. Define the WebSocket URL and create a new WebSocket instance
const url = `wss://solana-mainnet-api-vip.lyslabs.ai/v1/?apiKey=${API_KEY}`
const ws = new WebSocket(url)
// 3. Once the connection is open, log a success message and subscribe to events
ws.on('open', () => {
console.log('✅ Successfully connected to LYS Labs!')
// The 'subscribe' action tells the API to start sending all transaction events
ws.send(JSON.stringify({ action: 'subscribe' }))
})
// We'll add the message handler in the next step
ws.on('message', data => {
// Code to process incoming messages will go here
})
ws.on('error', error => {
console.error('WebSocket Error:', error)
})
ws.on('close', () => {
console.log('WebSocket connection closed.')
})
Listening for New Token Mints
Now that you have a WebSocket connection established, you can start listening to different events. In this example, we'll add a filter for new token mints and log them to the console.
The LYS API sends data either as a single transaction
object or a batch of transactions
. To simplify processing, we'll normalize the data into an array and then loop through it.
Update the ws.on("message", ...)
handler in your index.ts
file with the following code. This code checks for SPL_TOKEN
program events of type MINT
and logs relevant details like the mint address, amount, and destination account.
ws.on('message', data => {
const msg = JSON.parse(data.toString())
// Check if the message contains transaction data
if (msg.type === 'transaction' || msg.type === 'transactions') {
// Normalize the data into an array, whether it's a single object or an array
const transactions = Array.isArray(msg.data) ? msg.data : [msg.data]
for (const tx of transactions) {
// Filter for SPL_TOKEN program events of type MINT
if (tx.decoderType === 'SPL_TOKEN' && tx.eventType === 'MINT') {
// Extract the amount, handling potential variations in the data structure
const amount = tx.uiAmount?.value ?? tx.amount ?? 'N/A'
console.log('🎉 New Token Mint Detected!')
console.log(` Signature: ${tx.txSignature}`)
console.log(` Mint Address: ${tx.mint}`)
console.log(` Amount: ${amount}`)
console.log(` Destination: ${tx.destinationAccount}`)
console.log('------------------------------------')
}
}
}
})
The structure of the event data can vary depending on the eventType
. LYS Labs provides detailed examples for different events in their official documentation, which is a great resource to consult when tracking other event types.
Running the Script
With the code in place, you can now run the script and start tracking new token mints in real time.
Run the following command in your terminal to start the script:
npm run dev
You should see the "Successfully connected" message, followed by a stream of new token mints as they happen on the network, like this:
✅ Successfully connected to LYS Labs!
🎉 New Token Mint Detected!
Signature: 4h4cVhXPANAtYrw33sACEFmE35dSHGXV8baisG2qvgqdFF3QMoYa2sP8PuANy7fkCjZYVZsXLr6T8JBxzEpG6X5K
Mint Address: FZN7QZ8ZUUAxMPfxYEYkH3cXUASzH8EqA6B4tyCL8f1j
Amount: 857353
Destination: 3tt29ZvzMrzEW841n8H2V3AaKpevGf947Ryr4kzpXVH6
------------------------------------
🎉 New Token Mint Detected!
Signature: 4h4cVhXPANAtYrw33sACEFmE35dSHGXV8baisG2qvgqdFF3QMoYa2sP8PuANy7fkCjZYVZsXLr6T8JBxzEpG6X5K
Mint Address: 6cGACr5TqEoFm6UiromxCeHfA5UstEMnn1eLkMskAjuh
Amount: 964960861
Destination: HsVCHzkVzZfYbqM4gFWkAA153oVLLmgmLaJ4mkuRCqi2
------------------------------------
🎉 New Token Mint Detected!
Signature: 3DAVXZJJMwY9b7QdxwdAEzTSaHAk46G9rZ33XiHQJ91q6qkrqhj4MYLt9iSEehbQvGJuTpxHeGb5hfBSbiADQMJj
Mint Address: 3RpEekjLE5cdcG15YcXJUpxSepemvq2FpmMcgo342BwC
Amount: 7411092
Destination: CHtAbgouK9Sqf7hVBnhr7F5tkhJkHhCtkqbUHszymJL9
------------------------------------
(Optional) Tracking Pump.fun Token Migrations
Now that you have a basic understanding of how to use the add-on, let's take it a step further and track token migrations on the Pump.fun protocol. A "token migration" is when a token gets enough traction to "graduate" by moving its liquidity from Pump.fun's initial pricing model to a major DEX like Raydium. This is a critical event for traders because it makes the token available to a much wider market.
To do this, you only need to change the message handling logic. The setup and WebSocket connection remain the same.
Replace the ws.on("message", ...)
handler with the code below:
// Define a type for the decoded transaction data relevant to this example
type PumpFunTransaction = {
decoderType: string
eventType: string
txSignature: string
mint?: string // This property is part of the MIGRATE event
pool?: string // This property is part of the CREATE_POOL event
lpMint?: string // This property is part of the CREATE_POOL event
}
// A map to temporarily store migration events until the corresponding pool is created
const pendingMigrations: Map<string, PumpFunTransaction> = new Map()
ws.on('message', data => {
const msg = JSON.parse(data.toString())
if (msg.type === 'transaction' || msg.type === 'transactions') {
const transactions = (
Array.isArray(msg.data) ? msg.data : [msg.data]
) as PumpFunTransaction[]
for (const tx of transactions) {
// Watch for the MIGRATE event from Pump.fun
if (tx.decoderType === 'PUMP_FUN' && tx.eventType === 'MIGRATE') {
pendingMigrations.set(tx.txSignature, tx)
}
// Watch for the CREATE_POOL event from the Pump.fun AMM
if (tx.decoderType === 'PUMP_FUN_AMM' && tx.eventType === 'CREATE_POOL') {
// Check if we saw the matching migration event for this transaction
const migrateTx = pendingMigrations.get(tx.txSignature)
if (migrateTx) {
console.log('🚀 Token Graduated from Pump.fun!')
console.log(` Tx Signature: ${tx.txSignature}`)
console.log(` Token Mint: ${migrateTx.mint}`)
console.log(` AMM Pool Address: ${tx.pool}`)
console.log(` LP Mint: ${tx.lpMint}`)
console.log('------------------------------------')
// Clean up the map to prevent memory leaks
pendingMigrations.delete(tx.txSignature)
}
}
}
}
})
This logic uses the transaction signature to link the initial MIGRATE
event with the subsequent CREATE_POOL
event, giving you a complete picture of the token's graduation.
Full Code
Here's the complete code for both tracking new token mints and Pump.fun token migrations.
View Code for Tracking New Token Mints
import WebSocket from 'ws'
import 'dotenv/config'
// 1. Load the API key from the .env file
const API_KEY = process.env.LYS_API_KEY || null
if (!API_KEY) {
throw new Error('LYS_API_KEY is not defined in the .env file')
}
// 2. Define the WebSocket URL and create a new WebSocket instance
const url = `wss://solana-mainnet-api-vip.lyslabs.ai/v1/?apiKey=${API_KEY}`
const ws = new WebSocket(url)
// 3. Once the connection is open, log a success message and subscribe to events
ws.on('open', () => {
console.log('✅ Successfully connected to LYS Labs!')
// The 'subscribe' action tells the API to start sending all transaction events
ws.send(JSON.stringify({ action: 'subscribe' }))
})
ws.on('message', data => {
const msg = JSON.parse(data.toString())
// Check if the message contains transaction data
if (msg.type === 'transaction' || msg.type === 'transactions') {
// Normalize the data into an array, whether it's a single object or an array
const transactions = Array.isArray(msg.data) ? msg.data : [msg.data]
for (const tx of transactions) {
// Filter for SPL_TOKEN program events of type MINT
if (tx.decoderType === 'SPL_TOKEN' && tx.eventType === 'MINT') {
// Extract the amount, handling potential variations in the data structure
const amount = tx.uiAmount?.value ?? tx.amount ?? 'N/A'
console.log('🎉 New Token Mint Detected!')
console.log(` Signature: ${tx.txSignature}`)
console.log(` Mint Address: ${tx.mint}`)
console.log(` Amount: ${amount}`)
console.log(` Destination: ${tx.destinationAccount}`)
console.log('------------------------------------')
}
}
}
})
ws.on('error', error => {
console.error('WebSocket Error:', error)
})
ws.on('close', () => {
console.log('WebSocket connection closed.')
})
View Code for Tracking Pump.fun Token Migrations
import WebSocket from 'ws'
import 'dotenv/config'
// 1. Load the API key from the .env file
const API_KEY = process.env.LYS_API_KEY || null
if (!API_KEY) {
throw new Error('LYS_API_KEY is not defined in the .env file')
}
// 2. Define the WebSocket URL and create a new WebSocket instance
const url = `wss://solana-mainnet-api-vip.lyslabs.ai/v1/?apiKey=${API_KEY}`
const ws = new WebSocket(url)
// 3. Once the connection is open, log a success message and subscribe to events
ws.on('open', () => {
console.log('✅ Successfully connected to LYS Labs!')
// The 'subscribe' action tells the API to start sending all transaction events
ws.send(JSON.stringify({ action: 'subscribe' }))
})
// Define a type for the decoded transaction data relevant to this example
type PumpFunTransaction = {
decoderType: string
eventType: string
txSignature: string
mint?: string // This property is part of the MIGRATE event
pool?: string // This property is part of the CREATE_POOL event
lpMint?: string // This property is part of the CREATE_POOL event
}
// A map to temporarily store migration events until the corresponding pool is created
const pendingMigrations: Map<string, PumpFunTransaction> = new Map()
ws.on('message', data => {
const msg = JSON.parse(data.toString())
if (msg.type === 'transaction' || msg.type === 'transactions') {
const transactions = (
Array.isArray(msg.data) ? msg.data : [msg.data]
) as PumpFunTransaction[]
for (const tx of transactions) {
// Watch for the MIGRATE event from Pump.fun
if (tx.decoderType === 'PUMP_FUN' && tx.eventType === 'MIGRATE') {
pendingMigrations.set(tx.txSignature, tx)
}
// Watch for the CREATE_POOL event from the Pump.fun AMM
if (tx.decoderType === 'PUMP_FUN_AMM' && tx.eventType === 'CREATE_POOL') {
// Check if we saw the matching migration event for this transaction
const migrateTx = pendingMigrations.get(tx.txSignature)
if (migrateTx) {
console.log('🚀 Token Graduated from Pump.fun!')
console.log(` Tx Signature: ${tx.txSignature}`)
console.log(` Token Mint: ${migrateTx.mint}`)
console.log(` AMM Pool Address: ${tx.pool}`)
console.log(` LP Mint: ${tx.lpMint}`)
console.log('------------------------------------')
// Clean up the map to prevent memory leaks
pendingMigrations.delete(tx.txSignature)
}
}
}
}
})
ws.on('error', error => {
console.error('WebSocket Error:', error)
})
ws.on('close', () => {
console.log('WebSocket connection closed.')
})
Conclusion
Congratulations! You’ve successfully built a real-time Solana event tracker using a QuickNode Solana endpoint, powered by the Structured Real-Time Data add-on from LYS Labs. You now have the foundational knowledge to listen for any onchain event, opening up endless possibilities for building responsive and data-driven Web3 applications.
Acknowledgements
A special thank you to the LYS Labs team for their close collaboration in creating this guide.
Next Steps
- Explore other event types in the LYS Labs documentation
- Integrate this logic into a trading bot or a real-time analytics dashboard
- Check out other Solana guides on QuickNode to further enhance your skills
If you are stuck or have questions, drop them in our Discord. Stay up to date with the latest by following us on X (formerly Twitter) (@QuickNode) or our Telegram announcement channel.
We ❤️ Feedback!
Let us know if you have any feedback or requests for new topics. We'd love to hear from you.