Documentation Index Fetch the complete documentation index at: https://www.helius.dev/docs/llms.txt
Use this file to discover all available pages before exploring further.
Overview
Account monitoring lets you track balance changes, data modifications, ownership transfers, and account creation/deletion events across Solana in real-time. This guide covers filtering strategies and implementation patterns for different use cases.
Account Filtering Options
Specific Accounts
By Owner
Advanced Filters
Monitor individual accounts by public key Use this when you know exactly which accounts to watch: const subscribeRequest : SubscribeRequest = {
accounts: {
accountSubscribe: {
account: [
"EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v" , // USDC mint
"So11111111111111111111111111111111111111112" // Wrapped SOL
],
owner: [],
filters: []
}
},
commitment: CommitmentLevel . CONFIRMED
};
Best for: Monitoring specific token mints, known wallets, or critical program accounts
Monitor all accounts owned by specific programs Track all accounts owned by a program (like all token accounts for a specific mint): const subscribeRequest : SubscribeRequest = {
accounts: {
accountSubscribe: {
account: [],
owner: [
"TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA" , // Token Program
"11111111111111111111111111111111" // System Program
],
filters: []
}
},
commitment: CommitmentLevel . CONFIRMED
};
High volume: Owner filters can generate significant data. Use additional filters to narrow results.
Combine data size and memory comparison filters Filter accounts by data structure and content: const subscribeRequest : SubscribeRequest = {
accounts: {
accountSubscribe: {
account: [],
owner: [ "TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA" ],
filters: [
// Only token accounts (165 bytes)
{ dataSize: 165 },
// Only USDC token accounts (mint at offset 0)
{
memcmp: {
offset: 0 ,
bytes: "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v"
}
}
]
}
},
commitment: CommitmentLevel . CONFIRMED
};
Efficient filtering: Combine size and content filters to minimize unnecessary data
Data Slicing
Optimize bandwidth by requesting only specific byte ranges from account data:
// Only get the balance portion of token accounts (bytes 64-72)
const subscribeRequest : SubscribeRequest = {
accounts: {
accountSubscribe: {
owner: [ "TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA" ],
filters: [{ dataSize: 165 }]
}
},
accountsDataSlice: [
{ offset: 64 , length: 8 } // Token balance (u64)
],
commitment: CommitmentLevel . CONFIRMED
};
Practical Examples
Example 1: Monitor Large Token Holders
Track USDC accounts with significant balances:
import { StreamManager } from './stream-manager' ; // From quickstart guide
async function monitorLargeUSDCHolders () {
const streamManager = new StreamManager (
"your-grpc-endpoint" ,
"YOUR_API_KEY" ,
handleLargeHolderUpdate
);
const subscribeRequest : SubscribeRequest = {
accounts: {
accountSubscribe: {
owner: [ "TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA" ],
filters: [
{ dataSize: 165 }, // Token account size
{
memcmp: {
offset: 0 ,
bytes: "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v" // USDC mint
}
}
]
}
},
accountsDataSlice: [
{ offset: 32 , length: 32 }, // Owner
{ offset: 64 , length: 8 } // Balance
],
commitment: CommitmentLevel . CONFIRMED
};
await streamManager . connect ( subscribeRequest );
}
function handleLargeHolderUpdate ( data : any ) : void {
if ( data . account ) {
const account = data . account . account ;
// Parse token account data
if ( account . data && account . data . length >= 8 ) {
const balanceBuffer = Buffer . from ( account . data . slice ( 64 , 72 ), 'base64' );
const balance = balanceBuffer . readBigUInt64LE ();
const balanceInUSDC = Number ( balance ) / 1e6 ; // USDC has 6 decimals
// Only log accounts with > 100,000 USDC
if ( balanceInUSDC > 100000 ) {
console . log ( `🐋 Large USDC Holder Update:` );
console . log ( ` Account: ${ account . pubkey } ` );
console . log ( ` Balance: ${ balanceInUSDC . toLocaleString () } USDC` );
console . log ( ` Slot: ${ data . account . slot } ` );
}
}
}
}
Example 2: Track Program Account Changes
Monitor all accounts owned by a specific program:
async function monitorProgramAccounts () {
const PROGRAM_ID = "YourProgramId" ; // Replace with actual program ID
const streamManager = new StreamManager (
"your-grpc-endpoint" ,
"YOUR_API_KEY" ,
handleProgramAccountUpdate
);
const subscribeRequest : SubscribeRequest = {
accounts: {
accountSubscribe: {
owner: [ PROGRAM_ID ],
filters: []
}
},
commitment: CommitmentLevel . CONFIRMED
};
await streamManager . connect ( subscribeRequest );
}
function handleProgramAccountUpdate ( data : any ) : void {
if ( data . account ) {
const account = data . account . account ;
console . log ( `📋 Program Account Update:` );
console . log ( ` Account: ${ account . pubkey } ` );
console . log ( ` Owner: ${ account . owner } ` );
console . log ( ` Lamports: ${ account . lamports } ` );
console . log ( ` Data Length: ${ account . data ?. length || 0 } bytes` );
console . log ( ` Executable: ${ account . executable } ` );
console . log ( ` Rent Epoch: ${ account . rentEpoch } ` );
}
}
Example 3: New Account Creation Monitoring
Track when new accounts are created:
async function monitorNewAccounts () {
const streamManager = new StreamManager (
"your-grpc-endpoint" ,
"YOUR_API_KEY" ,
handleNewAccountCreation
);
const subscribeRequest : SubscribeRequest = {
accounts: {
accountSubscribe: {
owner: [ "11111111111111111111111111111111" ], // System Program
filters: []
}
},
commitment: CommitmentLevel . CONFIRMED
};
await streamManager . connect ( subscribeRequest );
}
function handleNewAccountCreation ( data : any ) : void {
if ( data . account && data . account . account . lamports === 0 ) {
// New account creation typically starts with 0 lamports
const account = data . account . account ;
console . log ( `🆕 New Account Created:` );
console . log ( ` Account: ${ account . pubkey } ` );
console . log ( ` Owner: ${ account . owner } ` );
console . log ( ` Slot: ${ data . account . slot } ` );
}
}
Filter Logic Reference
Understanding how filters combine:
Account-level filters (AND logic):
account AND owner AND filters must all match if specified
Within arrays (OR logic):
Any account in account array matches
Any owner in owner array matches
Within filters array (AND logic):
All dataSize and memcmp filters must match
Example: {
account : [ "A" , "B" ], // Match account A OR B
owner : [ "X" , "Y" ], // AND owned by X OR Y
filters : [
{ dataSize: 100 }, // AND data size is 100
{ memcmp: { ... } } // AND memcmp matches
]
}
Token accounts for specific mint: {
owner : [ "TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA" ],
filters : [
{ dataSize: 165 },
{ memcmp: { offset: 0 , bytes: "MINT_ADDRESS" } }
]
}
SPL token accounts with minimum balance: {
owner : [ "TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA" ],
filters : [
{ dataSize: 165 },
{ memcmp: { offset: 64 , bytes: "MINIMUM_BALANCE_BYTES" } }
]
}
Program-derived accounts: {
owner : [ "YOUR_PROGRAM_ID" ],
filters : [
{ dataSize: 200 }, // Your account size
{ memcmp: { offset: 8 , bytes: "DISCRIMINATOR" } }
]
}
Bandwidth Optimization Use data slicing to request only needed bytesApply strict filters to reduce unnecessary updatesChoose appropriate commitment levels for your use case
Scale Management Start with specific accounts before using owner filtersMonitor subscription volume and adjust filters as neededImplement backpressure handling for high-volume streams
Error Handling
Common account monitoring errors and solutions:
Error: Receiving too much data or hitting rate limitsSolution: Add more specific filters:
Use dataSize to match exact account types
Add memcmp filters for specific data patterns
Consider using accountsDataSlice to reduce bandwidth
Error: Stream connects but no account updates appearSolution:
Verify account addresses are correct
Check if accounts actually change frequently
Try PROCESSED commitment for more frequent updates
Test with a known active account first
Next Steps
Transaction Monitoring Learn to correlate account changes with transaction data
Advanced Patterns Real-world example: monitoring DeFi protocols