I am currently using Solana's onLogs method to monitor transactions for multiple wallets in real-time. The implementation involves subscribing to multiple wallet addresses and handling transaction events as they occur. However, I have encountered an issue where some transactions are being missed during monitoring.
Here are some key details about the implementation:
I am using the onLogs method from the Solana Web3.js library and QuickNode($39) Each wallet address is subscribed to individually, with a slight delay between each subscription to avoid overwhelming the connection. I have implemented mechanisms to avoid duplicate processing of transactions and to log all received events. Despite this setup, I am observing inconsistent behavior where some transactions for subscribed wallets do not trigger the onLogs callback. This issue occurs sporadically—sometimes transactions are detected, but other times they are not. I have also ensured that:
The WebSocket connection is stable and retries are handled in case of disconnections. There are no overlapping or duplicate subscriptions for the same wallet. The RPC endpoint being used (QuickNode) is functioning properly. This inconsistency is critical for my application, which requires real-time monitoring without missing any transactions. I am looking for insights into:
Potential reasons why onLogs might miss transactions for certain wallets. Whether there are known limitations or caveats with onLogs when monitoring a large number of wallets. Alternative approaches to ensure all transactions are captured in real-time. Any guidance, suggestions, or shared experiences with similar issues would be greatly appreciated!
async onModuleInit() {
try {
const rpcUrl = this.configService.get<string>('TEST_QUICK_NODE_URL');
const wssrpcUrl = this.configService.get<string>(
'TEST_QUICK_NODE_WSS_URL',
);
this.connection = new Connection(rpcUrl, {
wsEndpoint: wssrpcUrl,
});
const wallets = await this.walletService.listWallets();
this.walletAddresses = wallets.map((wallet) => ({
id: parseInt(wallet.id, 10),
address: new PublicKey(wallet.address),
important: wallet.important,
}));
Logger.log('Successfully loaded wallet addresses from the database.');
await this.monitorWallets();
} catch (error) {
Logger.error('Failed to initialize wallet monitoring:', error);
}
}
private async monitorWallets() {
for (const walletAddress of this.walletAddresses) {
try {
const subscriptionId = this.connection.onLogs(
walletAddress.address,
async (logInfo) => {
await this.handleAccountChange(logInfo.signature, walletAddress);
},
'confirmed',
);
this.subscriptions.set(walletAddress.id, subscriptionId);
Logger.log(
`Successfully subscribed to wallet ID ${walletAddress.id} with Subscription ID ${subscriptionId}`,
);
await new Promise((resolve) => setTimeout(resolve, 100));
} catch (error) {
Logger.error(
`Failed to subscribe to wallet ID ${walletAddress.id}: ${error.message}`,
);
setTimeout(() => {
Logger.warn(
`Retrying subscription for wallet ID ${walletAddress.id}`,
);
this.monitorWallets();
}, 5000);
}
}
}