Whale wallets move markets on Solana. When a wallet with a known track record of finding 10x tokens makes a large buy, that information is worth having — immediately, not 10 minutes later when you are checking a dashboard. A whale alert bot sends you that notification the moment the swap is indexed, to whatever channel you actually watch.
This guide builds a whale alert bot from scratch using the Solana API Wallet Tracker webhooks. You will end up with a service that monitors a list of whale wallets, fires a real-time webhook on every swap, applies a SOL size filter, and posts formatted alerts to Discord and Telegram. The concepts here complement the broader wallet tracking techniques covered in our guide on how to track whale wallets on Solana.
Architecture Overview
The bot has three components:
- Watchlist — whale wallets registered with the Wallet Tracker API
- Webhook receiver — an HTTP server that receives
wallet_tracker:event payloads in real time
- Alert dispatcher — formats the event and sends it to Discord and/or Telegram
The webhook approach is strongly preferred over polling for alert use cases. Polling on a 30-second cycle means your notifications can be up to 30 seconds late — which on a fast-moving Solana memecoin can mean the difference between getting in early and chasing. Webhooks push events to your server within seconds of indexing. Webhooks are available on PRO and ULTRA plans; see the API docs for setup.
Get your free API key to start — you can test with polling on BASIC before upgrading to webhook-based delivery.
What Makes a Wallet "Whale-Worthy"
Not every large wallet is worth tracking. The signal-to-noise ratio matters. Good whale candidates have:
- Consistent entry timing — they tend to buy before a token gets widely noticed, not after it has already 3x'd
- Meaningful position sizes — typically 10 SOL+ per trade, not micro-testing
- A verifiable track record — you can confirm their historical trades on-chain, not just take someone's word for it
- Low sell frequency on winners — whales that immediately dump after buying are providing noise, not signal
The KOL tracker on MadeOnSol is a good starting point for finding high-quality wallets with verified on-chain performance. The Deployer Hunter can surface wallets that are consistently early on successful token launches. To source whale candidates programmatically by performance, our Solana wallet win rate leaderboard API guide shows how to pull ranked wallets straight into your watchlist.
Once you have your list, track them programmatically using the Wallet Tracker API.
Step 1: Setting Up the Watchlist
Install dependencies and initialise the SDK:
npm install madeonsol express dotenv
npm install -D typescript @types/node @types/express ts-node
// src/setup-watchlist.ts
import { MadeOnSol } from 'madeonsol';
const client = new MadeOnSol({ apiKey: process.env.MADEONSOL_API_KEY! });
const WHALE_WALLETS = [
{ address: 'WHALE_ADDRESS_1', label: 'Whale: Large Cap Hunter' },
{ address: 'WHALE_ADDRESS_2', label: 'Whale: Memecoin Early' },
{ address: 'WHALE_ADDRESS_3', label: 'Whale: KOL Follow' },
];
async function setupWatchlist() {
for (const wallet of WHALE_WALLETS) {
try {
await client.walletTracker.addToWatchlist(wallet);
console.log(`✓ Added ${wallet.label}`);
} catch (err: any) {
if (err.status === 409) {
console.log(`– Already tracking ${wallet.label}`);
} else {
throw err;
}
}
}
}
setupWatchlist();
Run this once to register your wallets. After that your webhook will start receiving events automatically.
Step 2: Building the Webhook Receiver
The webhook receiver is a lightweight Express server that accepts POST requests from the Wallet Tracker API and processes each event. You will need a publicly accessible URL — use a server you control, or ngrok for local development.
// src/webhook-server.ts
import express, { Request, Response } from 'express';
import crypto from 'crypto';
import { sendDiscordAlert, sendTelegramAlert } from './alerts';
const app = express();
app.use(express.json());
// Minimum SOL size to trigger an alert
const MIN_ALERT_SOL = parseFloat(process.env.MIN_ALERT_SOL ?? '5');
// Your webhook secret from the API dashboard
const WEBHOOK_SECRET = process.env.WEBHOOK_SECRET!;
function verifySignature(rawBody: string, signature: string): boolean {
const expected = crypto
.createHmac('sha256', WEBHOOK_SECRET)
.update(rawBody)
.digest('hex');
return crypto.timingSafeEqual(
Buffer.from(expected),
Buffer.from(signature.replace('sha256=', ''))
);
}
app.post('/webhook/wallet-tracker', express.raw({ type: 'application/json' }), async (req: Request, res: Response) => {
const signature = req.headers['x-madeonsol-signature'] as string;
if (!verifySignature(req.body.toString(), signature)) {
console.warn('Invalid webhook signature — ignoring');
return res.status(401).send('Unauthorized');
}
const payload = JSON.parse(req.body.toString());
// Acknowledge immediately — process async to avoid timeouts
res.status(200).send('OK');
try {
await processEvent(payload);
} catch (err) {
console.error('Error processing webhook event:', err);
}
});
async function processEvent(payload: any) {
const { event, data } = payload;
// We only care about wallet_tracker:event
if (event !== 'wallet_tracker:event') return;
// Only alert on swaps (not raw transfers)
if (data.event_type !== 'swap') return;
// Only alert on buys above the threshold
if (data.action !== 'buy') return;
if (data.sol_amount < MIN_ALERT_SOL) return;
await dispatchAlerts(data);
}
async function dispatchAlerts(trade: any) {
const label = trade.wallet_label ?? trade.wallet.slice(0, 8) + '...';
const solAmount = trade.sol_amount.toFixed(2);
const tokenSymbol = trade.token_symbol ?? 'UNKNOWN';
const mint = trade.token_mint;
const sig = trade.signature;
const message = formatAlertMessage(label, solAmount, tokenSymbol, mint, sig);
await Promise.allSettled([
sendDiscordAlert(message),
sendTelegramAlert(message),
]);
}
function formatAlertMessage(
label: string,
solAmount: string,
symbol: string,
mint: string,
sig: string
): string {
return [
`🐋 **Whale Alert**`,
`**Wallet:** ${label}`,
`**Action:** BUY ${symbol}`,
`**Size:** ${solAmount} SOL`,
`**Token:** \`${mint.slice(0, 8)}...\``,
`[View on Solscan](https://solscan.io/tx/${sig})`,
].join('\n');
}
app.listen(3010, () => console.log('Webhook receiver listening on :3010'));
Step 3: Discord and Telegram Alert Dispatchers
Discord
Use an incoming webhook URL from your Discord server settings (Server Settings → Integrations → Webhooks):
// src/alerts/discord.ts
import https from 'https';
const DISCORD_WEBHOOK_URL = process.env.DISCORD_WEBHOOK_URL!;
export async function sendDiscordAlert(message: string): Promise<void> {
const body = JSON.stringify({ content: message });
return new Promise((resolve, reject) => {
const url = new URL(DISCORD_WEBHOOK_URL);
const options = {
hostname: url.hostname,
path: url.pathname + url.search,
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Content-Length': Buffer.byteLength(body),
},
};
const req = https.request(options, (res) => {
if (res.statusCode && res.statusCode >= 200 && res.statusCode < 300) {
resolve();
} else {
reject(new Error(`Discord returned ${res.statusCode}`));
}
});
req.on('error', reject);
req.write(body);
req.end();
});
}
Telegram
Use the Telegram Bot API. Create a bot via @BotFather and get your bot token and chat ID:
// src/alerts/telegram.ts
const TELEGRAM_BOT_TOKEN = process.env.TELEGRAM_BOT_TOKEN!;
const TELEGRAM_CHAT_ID = process.env.TELEGRAM_CHAT_ID!;
export async function sendTelegramAlert(message: string): Promise<void> {
// Convert markdown to Telegram-compatible format
const text = message.replace(/\*\*/g, '*');
const url = `https://api.telegram.org/bot${TELEGRAM_BOT_TOKEN}/sendMessage`;
const body = JSON.stringify({
chat_id: TELEGRAM_CHAT_ID,
text,
parse_mode: 'Markdown',
disable_web_page_preview: false,
});
const response = await fetch(url, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body,
});
if (!response.ok) {
throw new Error(`Telegram returned ${response.status}`);
}
}
Step 4: Tuning Your Alert Filters
The raw webhook stream includes every swap and transfer from every wallet in your watchlist. Without filters you will get noise — small test trades, stablecoin swaps, and routine portfolio rebalancing. Good filters make the difference between a useful alert bot and one you mute within a week.
Size Filtering
The MIN_ALERT_SOL threshold is the most important filter. Start conservative — if a whale typically trades 20–50 SOL, set your threshold at 10 SOL. You will miss some smaller conviction trades, but you will not get woken up at 2am for a 0.5 SOL test buy.
You can also filter on the upper end. Trades above 200 SOL from a single wallet in a small-cap token are worth a separate, higher-urgency alert channel.
Token Filtering
Some tokens are not worth alerting on regardless of size: