Overview
Transaction monitoring enables you to track transaction execution, success/failure status, program interactions, and token balance changes across Solana in real-time. This guide covers filtering strategies and practical implementations for different transaction monitoring use cases.
Transaction Filtering Options
Program Filtering Account-Specific Advanced Filtering Monitor transactions involving specific programs
Track all transactions that interact with your programs:
const subscribeRequest : SubscribeRequest = {
transactions: {
client: {
accountInclude: [
"TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA" , // Token Program
"11111111111111111111111111111111" , // System Program
"675kPX9MHTjS2zt1qfr1NYHuzeLXfQM9H24wFSUt1Mp8" // Your program
],
accountExclude: [],
accountRequired: [],
vote: false ,
failed: false
}
},
commitment: CommitmentLevel . CONFIRMED
};
Best for: Program-specific monitoring, DeFi protocol tracking, smart contract interactions
Monitor transactions involving specific programs
Track all transactions that interact with your programs:
const subscribeRequest : SubscribeRequest = {
transactions: {
client: {
accountInclude: [
"TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA" , // Token Program
"11111111111111111111111111111111" , // System Program
"675kPX9MHTjS2zt1qfr1NYHuzeLXfQM9H24wFSUt1Mp8" // Your program
],
accountExclude: [],
accountRequired: [],
vote: false ,
failed: false
}
},
commitment: CommitmentLevel . CONFIRMED
};
Best for: Program-specific monitoring, DeFi protocol tracking, smart contract interactions
Monitor transactions affecting specific accounts
Track transactions that modify specific accounts:
const subscribeRequest : SubscribeRequest = {
transactions: {
client: {
accountInclude: [
"EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v" , // USDC mint
"YourWalletAddress" // Your wallet
],
vote: false ,
failed: true // Include failures to track errors
}
},
commitment: CommitmentLevel . CONFIRMED
};
Use case: Wallet monitoring, token mint tracking, specific account activity
Combine multiple filter criteria
Use required accounts and exclusions for precise filtering:
const subscribeRequest : SubscribeRequest = {
transactions: {
client: {
accountInclude: [ "TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA" ],
accountRequired: [ "YourProgramId" ], // Must include this program
accountExclude: [ "VoteProgram" ], // Exclude vote-related txs
vote: false ,
failed: false
}
},
commitment: CommitmentLevel . CONFIRMED
};
Filter logic: accountInclude (OR) AND accountRequired (AND) AND NOT accountExclude
Practical Examples
Example 1: Monitor DEX Transactions
Track all transactions involving popular DEX programs:
import { StreamManager } from './stream-manager' ; // From quickstart guide
async function monitorDEXTransactions () {
const streamManager = new StreamManager (
"your-grpc-endpoint" ,
"your-api-key" ,
handleDEXTransaction
);
const subscribeRequest : SubscribeRequest = {
transactions: {
client: {
accountInclude: [
"9WzDXwBbmkg8ZTbNMqUxvQRAyrZzDsGYdLVL9zYtAWWM" , // Raydium V4
"675kPX9MHTjS2zt1qfr1NYHuzeLXfQM9H24wFSUt1Mp8" , // Raydium V5
"CAMMCzo5YL8w4VFF8KVHrK22GGUQpMpTFb6xRmpLFGNnSm" , // Raydium CLMM
"JUP6LkbZbjS1jKKwapdHNy74zcZ3tLUZoi5QNyVTaV4" // Jupiter
],
vote: false ,
failed: false
}
},
commitment: CommitmentLevel . CONFIRMED
};
await streamManager . connect ( subscribeRequest );
}
function handleDEXTransaction ( data : any ) : void {
if ( data . transaction ?. transaction ) {
const tx = data . transaction . transaction ;
console . log ( ` \n 🔄 DEX Transaction:` );
console . log ( ` Signature: ${ tx . signature } ` );
console . log ( ` Slot: ${ data . transaction . slot } ` );
console . log ( ` Status: ${ tx . meta ?. err ? 'Failed' : 'Success' } ` );
console . log ( ` Fee: ${ tx . meta ?. fee || 0 } lamports` );
console . log ( ` Compute Units: ${ tx . meta ?. computeUnitsConsumed || 0 } ` );
// Show token balance changes
if ( tx . meta ?. preTokenBalances ?. length > 0 ) {
console . log ( ` Token Balance Changes:` );
tx . meta . preTokenBalances . forEach (( preBalance : any , index : number ) => {
const postBalance = tx . meta . postTokenBalances [ index ];
if ( preBalance && postBalance ) {
const change = postBalance . uiTokenAmount . uiAmount - preBalance . uiTokenAmount . uiAmount ;
if ( change !== 0 ) {
console . log ( ` ${ preBalance . mint } : ${ change > 0 ? '+' : '' }${ change } ` );
}
}
});
}
}
}
Example 2: Monitor Failed Transactions
Track failed transactions to identify issues:
async function monitorFailedTransactions () {
const streamManager = new StreamManager (
"your-grpc-endpoint" ,
"your-api-key" ,
handleFailedTransaction
);
const subscribeRequest : SubscribeRequest = {
transactions: {
client: {
accountInclude: [ "YourProgramId" ], // Your program
vote: false ,
failed: true // Only failed transactions
}
},
commitment: CommitmentLevel . CONFIRMED
};
await streamManager . connect ( subscribeRequest );
}
function handleFailedTransaction ( data : any ) : void {
if ( data . transaction ?. transaction ?. meta ?. err ) {
const tx = data . transaction . transaction ;
console . log ( ` \n ❌ Failed Transaction:` );
console . log ( ` Signature: ${ tx . signature } ` );
console . log ( ` Slot: ${ data . transaction . slot } ` );
console . log ( ` Error: ${ JSON . stringify ( tx . meta . err ) } ` );
console . log ( ` Fee: ${ tx . meta . fee } lamports` );
console . log ( ` Compute Units: ${ tx . meta . computeUnitsConsumed || 0 } ` );
// Log instruction details for debugging
if ( tx . transaction ?. message ?. instructions ) {
console . log ( ` Instructions:` );
tx . transaction . message . instructions . forEach (( inst : any , i : number ) => {
console . log ( ` ${ i } : Program ${ inst . programIdIndex } , Data: ${ inst . data } ` );
});
}
}
}
Example 3: Monitor High-Value Transactions
Track transactions with significant SOL transfers:
async function monitorHighValueTransactions () {
const streamManager = new StreamManager (
"your-grpc-endpoint" ,
"your-api-key" ,
handleHighValueTransaction
);
const subscribeRequest : SubscribeRequest = {
transactions: {
client: {
accountInclude: [ "11111111111111111111111111111111" ], // System Program
vote: false ,
failed: false
}
},
commitment: CommitmentLevel . CONFIRMED
};
await streamManager . connect ( subscribeRequest );
}
function handleHighValueTransaction ( data : any ) : void {
if ( data . transaction ?. transaction ?. meta ) {
const tx = data . transaction . transaction ;
const preBalances = tx . meta . preBalances || [];
const postBalances = tx . meta . postBalances || [];
// Calculate largest balance change
let maxChange = 0 ;
preBalances . forEach (( preBalance : number , index : number ) => {
const postBalance = postBalances [ index ] || 0 ;
const change = Math . abs ( postBalance - preBalance );
maxChange = Math . max ( maxChange , change );
});
// Only log transactions with > 10 SOL moved
const changeInSOL = maxChange / 1e9 ;
if ( changeInSOL > 10 ) {
console . log ( ` \n 💰 High-Value Transaction:` );
console . log ( ` Signature: ${ tx . signature } ` );
console . log ( ` Slot: ${ data . transaction . slot } ` );
console . log ( ` Max SOL Transfer: ${ changeInSOL . toFixed ( 2 ) } SOL` );
console . log ( ` Fee: ${ tx . meta . fee / 1e9 } SOL` );
console . log ( ` Accounts: ${ tx . transaction ?. message ?. accountKeys ?. length || 0 } ` );
}
}
}
Transaction Data Structure
Understanding the transaction data structure helps extract relevant information:
Transaction Message Structure
Core transaction data:
{
signature : string ;
isVote : boolean ;
transaction : {
message : {
accountKeys : string []; // All accounts involved
instructions : Instruction []; // Program instructions
recentBlockhash : string ; // Recent blockhash used
};
signatures : string []; // Transaction signatures
};
meta : {
err : any ; // Error details if failed
fee : number ; // Transaction fee in lamports
computeUnitsConsumed : number ; // Compute units used
preBalances : number []; // Account balances before
postBalances : number []; // Account balances after
preTokenBalances : TokenBalance [];
postTokenBalances : TokenBalance [];
logMessages : string []; // Program log messages
};
}
Token balance structure:
{
accountIndex : number ;
mint : string ; // Token mint address
owner : string ; // Account owner
uiTokenAmount : {
amount : string ; // Raw token amount
decimals : number ; // Token decimals
uiAmount : number ; // Human-readable amount
uiAmountString : string ; // String representation
};
}
Program instruction structure:
{
programIdIndex : number ; // Index in accountKeys array
accounts : number []; // Account indices involved
data : string ; // Instruction data (base58)
}
Filter Logic Reference
Include Logic (OR) accountInclude: Transaction must involve ANY of these accounts
Example: ["A", "B"]
matches transactions involving account A OR account B
Required Logic (AND) accountRequired: Transaction must involve ALL of these accounts
Example: ["A", "B"]
matches transactions involving account A AND account B
Exclude Logic (NOT) accountExclude: Transaction must NOT involve any of these accounts
Example: ["A", "B"]
excludes transactions involving account A or account B
Combined Logic Final filter: (accountInclude OR empty) AND (accountRequired AND all) AND NOT (accountExclude OR any)
Volume Management Data Processing Transaction streams can be high-volume
Start with specific program filters
Use commitment levels appropriately
Monitor your processing capacity
Implement backpressure handling
// Rate limiting example
let transactionCount = 0 ;
const startTime = Date . now ();
function handleTransaction ( data : any ) : void {
transactionCount ++ ;
if ( transactionCount % 100 === 0 ) {
const elapsed = ( Date . now () - startTime ) / 1000 ;
const rate = transactionCount / elapsed ;
console . log ( `Processing ${ rate . toFixed ( 1 ) } tx/sec` );
}
// Your transaction processing logic
}
Transaction streams can be high-volume
Start with specific program filters
Use commitment levels appropriately
Monitor your processing capacity
Implement backpressure handling
// Rate limiting example
let transactionCount = 0 ;
const startTime = Date . now ();
function handleTransaction ( data : any ) : void {
transactionCount ++ ;
if ( transactionCount % 100 === 0 ) {
const elapsed = ( Date . now () - startTime ) / 1000 ;
const rate = transactionCount / elapsed ;
console . log ( `Processing ${ rate . toFixed ( 1 ) } tx/sec` );
}
// Your transaction processing logic
}
Optimize data extraction
Process data asynchronously
Extract only needed fields
Use efficient data structures
Consider batching updates
// Efficient data extraction
function extractTransactionData ( tx : any ) {
return {
signature: tx . signature ,
slot: tx . slot ,
success: ! tx . meta ?. err ,
fee: tx . meta ?. fee || 0 ,
computeUnits: tx . meta ?. computeUnitsConsumed || 0 ,
tokenChanges: extractTokenChanges ( tx . meta )
};
}
Error Handling
Common transaction monitoring errors and solutions:
Error: Overwhelming transaction volume
Solutions:
Add more specific filters (accountRequired, accountExclude)
Use higher commitment levels to reduce volume
Implement sampling or rate limiting
Process transactions asynchronously
Error: Expected transactions not appearing
Solutions:
Verify program addresses are correct
Check if transactions actually occur
Try PROCESSED commitment for faster updates
Ensure filters aren’t too restrictive
Error: Cannot parse transaction data
Solutions:
Handle missing fields gracefully
Validate data structure before processing
Log problematic transactions for debugging
Use try-catch blocks around parsing logic
Next Steps