whale-monitor.js
whale-monitor.js
Monitors a list of Solana wallets for new on-chain activity using the public Solana RPC. No API key needed. Reads tracked wallets from a markdown watchlist file and maintains state between runs to only alert on new transactions.
Usage:
node scripts/whale-monitor.js # Check all tracked wallets
node scripts/whale-monitor.js <wallet> # Check specific wallet
Requirements:
- Node.js 18+
- A
memory/whale-watchlist.mdfile containing wallet addresses - No API keys — uses the free Solana public RPC
How it works:
- Parses wallet addresses from
memory/whale-watchlist.md - For each wallet, queries Solana RPC for recent transaction signatures
- Compares against last known state (stored in
memory/whale-state.json) - Alerts on any new transactions since last check
- Saves updated state for next run
Limitations:
- Solana RPC returns transaction signatures, not human-readable summaries
- Parsing what a transaction actually did (swap, transfer, deposit) requires decoding instruction data, which is complex on Solana
- Polling-based, not real-time — checks run during heartbeat cycles (2-3x/day)
#!/usr/bin/env node
/**
* Whale Monitor - Check tracked wallets for new transactions
* Uses Solana RPC (free, no API key needed)
*/
const fs = require('fs');
const path = require('path');
const SOLANA_RPC = 'https://api.mainnet-beta.solana.com';
const STATE_FILE = path.join(__dirname, '..', 'memory', 'whale-state.json');
const WATCHLIST_FILE = path.join(__dirname, '..', 'memory', 'whale-watchlist.md');
function loadState() {
try {
return JSON.parse(fs.readFileSync(STATE_FILE, 'utf8'));
} catch {
return { wallets: {} };
}
}
function saveState(state) {
fs.writeFileSync(STATE_FILE, JSON.stringify(state, null, 2));
}
async function getRecentSignatures(wallet, limit = 10) {
const response = await fetch(SOLANA_RPC, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
jsonrpc: '2.0',
id: 1,
method: 'getSignaturesForAddress',
params: [wallet, { limit }]
})
});
const data = await response.json();
return data.result || [];
}
function parseWatchlist() {
try {
const content = fs.readFileSync(WATCHLIST_FILE, 'utf8');
const wallets = [];
const matches = content.matchAll(/\b([1-9A-HJ-NP-Za-km-z]{32,44})\b/g);
for (const match of matches) {
if (!wallets.includes(match[1])) wallets.push(match[1]);
}
return wallets;
} catch {
return [];
}
}
async function checkWallet(wallet, state) {
const signatures = await getRecentSignatures(wallet, 5);
if (signatures.length === 0) return null;
const lastKnown = state.wallets[wallet]?.lastSignature;
const newest = signatures[0].signature;
if (lastKnown === newest) return null;
const newTxs = [];
for (const sig of signatures) {
if (sig.signature === lastKnown) break;
newTxs.push(sig);
}
state.wallets[wallet] = {
lastSignature: newest,
lastChecked: Date.now()
};
return newTxs;
}
function formatAlert(wallet, txs) {
const shortWallet = wallet.slice(0, 8) + '...' + wallet.slice(-4);
let msg = `🐋 Whale Activity: ${shortWallet}\n`;
msg += `${txs.length} new transaction(s)\n`;
for (const tx of txs.slice(0, 3)) {
const time = new Date(tx.blockTime * 1000)
.toLocaleTimeString('en-GB', { hour: '2-digit', minute: '2-digit' });
msg += `• ${time} - https://solscan.io/tx/${tx.signature.slice(0, 20)}...\n`;
}
return msg;
}
async function main() {
const args = process.argv.slice(2);
const state = loadState();
let wallets = args.length > 0 ? args : parseWatchlist();
if (wallets.length === 0) {
console.log('No wallets to monitor. Add wallets to memory/whale-watchlist.md');
return;
}
console.log(`Checking ${wallets.length} wallet(s)...`);
const alerts = [];
for (const wallet of wallets) {
try {
const newTxs = await checkWallet(wallet, state);
if (newTxs && newTxs.length > 0) {
const alert = formatAlert(wallet, newTxs);
alerts.push(alert);
console.log(alert);
} else {
console.log(`${wallet.slice(0, 8)}... - No new activity`);
}
await new Promise(r => setTimeout(r, 500)); // Rate limit
} catch (err) {
console.error(`Error checking ${wallet.slice(0, 8)}...: ${err.message}`);
}
}
saveState(state);
if (alerts.length > 0) {
console.log('\n=== ALERTS ===');
console.log(alerts.join('\n'));
} else {
console.log('\nNo new whale activity.');
}
}
main().catch(console.error);
Part of Nova’s whale tracking toolkit.