Most Solana analytics platforms update every few seconds. Some poll RPC endpoints on a timer. A handful parse block data after finality. We chose a different path: dual-region gRPC streams that deliver every on-chain transaction to our services before most explorers even know it happened.
This post explains the real-time data pipeline behind MadeOnSol's KOL tracker, Deployer Hunter, and API platform. We cover the architecture decisions, the tradeoffs, and the failure modes we designed around. If you are building similar infrastructure on Solana, this should save you weeks of debugging.
Architecture at a Glance
Solana Validators
|
v
┌─────────────────────────────────────────────────────────┐
│ Kaldera Yellowstone gRPC (Geyser plugin) │
│ Frankfurt (primary) New York (secondary) │
└──────┬──────────┬──────────┬──────────┬─────────────────┘
| | | |
v v v v
kol-tracker deployer- dex-stream mc-tracker
(1058 KOLs) listener (9+ DEXs) (75K+ tokens)
| (10,600+ | |
| deployers) | |
v v | v
┌──────────────────┐ | ┌───────────────┐
│ PostgreSQL │ | │ In-memory │
│ (kol_trades, │ | │ price maps │
│ deployer_ │ | │ (V8 heap) │
│ tokens, etc.) │ | └───────────────┘
└────────┬─────────┘ |
| |
pg_notify bus | Direct WS broadcast
(madeonsol_events) | (port 3005)
| |
┌──────┴──────┐ |
v v v
webhook- ws-streaming dex-stream
worker (port 3004) WS clients
(HMAC-signed (ULTRA only)
delivery)
Five PM2-managed services. One Postgres event bus. Two gRPC regions. Everything runs on a single Hetzner dedicated server with 64 GB RAM and NVMe RAID-1, fronted by Cloudflare and self-hosted Supabase.
Why Dual-Region gRPC
The first version of our pipeline used a single Yellowstone gRPC connection to Kaldera's Frankfurt endpoint. It worked. Until it didn't.
gRPC streams over the public internet are inherently fragile. A network blip in Frankfurt means a gap in your data. A Geyser plugin restart means you miss transactions during the reconnect window. And because Solana's slot times are 400ms, even a 2-second dropout can mean missing 5 slots of activity.
Our solution: subscribe to the same transaction filters from two geographically separate Kaldera nodes simultaneously. Frankfurt and New York each deliver the full stream. Every trade is processed twice. Deduplication happens at the database level via ON CONFLICT (tx_signature) DO NOTHING.
This gives us three things:
- Zero-gap coverage. If Frankfurt drops, New York is already delivering the same transactions. There is no failover delay because both streams are always active.
- Reorg safety. Different regions sometimes see slightly different transaction ordering during reorgs. Processing both means we catch the surviving version.
- Latency optimization. Whichever region delivers a transaction first wins. Our server sits in Hetzner's Falkenstein datacenter, so Frankfurt typically arrives first, but New York occasionally beats it by a few milliseconds depending on validator geography.
The cost is double the gRPC bandwidth and double the parsing work. On the kol-tracker service, that means roughly 24,000 to 30,000 transactions parsed per minute across both regions before deduplication. The tradeoff is worth it. Missed trades are unacceptable when your users are making trading decisions based on KOL activity.
The KOL Tracker: 1,058 Wallets, Sub-2-Second Alerts
The kol-tracker is the core of our KOL intelligence product. It watches 1,058 curated wallets belonging to known Solana traders, influencers, and alpha callers. When any of these wallets execute a swap on any supported DEX, we detect it, enrich it, and deliver an alert — typically within 2 seconds of on-chain execution.
How it works
The service subscribes to transactions involving all major DEX program IDs via Yellowstone gRPC. This is a firehose — every swap on Raydium, Jupiter, Orca, PumpSwap, Meteora, and more comes through the stream. Client-side, we filter each transaction's account keys against an in-memory Set of KOL wallet addresses. Most transactions don't match and are discarded in microseconds. The ones that do get parsed.
Parsing extracts the trade details: which token was bought or sold, the SOL or USDC amount, the DEX program used, and the transaction signature. Resolving the human-readable name and symbol behind each mint relies on the on-chain metadata standard we cover in our guide to Solana token metadata and Metaplex. One subtle complexity: a single transaction can include multiple token balance changes (especially with bonding curve creates that inflate the curve reserve alongside the wallet's real position). Our parser groups changes by mint and picks the wallet's actual ATA balance change, not the inflated curve figure.
After parsing, the trade is upserted into kol_trades with ON CONFLICT (tx_signature) to handle the dual-region deduplication. Then pg_notify fires to push the event downstream.
Why pg_notify on a separate connection
Early on, we emitted pg_notify inside the same database transaction as the trade UPSERT. This caused a problem. The UPSERT transaction could hold row-level locks for several milliseconds, especially during coordination events where multiple KOLs buy the same token in quick succession. Because pg_notify only fires on COMMIT, the notification was delayed by however long the transaction held the lock.
The fix was simple: emit events on a separate auto-committing connection. The emitEvent() function runs SELECT pg_notify(channel, payload) on a standalone pool query. It commits immediately, independent of whatever else is happening in the main transaction. This brought alert latency from the occasional 500ms+ spike down to a consistent sub-100ms from DB write to notification delivery.
The numbers
Over the past 30 days, the kol-tracker has indexed over 1.6 million trades and detected 9,404 coordination events (multiple tracked KOLs buying the same token within a short window). Coordination events are one of the strongest alpha signals in the Solana memecoin market — when three or four known profitable wallets converge on the same token independently, that token tends to move.
Deployer Listener: 10,600+ Pump.fun Deployers
While the kol-tracker watches known wallets, the deployer-listener watches the other end of the funnel: the wallets creating new tokens on Pump.fun. It tracks over 10,600 deployer wallets and ranks them by their historical bonding rate.
The service subscribes to the Pump.fun program ID via the same dual-region gRPC setup. Every token creation event updates the deployer's stats. Every bonding completion (when a token fills its curve and graduates to PumpSwap) recalculates the deployer's tier.
Bond detection and the 2-minute sweep
Bond detection turned out to be trickier than expected. The initial approach — watching for the Pump.fun migration instruction — missed bonds that happened via PumpSwap pool creation rather than the legacy Raydium migration path. After Pump.fun switched its graduation target to PumpSwap, we needed a different signal.
The current approach uses two layers:
-
Primary: gRPC subscription for PumpSwap pool creation events. When a new pool is created on PumpSwap, the mc-tracker detects it via its pool-state gRPC subscription and emits a pg_notify event. The deployer-listener picks this up and resolves the bond.
-
Safety net: 2-minute sweep. A periodic check queries for tokens that were flagged as "pending bond" (bonding curve hit the threshold) but never had their bond resolved. This catches the cases where gRPC missed the pool creation — which happens more often than you would expect. Account creation events are not always reliably delivered by Yellowstone, especially during periods of high slot density.
The combination gives us near-100% bond detection accuracy. The primary path resolves bonds in under a second. The sweep catches the stragglers within 2 minutes. Before adding the sweep, we were missing roughly 3-5% of bonds, which silently degraded deployer tier calculations.