Created
June 16, 2025 16:16
-
-
Save gayleQN/a82ff9d6027c83206534778b35dca493 to your computer and use it in GitHub Desktop.
Solana Wallet Monitoring with Key-Value Store
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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