Skip to content

Instantly share code, notes, and snippets.

@gayleQN
Created June 16, 2025 16:16
Show Gist options
  • Save gayleQN/a82ff9d6027c83206534778b35dca493 to your computer and use it in GitHub Desktop.
Save gayleQN/a82ff9d6027c83206534778b35dca493 to your computer and use it in GitHub Desktop.
Solana Wallet Monitoring with Key-Value Store
const tokenPrograms = new Set([
'TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA',
'TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb',
]);
const ignorablePrograms = new Set([
'Vote111111111111111111111111111111111111111',
'ComputeBudget111111111111111111111111111111',
'AddressLookupTab1e1111111111111111111111111',
]);
const LIST_NAME = 'solanawallets';
function shouldSkipTransaction(tx) {
const instructions = tx.transaction?.message?.instructions || [];
return instructions.length > 0 && instructions.every(inst => ignorablePrograms.has(inst.programId));
}
async function main(stream) {
try {
const blocks = stream.data;
const results = [];
for (const block of blocks) {
if (!block?.transactions) continue;
const matchedTransactions = [];
const addressCache = new Map();
async function isMatch(addr) {
if (addressCache.has(addr)) return addressCache.get(addr);
const match = await qnLib.qnContainsListItem(LIST_NAME, addr);
addressCache.set(addr, match);
return match;
}
for (const tx of block.transactions) {
if (shouldSkipTransaction(tx)) continue;
const matches = new Set();
const keys = tx.transaction?.message?.accountKeys || [];
const pre = tx.meta?.preBalances || [];
const post = tx.meta?.postBalances || [];
const checkPromises = [];
// Native SOL balance change detection
for (let i = 0; i < Math.min(pre.length, post.length); i++) {
if (BigInt(pre[i]) !== BigInt(post[i])) {
const addr = keys[i]?.pubkey || keys[i];
if (addr && typeof addr === 'string') {
checkPromises.push(
isMatch(addr).then(match => {
if (match) matches.add(addr);
})
);
}
}
}
// Token transfer detection
const instructions = [
...(tx.transaction?.message?.instructions || []),
...(tx.meta?.innerInstructions || []).flatMap(i => i.instructions || []),
];
for (const inst of instructions) {
if (!tokenPrograms.has(inst.programId)) continue;
const info = inst.parsed?.info || {};
for (const addr of [info.source, info.destination, info.authority, info.multisigAuthority]) {
if (addr && typeof addr === 'string') {
checkPromises.push(
isMatch(addr).then(match => {
if (match) matches.add(addr);
})
);
}
}
}
await Promise.all(checkPromises);
if (matches.size > 0) {
matchedTransactions.push({ wallets: Array.from(matches), raw: tx });
}
}
if (matchedTransactions.length > 0) {
results.push({
block: { ...block, transactions: undefined },
transactions: matchedTransactions
});
}
}
return results.length > 0 ? results : null;
} catch (error) {
return { error: error.message, stack: error.stack };
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment