<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Lambo Poewert</title>
    <description>The latest articles on DEV Community by Lambo Poewert (@lambopoewert).</description>
    <link>https://dev.to/lambopoewert</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3780819%2Ff361cb99-607a-4014-9d4f-8dd3aedf0868.png</url>
      <title>DEV Community: Lambo Poewert</title>
      <link>https://dev.to/lambopoewert</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/lambopoewert"/>
    <language>en</language>
    <item>
      <title>How I Built a Profitable Solana Intelligence Platform on a Single Server — €132/month</title>
      <dc:creator>Lambo Poewert</dc:creator>
      <pubDate>Tue, 21 Apr 2026 08:44:14 +0000</pubDate>
      <link>https://dev.to/lambopoewert/how-i-built-a-profitable-solana-intelligence-platform-on-a-single-server-eu132month-2dm</link>
      <guid>https://dev.to/lambopoewert/how-i-built-a-profitable-solana-intelligence-platform-on-a-single-server-eu132month-2dm</guid>
      <description>&lt;h1&gt;
  
  
  How I Built a Profitable Solana Intelligence Platform on a Single Server — €132/month
&lt;/h1&gt;

&lt;p&gt;Two months ago I started building MadeOnSol, a real-time trading intelligence platform for Solana. Today it processes 22,000+ API calls, tracks 1,000 KOL wallets across 11 DEXes, scores 17,000+ token deployers, and indexes 1.1 million buyer records.&lt;/p&gt;

&lt;p&gt;It runs on a single dedicated server. It's profitable. Here's the full technical breakdown.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Stack
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Framework:&lt;/strong&gt; Next.js (App Router, SSG)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Database:&lt;/strong&gt; Self-hosted Supabase (PostgreSQL + Auth)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Styling:&lt;/strong&gt; Tailwind CSS&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Real-time data:&lt;/strong&gt; Dual gRPC streams via Yellowstone (Triton) from Constant-K validator nodes&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Process manager:&lt;/strong&gt; PM2&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Reverse proxy:&lt;/strong&gt; Nginx&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;CDN/DNS:&lt;/strong&gt; Cloudflare (free tier)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Email:&lt;/strong&gt; Resend&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Analytics:&lt;/strong&gt; Umami (self-hosted)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Payments:&lt;/strong&gt; USDC and SOL on Solana (no Stripe, no credit cards)&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  The Server
&lt;/h2&gt;

&lt;p&gt;Hetzner dedicated server, €100/month:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Intel Xeon E-2176G (6 cores / 12 threads, 3.70GHz)&lt;/li&gt;
&lt;li&gt;64GB DDR4 ECC RAM&lt;/li&gt;
&lt;li&gt;2x Samsung PM983 960GB NVMe in RAID1 (database)&lt;/li&gt;
&lt;li&gt;2x Micron 5200 1.92TB SATA SSD in RAID1 (backups + historical data)&lt;/li&gt;
&lt;li&gt;1 Gbit unlimited traffic&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Plus ~€32/month for two Constant-K gRPC validator nodes (Frankfurt + New York). Total: €132/month.&lt;/p&gt;

&lt;h2&gt;
  
  
  How the Data Pipeline Works
&lt;/h2&gt;

&lt;p&gt;Solana processes thousands of transactions per second across dozens of DEX programs. I needed to capture every swap in real-time.&lt;/p&gt;

&lt;h3&gt;
  
  
  gRPC Streams
&lt;/h3&gt;

&lt;p&gt;I use Yellowstone gRPC (by Triton) to subscribe to transaction updates from Solana validators. Two streams run in parallel:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Frankfurt node&lt;/strong&gt; — primary, low latency for European users&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;New York node&lt;/strong&gt; — failover, low latency for US users&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Each stream subscribes to 11 DEX program IDs:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;DEX_PROGRAMS&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
  &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;PumpFun&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;PumpSwap&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
  &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Raydium&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Jupiter&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Orca&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Meteora&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;LetsBonk&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Bags.app&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="c1"&gt;// + 3 more&lt;/span&gt;
&lt;span class="p"&gt;];&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When a transaction comes in, the listener:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Parses the instruction data (each DEX has its own format)&lt;/li&gt;
&lt;li&gt;Extracts: wallet, token mint, SOL amount, direction (buy/sell), DEX name&lt;/li&gt;
&lt;li&gt;Checks against 1,000 tracked KOL wallets (O(1) hash lookup)&lt;/li&gt;
&lt;li&gt;Checks against 17,000+ scored deployer wallets&lt;/li&gt;
&lt;li&gt;Stores the trade in PostgreSQL&lt;/li&gt;
&lt;li&gt;Pushes to active WebSocket subscribers&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Detection latency: under 3 seconds from on-chain to API response.&lt;/p&gt;

&lt;h3&gt;
  
  
  Dual-Region Failover
&lt;/h3&gt;

&lt;p&gt;The two gRPC streams run independently. If Frankfurt drops, New York keeps processing. Reconnection is handled in-process — no PM2 restarts needed.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;GrpcStream&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="nf"&gt;connect&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;stream&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;subscribe&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
      &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;stream&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;on&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;data&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;handleTransaction&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;stream&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;on&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;error&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;reconnect&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nf"&gt;setTimeout&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;connect&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="mi"&gt;5000&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="nf"&gt;reconnect&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;stream&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nf"&gt;destroy&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;connect&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  KOL Tracking
&lt;/h2&gt;

&lt;p&gt;We maintain a list of 1,000 Solana KOL (Key Opinion Leader) wallets. For every trade detected, we check if the wallet is a tracked KOL.&lt;/p&gt;

&lt;p&gt;The lookup is a simple &lt;code&gt;Set&lt;/code&gt; in memory:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;kolWallets&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;loadKolWallets&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;

&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;handleTrade&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;trade&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;kolWallets&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;has&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;trade&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;wallet&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// Store KOL trade, calculate PnL, check coordination&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Coordination detection: when 3+ KOLs buy the same token within a time window, we flag it as a potential coordination signal.&lt;/p&gt;

&lt;h2&gt;
  
  
  Deployer Scoring
&lt;/h2&gt;

&lt;p&gt;Every Pump.fun token has a deployer wallet. We track all 17,000+ deployers and score them by:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Bonding rate&lt;/strong&gt; — what percentage of their tokens graduated from the bonding curve&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Lifetime deploys&lt;/strong&gt; — total tokens launched&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Recent performance&lt;/strong&gt; — last 10 tokens vs lifetime average&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Tiers: Elite (50%+ bond rate), Good (30-50%), Average (10-30%), Poor (&amp;lt;10%)&lt;/p&gt;

&lt;p&gt;The first 20 buyers on every Pump.fun token are also recorded. Over time this builds a dataset of 1.1 million+ buyer records that reveals which wallets consistently buy early on winning tokens.&lt;/p&gt;

&lt;h2&gt;
  
  
  Database Optimization
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Materialized Views
&lt;/h3&gt;

&lt;p&gt;The KOL leaderboard and deployer stats are expensive queries (joins + aggregations across millions of rows). Running them on every API request would kill performance.&lt;/p&gt;

&lt;p&gt;Solution: materialized views refreshed by pg_cron.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;CREATE&lt;/span&gt; &lt;span class="n"&gt;MATERIALIZED&lt;/span&gt; &lt;span class="k"&gt;VIEW&lt;/span&gt; &lt;span class="n"&gt;kol_pairs_summary&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt;
&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="n"&gt;wallet&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;token_mint&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
       &lt;span class="k"&gt;count&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;trade_count&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
       &lt;span class="k"&gt;sum&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;sol_amount&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;total_volume&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;kol_trades&lt;/span&gt;
&lt;span class="k"&gt;GROUP&lt;/span&gt; &lt;span class="k"&gt;BY&lt;/span&gt; &lt;span class="n"&gt;wallet&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;token_mint&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;-- Refresh every 5 minutes&lt;/span&gt;
&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="n"&gt;cron&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;schedule&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'refresh-kol-pairs'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'*/5 * * * *'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
  &lt;span class="s1"&gt;'REFRESH MATERIALIZED VIEW CONCURRENTLY kol_pairs_summary'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Result: &lt;code&gt;/kol/pairs&lt;/code&gt; endpoint went from 500ms to 5ms. 100x improvement.&lt;/p&gt;

&lt;h3&gt;
  
  
  Connection Pooling
&lt;/h3&gt;

&lt;p&gt;Self-hosted Supabase includes Supavisor for connection pooling. PostgreSQL's process-per-connection model means 200 connections = 200 OS processes. Supavisor multiplexes thousands of API requests through a smaller pool of actual database connections.&lt;/p&gt;

&lt;h2&gt;
  
  
  API Architecture
&lt;/h2&gt;

&lt;p&gt;Three tiers:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Tier&lt;/th&gt;
&lt;th&gt;Price&lt;/th&gt;
&lt;th&gt;Rate Limit&lt;/th&gt;
&lt;th&gt;Features&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Free&lt;/td&gt;
&lt;td&gt;$0&lt;/td&gt;
&lt;td&gt;200/day&lt;/td&gt;
&lt;td&gt;REST endpoints&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Pro&lt;/td&gt;
&lt;td&gt;$49/mo&lt;/td&gt;
&lt;td&gt;10,000/day&lt;/td&gt;
&lt;td&gt;REST + 1 WebSocket + 3 webhooks&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Ultra&lt;/td&gt;
&lt;td&gt;$149/mo&lt;/td&gt;
&lt;td&gt;100,000/day&lt;/td&gt;
&lt;td&gt;REST + 3 WebSocket + DEX firehose + 10 webhooks&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Authentication: Bearer token (&lt;code&gt;msk_&lt;/code&gt; prefixed API keys). Rate limiting: sliding window counter in PostgreSQL (considering Redis for this when load increases).&lt;/p&gt;

&lt;p&gt;Payments: USDC or SOL on Solana. No Stripe integration. Users connect their wallet, send payment to our treasury, and the API verifies the transaction on-chain.&lt;/p&gt;

&lt;h2&gt;
  
  
  WebSocket &amp;amp; DEX Firehose
&lt;/h2&gt;

&lt;p&gt;The DEX Firehose streams every swap across all 11 DEXes via WebSocket. Users subscribe with server-side filters:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"stream"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"dex_trades"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"filters"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"dexes"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"pumpfun"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"raydium"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"min_sol_amount"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"action_types"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"buy"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The server checks each incoming trade against all active subscriptions and only pushes matching events. One gRPC stream in, many filtered WebSocket streams out.&lt;/p&gt;

&lt;h2&gt;
  
  
  SEO: 9 to 7,652 Indexed Pages
&lt;/h2&gt;

&lt;p&gt;Programmatic SEO at scale:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;1,000+ tool pages with unique descriptions&lt;/li&gt;
&lt;li&gt;Comparison pages ("Tool A vs Tool B")&lt;/li&gt;
&lt;li&gt;Alternatives pages ("Best X alternatives")&lt;/li&gt;
&lt;li&gt;Best-of pages per category&lt;/li&gt;
&lt;li&gt;Schema markup, proper meta tags, XML sitemap&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Google indexed 94% of the sitemap. Bing's AI has cited the site 3,400+ times. ChatGPT and DuckDuckGo send referral traffic.&lt;/p&gt;

&lt;p&gt;The insight: AI search engines cite structured, detailed, honest content automatically. I didn't optimize for AI — I just built useful pages.&lt;/p&gt;

&lt;h2&gt;
  
  
  Scraper Defense
&lt;/h2&gt;

&lt;p&gt;With 7,652 indexed pages and growing traffic, scrapers showed up. Asian residential proxies running headless Chrome inflated the bounce rate from 54% to 91%.&lt;/p&gt;

&lt;p&gt;Defense layers:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Cloudflare Bot Fight Mode&lt;/strong&gt; — catches obvious bots&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Cloudflare firewall rules&lt;/strong&gt; — challenge traffic from VN/HK/JP/ID without a search engine referrer&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Nginx rate limiting&lt;/strong&gt; — per-IP request caps&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Supabase RLS&lt;/strong&gt; — intelligence data (KOL trades, deployer scores) requires authentication&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The tool directory is public (it's SEO content), but the paid data is gated.&lt;/p&gt;

&lt;h2&gt;
  
  
  Security Audit Findings
&lt;/h2&gt;

&lt;p&gt;Ran a full security audit in month 3. Key findings:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Critical:&lt;/strong&gt; Missing Row Level Security on payment-related tables. Fixed with a migration adding RLS policies.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Critical:&lt;/strong&gt; SSRF in webhook delivery — user-supplied URLs weren't validated. Fixed by reusing existing &lt;code&gt;isPrivateUrl()&lt;/code&gt; validation.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;High:&lt;/strong&gt; Unauthenticated routes using admin Supabase client (bypassing RLS). Data was intentionally public, but pattern was risky.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Medium:&lt;/strong&gt; 50+ &lt;code&gt;any&lt;/code&gt; types from untyped Supabase queries. Fixed by generating types with &lt;code&gt;supabase gen types typescript&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Capacity Planning
&lt;/h2&gt;

&lt;p&gt;Current load: ~2,250 API calls/day (0.03 requests/second).&lt;/p&gt;

&lt;p&gt;Server can handle conservatively 17 million calls/day (200 requests/second mixed workload). That's 7,500x current usage.&lt;/p&gt;

&lt;p&gt;Bottleneck order:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;PostgreSQL connections (fix: PgBouncer)&lt;/li&gt;
&lt;li&gt;CPU contention between gRPC + API + DB (fix: split servers)&lt;/li&gt;
&lt;li&gt;WebSocket file descriptors (fix: increase ulimit)&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Memory, disk I/O, and network are not bottlenecks.&lt;/p&gt;

&lt;p&gt;Scaling plan: this single server handles up to 500-1,000 active API users. When revenue justifies it, split into a database server (keep the Xeon) and a cheap app server (~€15/month).&lt;/p&gt;

&lt;h2&gt;
  
  
  Results After 2 Months
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;MRR:&lt;/strong&gt; $196 (4 Pro subscribers, profitable)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Registered developers:&lt;/strong&gt; 45&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;API calls:&lt;/strong&gt; 22,000+&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Data indexed:&lt;/strong&gt; 1.1M buyer records, 161K wallets, 17K deployers&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Google indexed pages:&lt;/strong&gt; 7,652&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;AI citations:&lt;/strong&gt; 3,400+&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Infrastructure cost:&lt;/strong&gt; €132/month&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Marketing spend:&lt;/strong&gt; €0&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Team size:&lt;/strong&gt; 1&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The entire platform — real-time gRPC streams, PostgreSQL, Next.js, WebSocket server, API, analytics — runs on a single €100/month dedicated server and is profitable.&lt;/p&gt;

&lt;h2&gt;
  
  
  Key Takeaways
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Self-hosting is viable for SaaS.&lt;/strong&gt; Supabase, PostgreSQL, Next.js on a dedicated server gives you full control at a fraction of cloud costs.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Materialized views solve most performance problems.&lt;/strong&gt; Before reaching for Redis, try pg_cron + materialized views.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Programmatic SEO works if the content is useful.&lt;/strong&gt; 7,652 pages indexed because each one answers a real question.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;AI search is a real distribution channel.&lt;/strong&gt; 3,400 citations without optimization.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Crypto payments reduce friction.&lt;/strong&gt; USDC on Solana is faster than Stripe for our audience.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Don't scale prematurely.&lt;/strong&gt; One server handles 7,500x our current load. Scale when you need to, not when you think you might.&lt;/li&gt;
&lt;/ol&gt;




&lt;p&gt;If you're building a data-intensive SaaS and wondering whether to go cloud or self-host, the math is clear: €132/month for a server that handles millions of daily requests vs $500+/month for equivalent cloud infrastructure.&lt;/p&gt;

&lt;p&gt;Happy to answer questions about any part of the stack.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://madeonsol.com" rel="noopener noreferrer"&gt;madeonsol.com&lt;/a&gt;&lt;/p&gt;

</description>
      <category>cryptocurrency</category>
      <category>solana</category>
      <category>web3</category>
      <category>saas</category>
    </item>
    <item>
      <title>Build a Solana KOL Copy-Trading Bot with Real-Time Webhooks</title>
      <dc:creator>Lambo Poewert</dc:creator>
      <pubDate>Fri, 03 Apr 2026 18:42:58 +0000</pubDate>
      <link>https://dev.to/lambopoewert/build-a-solana-kol-copy-trading-bot-with-real-time-webhooks-4988</link>
      <guid>https://dev.to/lambopoewert/build-a-solana-kol-copy-trading-bot-with-real-time-webhooks-4988</guid>
      <description>&lt;p&gt;Every profitable Solana trading bot has one thing in common: speed. The faster you know a KOL bought, the faster you can follow.&lt;/p&gt;

&lt;p&gt;Most APIs make you poll every few seconds. That wastes requests, adds latency, and burns your rate limit. The MadeOnSol API now supports &lt;strong&gt;webhooks&lt;/strong&gt; and &lt;strong&gt;WebSocket streaming&lt;/strong&gt; — your server gets notified the instant a trade happens.&lt;/p&gt;

&lt;p&gt;In this post I'll show you how to:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Set up a webhook to receive KOL trades in real-time&lt;/li&gt;
&lt;li&gt;Verify the HMAC signature to prove it's authentic&lt;/li&gt;
&lt;li&gt;Build a simple Express server that logs trades as they happen&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  What is MadeOnSol?
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://madeonsol.com" rel="noopener noreferrer"&gt;MadeOnSol&lt;/a&gt; tracks &lt;strong&gt;946 Solana KOL wallets&lt;/strong&gt; in real-time via dual gRPC streams across 9 DEX programs (Pump.fun, Raydium, Jupiter, Orca, etc.). It also monitors &lt;strong&gt;6,700+ Pump.fun deployers&lt;/strong&gt; and classifies them by bonding success rate.&lt;/p&gt;

&lt;p&gt;The API is available on &lt;a href="https://rapidapi.com/ClaudeTools/api/madeonsol-solana-kol-tracker-tools-api" rel="noopener noreferrer"&gt;RapidAPI&lt;/a&gt; with Free, Pro ($49/mo), and Ultra ($149/mo) tiers.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 1: Get your API key
&lt;/h2&gt;

&lt;p&gt;Sign up on &lt;a href="https://rapidapi.com/ClaudeTools/api/madeonsol-solana-kol-tracker-tools-api" rel="noopener noreferrer"&gt;RapidAPI&lt;/a&gt; and subscribe to Pro or Ultra (webhooks require a paid tier).&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 2: Register a webhook
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;API_KEY&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;your-rapidapi-key&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;HOST&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;madeonsol-solana-kol-tracker-tools-api.p.rapidapi.com&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;https://madeonsol.com/api/v1/webhooks&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;method&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;POST&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Content-Type&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;application/json&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;x-rapidapi-key&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;API_KEY&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;x-rapidapi-host&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;HOST&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="na"&gt;body&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stringify&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="na"&gt;url&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;https://your-server.com/webhooks/madeonsol&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;events&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;kol:trade&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;deployer:alert&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="na"&gt;filters&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;min_sol&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;// Only trades &amp;gt;= 1 SOL&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;}),&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Webhook ID:&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;webhook&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Secret:&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;webhook&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;secret&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// Save this! Only shown once.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The secret is an HMAC-SHA256 key. Every webhook delivery includes an &lt;code&gt;X-MadeOnSol-Signature&lt;/code&gt; header — you use the secret to verify it.&lt;/p&gt;

&lt;h3&gt;
  
  
  Available events
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Event&lt;/th&gt;
&lt;th&gt;Description&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;kol:trade&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;A KOL wallet buys or sells a token&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;kol:coordination&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Multiple KOLs converge on the same token&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;deployer:alert&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;A tracked deployer launches a new token&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;deployer:bond&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;A tracked deployer's token graduates the bonding curve&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  Available filters
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Filter&lt;/th&gt;
&lt;th&gt;Type&lt;/th&gt;
&lt;th&gt;Description&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;min_sol&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;number&lt;/td&gt;
&lt;td&gt;Minimum SOL amount (for kol:trade)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;action&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;string&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;"buy"&lt;/code&gt; or &lt;code&gt;"sell"&lt;/code&gt; only&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;kol_name&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;string&lt;/td&gt;
&lt;td&gt;Filter by specific KOL name&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;deployer_tier&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;string[]&lt;/td&gt;
&lt;td&gt;Filter by deployer tier: &lt;code&gt;["elite", "good"]&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;min_kols&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;number&lt;/td&gt;
&lt;td&gt;Minimum KOL count (for kol:coordination)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  Step 3: Build your webhook receiver
&lt;/h2&gt;

&lt;p&gt;Here's a minimal Express server that receives and verifies webhook payloads:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;express&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;express&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;crypto&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;crypto&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;app&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;express&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;use&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;express&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;WEBHOOK_SECRET&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;your-webhook-secret-from-step-2&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;post&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;/webhooks/madeonsol&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// 1. Verify HMAC signature&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;signature&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;x-madeonsol-signature&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;expected&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;crypto&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;createHmac&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;sha256&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;WEBHOOK_SECRET&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;update&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stringify&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;body&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;digest&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;hex&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;signature&lt;/span&gt; &lt;span class="o"&gt;!==&lt;/span&gt; &lt;span class="nx"&gt;expected&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;warn&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Invalid signature — rejecting&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;status&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;401&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;send&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Invalid signature&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="c1"&gt;// 2. Process the event&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;timestamp&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;body&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;kol:trade&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;kol_name&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;action&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;toUpperCase&lt;/span&gt;&lt;span class="p"&gt;()}&lt;/span&gt;&lt;span class="s2"&gt; &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;sol_amount&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; SOL`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="s2"&gt;`→ &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;token_symbol&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;token_mint&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;slice&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;8&lt;/span&gt;&lt;span class="p"&gt;)}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="s2"&gt;`at &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;timestamp&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;
    &lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="c1"&gt;// This is where you'd trigger your copy-trade logic:&lt;/span&gt;
    &lt;span class="c1"&gt;// await copyTrade(data.token_mint, data.action, data.sol_amount);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;deployer:alert&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;deployer_tier&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; deployer launched &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;token_symbol&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="s2"&gt;`Bond rate: &lt;/span&gt;&lt;span class="p"&gt;${(&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;bonding_rate&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;toFixed&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)}&lt;/span&gt;&lt;span class="s2"&gt;%`&lt;/span&gt;
    &lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;status&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;send&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;OK&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;listen&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;3000&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Webhook server running on :3000&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Step 4: Test your webhook
&lt;/h2&gt;

&lt;p&gt;Before waiting for a real trade, send a test payload to make sure everything works:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;https://madeonsol.com/api/v1/webhooks/test&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;method&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;POST&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Content-Type&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;application/json&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;x-rapidapi-key&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;API_KEY&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;x-rapidapi-host&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;HOST&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="na"&gt;body&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stringify&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;webhook_id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="p"&gt;}),&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This sends a sample KOL trade event to your URL. If your server responds 200, you'll see &lt;code&gt;{ success: true, status_code: 200, response_time_ms: ... }&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Alternative: WebSocket Streaming
&lt;/h2&gt;

&lt;p&gt;If you prefer a persistent connection instead of webhooks, you can use WebSocket streaming:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// 1. Get a streaming token (valid 24 hours)&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;tokenRes&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;https://madeonsol.com/api/v1/stream/token&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;method&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;POST&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;x-rapidapi-key&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;API_KEY&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;x-rapidapi-host&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;HOST&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;token&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;tokenRes&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="c1"&gt;// 2. Connect to WebSocket&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;WebSocket&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;ws&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;ws&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;WebSocket&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`wss://madeonsol.com/ws/v1/stream?token=&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;token&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="nx"&gt;ws&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;on&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;open&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// 3. Subscribe to channels&lt;/span&gt;
  &lt;span class="nx"&gt;ws&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;send&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stringify&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;subscribe&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;channels&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;kol:trades&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;deployer:alerts&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="na"&gt;filters&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;min_sol&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;}));&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="nx"&gt;ws&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;on&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;message&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;raw&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;msg&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;parse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;raw&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;toString&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;

  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;msg&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;heartbeat&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// 30s keepalive&lt;/span&gt;

  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;msg&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;kol:trade&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;msg&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;kol_name&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;msg&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;action&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;msg&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;sol_amount&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; SOL`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Connection limits:&lt;/strong&gt; Pro = 1 concurrent connection, Ultra = 3.&lt;/p&gt;

&lt;h2&gt;
  
  
  Retry &amp;amp; reliability
&lt;/h2&gt;

&lt;p&gt;MadeOnSol webhooks are built for reliability:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;3 retries&lt;/strong&gt; with exponential backoff (5s, 30s, 2min)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;10s timeout&lt;/strong&gt; per delivery attempt&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Auto-disable&lt;/strong&gt; after 10 consecutive failures (re-enable via PATCH)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Delivery log&lt;/strong&gt; — check recent deliveries with &lt;code&gt;GET /webhooks/{id}&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  What can you build with this?
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Copy-trading bot&lt;/strong&gt; — follow KOL buys automatically via Raydium/Jupiter&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Telegram/Discord alerts&lt;/strong&gt; — push KOL trades to a group chat&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Analytics dashboard&lt;/strong&gt; — track KOL performance in real-time&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Token screener&lt;/strong&gt; — filter new tokens by deployer reputation before buying&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Multi-KOL detector&lt;/strong&gt; — get notified when 3+ KOLs buy the same token&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  SDKs
&lt;/h2&gt;

&lt;p&gt;The MadeOnSol API has SDKs for multiple platforms:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;TypeScript:&lt;/strong&gt; &lt;code&gt;npm install madeonsol-x402&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Python:&lt;/strong&gt; &lt;code&gt;pip install madeonsol-x402&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;MCP Server (Claude/Cursor):&lt;/strong&gt; &lt;code&gt;npm install -g mcp-server-madeonsol&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;ElizaOS Plugin:&lt;/strong&gt; &lt;code&gt;npm install @madeonsol/plugin-madeonsol&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Solana Agent Kit:&lt;/strong&gt; &lt;code&gt;npm install solana-agent-kit-plugin-madeonsol&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Links
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;API Docs:&lt;/strong&gt; &lt;a href="https://madeonsol.com/api-docs" rel="noopener noreferrer"&gt;madeonsol.com/api-docs&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;RapidAPI:&lt;/strong&gt; &lt;a href="https://rapidapi.com/ClaudeTools/api/madeonsol-solana-kol-tracker-tools-api" rel="noopener noreferrer"&gt;Get API Key&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Website:&lt;/strong&gt; &lt;a href="https://madeonsol.com" rel="noopener noreferrer"&gt;madeonsol.com&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;GitHub:&lt;/strong&gt; &lt;a href="https://github.com/LamboPoewert" rel="noopener noreferrer"&gt;github.com/LamboPoewert&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>solana</category>
      <category>api</category>
      <category>webdev</category>
      <category>typescript</category>
    </item>
    <item>
      <title>I built a free Solana trading API — here's how to use it</title>
      <dc:creator>Lambo Poewert</dc:creator>
      <pubDate>Tue, 31 Mar 2026 17:56:47 +0000</pubDate>
      <link>https://dev.to/lambopoewert/i-built-a-free-solana-trading-api-heres-how-to-use-it-4e3n</link>
      <guid>https://dev.to/lambopoewert/i-built-a-free-solana-trading-api-heres-how-to-use-it-4e3n</guid>
      <description>&lt;p&gt;I run &lt;a href="https://madeonsol.com" rel="noopener noreferrer"&gt;MadeOnSol&lt;/a&gt;, a Solana tool directory. Over the past few months I built infrastructure that tracks 946 KOL (Key Opinion Leader) wallets in real-time and monitors 6,700+ Pump.fun token deployers. I packaged all of it into a public API with a TypeScript SDK.&lt;/p&gt;

&lt;p&gt;The free tier gives you 100 calls/day, no credit card. Here's what you can build with it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Install
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm &lt;span class="nb"&gt;install &lt;/span&gt;madeonsol
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;MadeOnSol&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;madeonsol&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;client&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;MadeOnSol&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;apiKey&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;RAPIDAPI_KEY&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Get your key from the &lt;a href="https://madeonsol.com/solana-api" rel="noopener noreferrer"&gt;MadeOnSol API page&lt;/a&gt; on RapidAPI.&lt;/p&gt;

&lt;h2&gt;
  
  
  What's in the API
&lt;/h2&gt;

&lt;p&gt;Three product lines:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;KOL Tracker&lt;/strong&gt; — Real-time trades from 946 tracked Solana influencer wallets. Sub-3-second detection via dual gRPC streams (Frankfurt + New York).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Deployer Hunter&lt;/strong&gt; — Reputation data for 6,700+ Pump.fun token deployers. Tier classification (elite/good/moderate/rising/cold) based on bonding rates and deployment history.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Tools Directory&lt;/strong&gt; — 1,000+ Solana tools and dApps, searchable by name or category.&lt;/p&gt;

&lt;h2&gt;
  
  
  Use case 1: KOL trade alert bot
&lt;/h2&gt;

&lt;p&gt;Track what Solana KOLs are buying in real-time and alert yourself when someone drops serious SOL on a token.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;pollKolBuys&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;trades&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;kol&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;feed&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;limit&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;action&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;buy&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;

  &lt;span class="k"&gt;for &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;trade&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt; &lt;span class="nx"&gt;trades&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;trade&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;sol_amount&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;trade&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;kol_name&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; bought $&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;trade&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;token_symbol&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; for &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;trade&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;sol_amount&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; SOL`&lt;/span&gt;
      &lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="c1"&gt;// Send to Telegram, Discord, etc.&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nf"&gt;setInterval&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;pollKolBuys&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;30&lt;/span&gt;&lt;span class="nx"&gt;_000&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nf"&gt;pollKolBuys&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You could pipe this into a Telegram bot, Discord webhook, or just log to CSV for later analysis.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Rate limit note:&lt;/strong&gt; 30-second polling = ~2,880 calls/day. Free tier is 100/day, so increase the interval to 5 minutes for testing, or use a paid plan ($49/mo for 10K calls/day) for production.&lt;/p&gt;

&lt;h2&gt;
  
  
  Use case 2: Check a deployer before you ape
&lt;/h2&gt;

&lt;p&gt;Before buying a freshly launched Pump.fun token, check if the deployer has a history of tokens that actually bond to Raydium — or if they're a serial rugger.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;checkDeployer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;wallet&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;deployer&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;deployer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;profile&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;wallet&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`Tier: &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;deployer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;tier&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`Bond rate: &lt;/span&gt;&lt;span class="p"&gt;${(&lt;/span&gt;&lt;span class="nx"&gt;deployer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;bonding_rate&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;toFixed&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)}&lt;/span&gt;&lt;span class="s2"&gt;%`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`Total deployed: &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;deployer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;total_tokens_deployed&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`Total bonded: &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;deployer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;total_bonded&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;deployer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;tier&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;elite&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nx"&gt;deployer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;tier&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;good&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Solid track record&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Proceed with caution&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nf"&gt;checkDeployer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;7xKXtg2CW87d97TXJSDpbD5jBkheTqA83TZRuJosgAsU&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The tier system:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Elite&lt;/strong&gt; — consistently creates tokens that graduate to Raydium&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Good&lt;/strong&gt; — above-average bonding rate&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Moderate/Rising&lt;/strong&gt; — mixed or new, not a strong signal either way&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Cold&lt;/strong&gt; — low bonding rate, most tokens die on the curve&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;One API call per token. 100 free calls = 100 deployer checks per day.&lt;/p&gt;

&lt;h2&gt;
  
  
  Use case 3: Multi-KOL convergence scanner
&lt;/h2&gt;

&lt;p&gt;When 3+ KOLs buy the same token within hours, that's a different signal than one influencer shilling their bags. The coordination endpoint finds these patterns.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;findConvergence&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;tokens&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;kol&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;coordination&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="na"&gt;period&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;24h&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;min_kols&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;

  &lt;span class="k"&gt;for &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;token&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt; &lt;span class="nx"&gt;tokens&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;direction&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;token&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;net_flow&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;accumulating&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;distributing&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;token&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;token_symbol&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;: &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;token&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;kol_count&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; KOLs, &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;direction&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;
    &lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nf"&gt;findConvergence&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Filter for only accumulating tokens and sort by KOL count:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;accumulating&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;tokens&lt;/span&gt;
  &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;filter&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;t&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;t&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;net_flow&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;sort&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;b&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;b&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;kol_count&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;kol_count&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Chain them together
&lt;/h2&gt;

&lt;p&gt;The real value is combining all three. Find convergence signals, check deployer reputation, output a ranked list:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;fullScan&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;tokens&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;kol&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;coordination&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="na"&gt;period&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;4h&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;min_kols&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;

  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;results&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[];&lt;/span&gt;

  &lt;span class="k"&gt;for &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;token&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt; &lt;span class="nx"&gt;tokens&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;filter&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;t&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;t&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;net_flow&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;token&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;deployer_wallet&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;deployer&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;deployer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;profile&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="nx"&gt;token&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;deployer_wallet&lt;/span&gt;
      &lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="nx"&gt;results&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;push&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
        &lt;span class="na"&gt;symbol&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;token&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;token_symbol&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;kolCount&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;token&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;kol_count&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;deployerTier&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;deployer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;tier&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;bondRate&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;deployer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;bonding_rate&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="p"&gt;});&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;tierOrder&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;elite&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;good&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;average&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;poor&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;
  &lt;span class="nx"&gt;results&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;sort&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;b&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;diff&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
      &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;tierOrder&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;b&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;deployerTier&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;tierOrder&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;deployerTier&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;diff&lt;/span&gt; &lt;span class="o"&gt;!==&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="nx"&gt;diff&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;b&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;kolCount&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;kolCount&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;

  &lt;span class="k"&gt;for &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;r&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt; &lt;span class="nx"&gt;results&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;r&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;symbol&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; — &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;r&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;kolCount&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; KOLs, deployer: &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;r&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;deployerTier&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; (&lt;/span&gt;&lt;span class="p"&gt;${(&lt;/span&gt;&lt;span class="nx"&gt;r&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;bondRate&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;toFixed&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)}&lt;/span&gt;&lt;span class="s2"&gt;% bond rate)`&lt;/span&gt;
    &lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nf"&gt;fullScan&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Each token in the results triggers a deployer lookup, so a scan with 10 tokens = 11 API calls total.&lt;/p&gt;

&lt;h2&gt;
  
  
  Error handling
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;deployer&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;deployer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;profile&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;wallet&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;status&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="mi"&gt;404&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Deployer not in database&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;status&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="mi"&gt;429&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Rate limited — back off and retry&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Error:&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;message&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Full endpoint list
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;KOL Tracker:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;client.kol.feed()&lt;/code&gt; — real-time trade feed&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;client.kol.leaderboard()&lt;/code&gt; — PnL rankings&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;client.kol.wallet(address)&lt;/code&gt; — individual KOL stats&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;client.kol.coordination()&lt;/code&gt; — multi-wallet convergence&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;client.kol.tokenFlow(mint)&lt;/code&gt; — KOL flow per token&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Deployer Hunter:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;client.deployer.stats()&lt;/code&gt; — global stats&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;client.deployer.leaderboard()&lt;/code&gt; — ranked deployers&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;client.deployer.profile(wallet)&lt;/code&gt; — deployer details (Pro)&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;client.deployer.tokens(wallet)&lt;/code&gt; — token history (Pro)&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;client.deployer.alerts()&lt;/code&gt; — new launch alerts&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;client.deployer.alertStats()&lt;/code&gt; — alert performance&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;client.deployer.bestTokens()&lt;/code&gt; — top performers&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;client.deployer.recentBonds()&lt;/code&gt; — recent graduations&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Tools:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;client.tools.search()&lt;/code&gt; — search 1,000+ tools&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Pricing
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Plan&lt;/th&gt;
&lt;th&gt;Price&lt;/th&gt;
&lt;th&gt;Daily limit&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Basic&lt;/td&gt;
&lt;td&gt;Free&lt;/td&gt;
&lt;td&gt;100&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Pro&lt;/td&gt;
&lt;td&gt;$49/mo&lt;/td&gt;
&lt;td&gt;10,000&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Ultra&lt;/td&gt;
&lt;td&gt;$149/mo&lt;/td&gt;
&lt;td&gt;100,000&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  Links
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://madeonsol.com/solana-api" rel="noopener noreferrer"&gt;API product page + sign up&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://madeonsol.com/api-docs" rel="noopener noreferrer"&gt;Full API docs&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.npmjs.com/package/madeonsol" rel="noopener noreferrer"&gt;npm package&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/LamboPoewert/madeonsol-sdk" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://madeonsol.com" rel="noopener noreferrer"&gt;MadeOnSol directory&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you build something with it, I'd love to see it. Drop a comment or find me on &lt;a href="https://twitter.com/MadeOnSolx" rel="noopener noreferrer"&gt;Twitter&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>solana</category>
      <category>typescript</category>
      <category>webdev</category>
      <category>api</category>
    </item>
    <item>
      <title>Moved from hosted Supabase to self-hosted on a single VPS — here's what I learned after weeks in production</title>
      <dc:creator>Lambo Poewert</dc:creator>
      <pubDate>Fri, 27 Mar 2026 08:23:55 +0000</pubDate>
      <link>https://dev.to/lambopoewert/moved-from-hosted-supabase-to-self-hosted-on-a-single-vps-heres-what-i-learned-after-weeks-in-2om1</link>
      <guid>https://dev.to/lambopoewert/moved-from-hosted-supabase-to-self-hosted-on-a-single-vps-heres-what-i-learned-after-weeks-in-2om1</guid>
      <description>&lt;p&gt;Most self-hosting guides show you how to get Supabase running. Not many tell you what happens after you've been running it for months with real users and real data. Here's what I've learned.&lt;/p&gt;

&lt;h2&gt;
  
  
  The project
&lt;/h2&gt;

&lt;p&gt;I built &lt;a href="https://madeonsol.com" rel="noopener noreferrer"&gt;MadeOnSol&lt;/a&gt;, a Solana ecosystem directory that tracks 950+ tools across 26 categories. The stack includes 74 Postgres tables, 100k+ real-time trade records, Google OAuth, file storage for 900+ tool logos, and Row Level Security on every table.&lt;/p&gt;

&lt;p&gt;It started on Supabase's free hosted tier. Four months ago I moved everything to a self-hosted Docker setup on a Hetzner CPX32 (8GB RAM, 4 vCPU, €15/month). Here's what I'd want to know if I were doing it again.&lt;/p&gt;

&lt;h2&gt;
  
  
  How much resources does it actually use?
&lt;/h2&gt;

&lt;p&gt;This is the question nobody answers well. The default self-hosted Supabase ships with ~15 containers. I stripped it down to 8 by removing Studio, Edge Functions, Analytics, Vector, Meta, and the Deno cache. None of them were doing anything useful in production.&lt;/p&gt;

&lt;p&gt;Here's what the remaining services actually consume:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Service&lt;/th&gt;
&lt;th&gt;Memory&lt;/th&gt;
&lt;th&gt;What it does&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;supabase-db&lt;/td&gt;
&lt;td&gt;~413 MB&lt;/td&gt;
&lt;td&gt;Postgres 15&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;supabase-kong&lt;/td&gt;
&lt;td&gt;~429 MB&lt;/td&gt;
&lt;td&gt;API gateway (yes, really)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;realtime&lt;/td&gt;
&lt;td&gt;~169 MB&lt;/td&gt;
&lt;td&gt;WebSocket subscriptions&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;supabase-storage&lt;/td&gt;
&lt;td&gt;~109 MB&lt;/td&gt;
&lt;td&gt;File storage&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;supabase-pooler&lt;/td&gt;
&lt;td&gt;~60 MB&lt;/td&gt;
&lt;td&gt;Supavisor connection pooling&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;supabase-rest&lt;/td&gt;
&lt;td&gt;~40 MB&lt;/td&gt;
&lt;td&gt;PostgREST&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;supabase-auth&lt;/td&gt;
&lt;td&gt;~22 MB&lt;/td&gt;
&lt;td&gt;GoTrue (email + Google OAuth)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;supabase-imgproxy&lt;/td&gt;
&lt;td&gt;~17 MB&lt;/td&gt;
&lt;td&gt;Image transforms&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Total: ~1.26 GB.&lt;/strong&gt; On 8GB that leaves plenty of room for a Next.js app, three background services, Nginx, and Umami analytics.&lt;/p&gt;

&lt;h2&gt;
  
  
  The things that went right
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Co-located database performance.&lt;/strong&gt; With the database on the same machine as the app, API responses went from ~120ms (hosted) to ~30ms. No network hop makes a real difference when your background services are doing batch operations on 100k+ records.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Supavisor connection pooling is solid.&lt;/strong&gt; Session mode on port 5432 for the Next.js app, transaction mode on port 6543 for background scripts. Set it up once, never thought about it again.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Storage migration was seamless.&lt;/strong&gt; Copied all files over, updated URLs, and the storage API behaved identically to hosted. No app code changes needed.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Google OAuth just worked.&lt;/strong&gt; Set the &lt;code&gt;GOTRUE_EXTERNAL_GOOGLE_*&lt;/code&gt; environment variables, configured the consent screen, done. One gotcha: I use a manual PKCE flow with localStorage instead of cookies because cross-site cookie loss was causing redirect issues.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Backups went from "nothing" to solid.&lt;/strong&gt; On the free hosted tier I had zero backups. Now it's &lt;code&gt;pg_dump&lt;/code&gt; → gzip → SSH to a separate storage box, running at 3 AM daily with 30-day retention. Peace of mind for ~5 lines of bash.&lt;/p&gt;

&lt;h2&gt;
  
  
  The things that bit me
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Kong uses an absurd amount of memory.&lt;/strong&gt; 429 MB for an API gateway. It spikes to 600MB+ under load. It's the single biggest memory consumer after Postgres itself. I've considered replacing it with Nginx routing but Kong handles auth middleware that would be painful to replicate.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Realtime goes silently bad.&lt;/strong&gt; The container reports "healthy" to Docker but stops delivering messages. The only fix is &lt;code&gt;docker compose restart realtime&lt;/code&gt;. I now run a health check every 5 minutes that tests actual message delivery, not just the HTTP health endpoint.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;JWT rotation will break every active session.&lt;/strong&gt; I rotated my JWT secret once and every user got stuck in auth loops because their browser cookies had the old token. I wrote middleware that detects stale &lt;code&gt;sb-*&lt;/code&gt; cookies and clears them automatically. Also learned the hard way: use &lt;code&gt;docker compose up -d --force-recreate&lt;/code&gt; when changing env vars, not &lt;code&gt;docker compose restart&lt;/code&gt; — restart doesn't re-read the &lt;code&gt;.env&lt;/code&gt; file.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The default docker-compose is a security risk.&lt;/strong&gt; Ports are bound to &lt;code&gt;0.0.0.0&lt;/code&gt; by default, which means your Postgres, PostgREST, and Kong are accessible to the entire internet. Change every port binding to &lt;code&gt;127.0.0.1:PORT:PORT&lt;/code&gt; and put Nginx with SSL in front of everything.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Upgrades are a gamble.&lt;/strong&gt; The self-hosted repo moves fast and I've skipped most updates. My rule: only upgrade if there's a specific bug fix I need. Always pin your Docker image versions.&lt;/p&gt;

&lt;h2&gt;
  
  
  Security checklist
&lt;/h2&gt;

&lt;p&gt;If you're self-hosting, this is the minimum:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Nginx reverse proxy with Let's Encrypt SSL in front of Kong (port 8000)&lt;/li&gt;
&lt;li&gt;UFW firewall allowing only Cloudflare IPs on 80/443&lt;/li&gt;
&lt;li&gt;Every Docker port bound to 127.0.0.1&lt;/li&gt;
&lt;li&gt;RLS on every table — self-hosting doesn't mean you can skip this&lt;/li&gt;
&lt;li&gt;Admin checks via &lt;code&gt;app_metadata.is_admin&lt;/code&gt; (not &lt;code&gt;user_metadata&lt;/code&gt;, which users can modify themselves)&lt;/li&gt;
&lt;li&gt;Rate limiting at both the Nginx and application layer&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  When it makes sense (and when it doesn't)
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Self-host if&lt;/strong&gt; you're comfortable with Postgres, your project is outgrowing the free tier, you want direct database access for cron jobs and background processing, or you want full control over backups.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Stay on hosted&lt;/strong&gt; if you want zero ops overhead, rely on Supabase Studio, don't want to manage Docker/Nginx/SSL, or your project fits comfortably on the free tier.&lt;/p&gt;

&lt;p&gt;For me the tipping point was having background services that needed direct Postgres access for heavy batch work. The co-located performance and €15/month flat cost made it an easy call.&lt;/p&gt;

&lt;p&gt;The full stack — Supabase, Next.js, three background services, Nginx, and Umami — all runs on a single VPS. Happy to answer questions about any part of the setup.&lt;/p&gt;

</description>
      <category>supabase</category>
      <category>postgres</category>
      <category>selfhosted</category>
      <category>webdev</category>
    </item>
    <item>
      <title>How I Built a Real-Time KOL Trade Tracker on Solana Using Dual gRPC Streams</title>
      <dc:creator>Lambo Poewert</dc:creator>
      <pubDate>Wed, 25 Mar 2026 18:52:32 +0000</pubDate>
      <link>https://dev.to/lambopoewert/how-i-built-a-real-time-kol-trade-tracker-on-solana-using-dual-grpc-streams-3df</link>
      <guid>https://dev.to/lambopoewert/how-i-built-a-real-time-kol-trade-tracker-on-solana-using-dual-grpc-streams-3df</guid>
      <description>&lt;h1&gt;
  
  
  I built a real-time KOL wallet tracker that monitors 463 Solana wallets — here's how it works
&lt;/h1&gt;

&lt;p&gt;Every second, hundreds of transactions flow through Solana's DEX programs. Somewhere in that firehose, a whale wallet just bought 50 SOL of a token nobody's heard of. By the time it shows up on a block explorer, the price has already moved.&lt;/p&gt;

&lt;p&gt;I built a system that detects these trades in real-time for 463 tracked KOL (Key Opinion Leader) wallets — every buy and sell, within seconds. It's processed over 100,000 trades across 10,900+ unique tokens so far.&lt;/p&gt;

&lt;p&gt;Here's how it works.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Architecture
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Kaldera gRPC (Frankfurt) ──┐
                            ├──▶ Parse ──▶ Filter KOL wallets ──▶ PostgreSQL
Kaldera gRPC (New York) ───┘        ▲              │
                                    │              ▼
                              Dedup via        WebSocket
                            tx_signature      fan-out to
                           ON CONFLICT        frontend
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The core idea: subscribe to every DEX transaction on Solana via gRPC, filter client-side for wallets we care about, then store and broadcast the trades.&lt;/p&gt;




&lt;h2&gt;
  
  
  Why gRPC, Not WebSocket or Polling
&lt;/h2&gt;

&lt;p&gt;Most Solana data tools use WebSocket subscriptions (&lt;code&gt;onLogs&lt;/code&gt;, &lt;code&gt;onAccountChange&lt;/code&gt;) or poll RPC endpoints. Both have problems at this scale:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;WebSocket subscriptions&lt;/strong&gt; on public RPCs have strict limits (usually 100-200 subscriptions). We track 463 wallets across 9 DEX programs — that's way over the limit.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Polling&lt;/strong&gt; &lt;code&gt;getSignaturesForAddress&lt;/code&gt; for 463 wallets would mean ~463 RPC calls every few seconds. You'll hit rate limits instantly.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;gRPC (via Yellowstone/Geyser plugins) gives you the full transaction firehose at the commitment level you choose. One subscription, all DEX transactions, filtered client-side:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;Client&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;CommitmentLevel&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@triton-one/yellowstone-grpc&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;DEX_PROGRAMS&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;6EF8rrecthR5Dkzon8Nwu78hRvfCKubJ14M5uBEwF6P&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;  &lt;span class="c1"&gt;// Pump.fun&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;pAMMBay6oceH9fJKBRHGP5D4bD4sWpmSwMn52FMfXEA&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;  &lt;span class="c1"&gt;// Pump AMM&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;675kPX9MHTjS2zt1qfr1NYHuzeLXfQM9H24wFSUt1Mp8&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;  &lt;span class="c1"&gt;// Raydium V4&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;JUP6LkbZbjS1jKKwapdHNy74zcZ3tLUZoi5QNyVTaV4&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;   &lt;span class="c1"&gt;// Jupiter V6&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;whirLbMiicVdio4qvUfM5KAg6Ct8VwpYzGff3uctyCc&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;   &lt;span class="c1"&gt;// Orca Whirlpool&lt;/span&gt;
  &lt;span class="c1"&gt;// ... 4 more&lt;/span&gt;
&lt;span class="p"&gt;];&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;stream&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;subscribe&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="nx"&gt;stream&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;write&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;transactions&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;swaps&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;accountInclude&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;DEX_PROGRAMS&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;vote&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;failed&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="na"&gt;commitment&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;CommitmentLevel&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;CONFIRMED&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This gives us every confirmed DEX swap. On a typical day, that's ~15,000-20,000 transactions every 30 seconds. We then check if any account in the transaction matches our KOL wallet set.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Dual-Stream Failover
&lt;/h2&gt;

&lt;p&gt;gRPC connections reset hourly (HTTP/2 GOAWAY frames), and servers occasionally go down for maintenance. With a single stream, you get a gap: disconnect → reconnect → backfill. During that gap, you miss trades.&lt;/p&gt;

&lt;p&gt;The fix: run two streams simultaneously from different regions.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;GRPC_REGIONS&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;url&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;KALDERA_GRPC_URL&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;label&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Frankfurt&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;url&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;KALDERA_GRPC_URL_FALLBACK&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;label&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;New York&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;];&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;streams&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Map&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nb"&gt;Promise&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;allSettled&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="nx"&gt;GRPC_REGIONS&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;region&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;connectRegion&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;region&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Both streams fire simultaneously. When Frankfurt disconnects for its hourly h2 reset, New York keeps detecting trades. When New York reconnects, Frankfurt covers it. Zero gaps.&lt;/p&gt;

&lt;p&gt;"But won't you get duplicate trades?" Yes — and that's fine. PostgreSQL handles it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;INSERT&lt;/span&gt; &lt;span class="k"&gt;INTO&lt;/span&gt; &lt;span class="n"&gt;kol_trades&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;kol_wallet_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;token_mint&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;action&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;sol_amount&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;tx_signature&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;...)&lt;/span&gt;
&lt;span class="k"&gt;ON&lt;/span&gt; &lt;span class="n"&gt;CONFLICT&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;tx_signature&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;DO&lt;/span&gt; &lt;span class="k"&gt;NOTHING&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;tx_signature&lt;/code&gt; is unique per transaction. Duplicates are silently dropped. In practice, both streams process ~18,000 tx each per 30-second window, but only unique trades get stored.&lt;/p&gt;




&lt;h2&gt;
  
  
  Parsing the Swap
&lt;/h2&gt;

&lt;p&gt;A DEX transaction doesn't come with a nice label saying "buy 2.5 SOL of TOKEN_X." You have to reconstruct the trade from raw balance changes:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;parseSwap&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;txUpdate&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;meta&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;txUpdate&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;transaction&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nx"&gt;transaction&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nx"&gt;meta&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;msg&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;txUpdate&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;transaction&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nx"&gt;transaction&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nx"&gt;message&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="c1"&gt;// Build full account key list (static + loaded)&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;accountKeys&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;msg&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;accountKeys&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;k&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;bs58&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;encode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;Buffer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;from&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;k&lt;/span&gt;&lt;span class="p"&gt;))),&lt;/span&gt;
    &lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;meta&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;loadedWritableAddresses&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;k&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;bs58&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;encode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;Buffer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;from&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;k&lt;/span&gt;&lt;span class="p"&gt;))),&lt;/span&gt;
    &lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;meta&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;loadedReadonlyAddresses&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;k&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;bs58&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;encode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;Buffer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;from&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;k&lt;/span&gt;&lt;span class="p"&gt;))),&lt;/span&gt;
  &lt;span class="p"&gt;];&lt;/span&gt;

  &lt;span class="c1"&gt;// Find our KOL wallet in the transaction&lt;/span&gt;
  &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;kolAddress&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="k"&gt;for &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;key&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt; &lt;span class="nx"&gt;accountKeys&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;kolWalletSet&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;has&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;key&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;kolAddress&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;key&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
      &lt;span class="k"&gt;break&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;kolAddress&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="c1"&gt;// SOL balance change = sell amount or buy cost&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;preSol&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Number&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;meta&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;preBalances&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;walletIndex&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="nx"&gt;e9&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;postSol&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Number&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;meta&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;postBalances&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;walletIndex&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="nx"&gt;e9&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;solChange&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;postSol&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nx"&gt;preSol&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="c1"&gt;// Token balance changes (SPL tokens)&lt;/span&gt;
  &lt;span class="k"&gt;for &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;post&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt; &lt;span class="nx"&gt;meta&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;postTokenBalances&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;post&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;owner&lt;/span&gt; &lt;span class="o"&gt;!==&lt;/span&gt; &lt;span class="nx"&gt;kolAddress&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;continue&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;pre&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;meta&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;preTokenBalances&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;find&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="nx"&gt;p&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;p&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;accountIndex&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="nx"&gt;post&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;accountIndex&lt;/span&gt;
    &lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;change&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;post&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;uiTokenAmount&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nx"&gt;uiAmount&lt;/span&gt; &lt;span class="o"&gt;??&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                 &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;pre&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nx"&gt;uiTokenAmount&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nx"&gt;uiAmount&lt;/span&gt; &lt;span class="o"&gt;??&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;abs&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;change&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mf"&gt;0.000001&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;tokenChanges&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;push&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;mint&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;post&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;mint&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;change&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="c1"&gt;// Positive token change + negative SOL change = buy&lt;/span&gt;
  &lt;span class="c1"&gt;// Negative token change + positive SOL change = sell&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;action&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;primaryToken&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;change&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;buy&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;sell&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The key insight: compare &lt;code&gt;preBalances&lt;/code&gt; vs &lt;code&gt;postBalances&lt;/code&gt; for SOL, and &lt;code&gt;preTokenBalances&lt;/code&gt; vs &lt;code&gt;postTokenBalances&lt;/code&gt; for SPL tokens. The wallet that gained tokens and lost SOL is buying. Vice versa for selling.&lt;/p&gt;




&lt;h2&gt;
  
  
  Handling the Edge Cases
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Token metadata isn't available immediately.&lt;/strong&gt; When a token launches on Pump.fun, the Metaplex metadata might not be queryable for a few seconds. We store trades with &lt;code&gt;token_name: null&lt;/code&gt; and retry every 2 minutes:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nf"&gt;setInterval&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;rows&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;pool&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;query&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="s2"&gt;`SELECT DISTINCT token_mint FROM kol_trades
     WHERE token_name IS NULL
     AND traded_at &amp;gt; NOW() - INTERVAL '24 hours' LIMIT 20`&lt;/span&gt;
  &lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="k"&gt;for &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;token_mint&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt; &lt;span class="nx"&gt;rows&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;meta&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;getTokenMeta&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;token_mint&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;meta&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;pool&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;query&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="s2"&gt;`UPDATE kol_trades SET token_name = $1, token_symbol = $2
         WHERE token_mint = $3 AND token_name IS NULL`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;meta&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;meta&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;symbol&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;token_mint&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
      &lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;60&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;1000&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;RPC can go down.&lt;/strong&gt; Metadata fetches use a circuit breaker — after 5 consecutive failures, we pause RPC calls for 30 seconds instead of hammering a dead endpoint.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Backfill on reconnect.&lt;/strong&gt; When the service starts (or both streams were down), we check the last 10 transactions of recently active wallets via &lt;code&gt;getSignaturesForAddress&lt;/code&gt; to catch anything missed.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Watchdog
&lt;/h2&gt;

&lt;p&gt;Each stream has an independent watchdog. If no data arrives for 60 seconds, the stream is considered dead and gets force-reconnected:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nf"&gt;setInterval&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;for &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;region&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt; &lt;span class="nx"&gt;GRPC_REGIONS&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;state&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;streams&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;region&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;label&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;silenceMs&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;Date&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;now&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nx"&gt;state&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;lastAliveAt&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;silenceMs&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;60000&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nx"&gt;state&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;stream&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`[&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;region&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;label&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;] Watchdog: forcing reconnect`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="nx"&gt;state&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;stream&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;cancel&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
      &lt;span class="nx"&gt;state&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;stream&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
      &lt;span class="nf"&gt;scheduleReconnect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;region&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// exponential backoff&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="mi"&gt;30000&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The heartbeat file (&lt;code&gt;/tmp/kol-tracker-heartbeat&lt;/code&gt;) gets picked up by our health monitor, which can auto-restart the PM2 process if things go truly sideways.&lt;/p&gt;




&lt;h2&gt;
  
  
  Results
&lt;/h2&gt;

&lt;p&gt;The system runs 24/7 on a single Hetzner VPS (8GB RAM), tracking 463 KOL wallets across 9 DEX programs. Some numbers:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;100,000+&lt;/strong&gt; trades detected so far&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;~21,000&lt;/strong&gt; trades per day on active days&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;10,900+&lt;/strong&gt; unique tokens tracked&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&amp;lt;3 second&lt;/strong&gt; latency from on-chain confirmation to database&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Zero gaps&lt;/strong&gt; since implementing dual-stream failover&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;~200MB&lt;/strong&gt; memory footprint per stream&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The whole thing is a single Node.js process managed by PM2. No Kubernetes, no message queues, no microservices. Sometimes the simplest architecture is the right one.&lt;/p&gt;




&lt;h2&gt;
  
  
  Stack
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;gRPC:&lt;/strong&gt; @triton-one/yellowstone-grpc (Yellowstone Geyser plugin client)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Database:&lt;/strong&gt; PostgreSQL (self-hosted Supabase)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Process:&lt;/strong&gt; PM2 on Ubuntu&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Infrastructure:&lt;/strong&gt; Kaldera gRPC nodes (Frankfurt + New York)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Frontend:&lt;/strong&gt; Next.js with WebSocket fan-out for real-time updates&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;The KOL Tracker is part of &lt;a href="https://madeonsol.com/kol-tracker" rel="noopener noreferrer"&gt;madeonsol.com/kol-tracker&lt;/a&gt; — a directory of 950+ Solana tools I've been building. The live leaderboard shows who's profiting and who's not, updated in real-time.&lt;/p&gt;

&lt;p&gt;If you're building on Solana and want to work with gRPC streams, the Yellowstone plugin is incredible. Just make sure you handle disconnects gracefully — the firehose is powerful but unforgiving.&lt;/p&gt;

</description>
      <category>solana</category>
      <category>webdev</category>
      <category>javascript</category>
      <category>blockchain</category>
    </item>
    <item>
      <title># Building the Largest Solana Tool Directory — Progress Report</title>
      <dc:creator>Lambo Poewert</dc:creator>
      <pubDate>Mon, 23 Mar 2026 19:12:26 +0000</pubDate>
      <link>https://dev.to/lambopoewert/-building-the-largest-solana-tool-directory-progress-report-25pk</link>
      <guid>https://dev.to/lambopoewert/-building-the-largest-solana-tool-directory-progress-report-25pk</guid>
      <description>&lt;p&gt;A month ago I started building &lt;a href="https://madeonsol.com" rel="noopener noreferrer"&gt;MadeOnSol&lt;/a&gt;, a curated directory and intelligence platform for the Solana ecosystem. This is a full progress report — what I shipped, what's working, what I learned, and where things stand today.&lt;/p&gt;




&lt;h2&gt;
  
  
  What is MadeOnSol
&lt;/h2&gt;

&lt;p&gt;MadeOnSol is a free, unbiased directory of Solana tools — trading bots, DEXs, wallets, DeFi protocols, analytics platforms, developer tools, and more. Every listing has honest descriptions, pros/cons, pricing, and alternatives. No paid rankings.&lt;/p&gt;

&lt;p&gt;On top of the directory, I built a set of intelligence tools that track on-chain activity in real time.&lt;/p&gt;




&lt;h2&gt;
  
  
  What I shipped
&lt;/h2&gt;

&lt;h3&gt;
  
  
  The directory
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;815 tools&lt;/strong&gt; across &lt;strong&gt;26 categories&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Each tool has its own page with description, features, pros/cons, pricing model, alternatives, and comparison links&lt;/li&gt;
&lt;li&gt;Category pages, best-of pages, and comparison pages (Axiom vs BullX, Phantom vs Solflare, etc.)&lt;/li&gt;
&lt;li&gt;Community reviews with a monthly reward system — top 3 contributors earn 0.25 SOL each&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Intelligence tools
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Deployer Hunter&lt;/strong&gt; — tracks 7,197+ pump.fun deployers in real time. Each deployer is scored by graduation rate (how often their tokens bond to Raydium). Deployers are classified as Elite, Good, or Rising. The feed shows live deployments from tracked deployers, filterable by tier.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;KOL Tracker&lt;/strong&gt; — monitors 462 top Solana wallets, showing what they actually buy and sell on-chain. Includes a PnL leaderboard, top buys feed, hot tokens (tokens with multiple KOL buyers), and a compare tool.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The connection&lt;/strong&gt; — Deployer Hunter and KOL Tracker now feed each other. When a tracked deployer launches a token and KOLs start buying, you see it on both feeds instantly. Every KOL profile has a Deployer Score showing what % of their buys come from tracked deployers.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;TikTok Trend Detector&lt;/strong&gt; — scans viral TikTok videos that could become the next memecoin. Updates every few minutes, scored by the community.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Token Launcher&lt;/strong&gt; — launch tokens on pump.fun with MEV protection, multi-wallet distribution, and automated sell strategies.&lt;/p&gt;

&lt;h3&gt;
  
  
  Content
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;120+ blog guides&lt;/strong&gt; — trading bot comparisons, DeFi explainers, bridge breakdowns, step-by-step tutorials&lt;/li&gt;
&lt;li&gt;Glossary pages for Solana-specific terms&lt;/li&gt;
&lt;li&gt;Bot fee calculator and RPC comparison tools&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Community features
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Review system with replies&lt;/li&gt;
&lt;li&gt;Private messaging between users&lt;/li&gt;
&lt;li&gt;Achievement badges and reputation points&lt;/li&gt;
&lt;li&gt;Weekly leaderboard with SOL rewards&lt;/li&gt;
&lt;li&gt;Tool watchlists and follow system&lt;/li&gt;
&lt;li&gt;Tool claim system for project teams&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  The numbers
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Metric&lt;/th&gt;
&lt;th&gt;Value&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Tools listed&lt;/td&gt;
&lt;td&gt;815&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Categories&lt;/td&gt;
&lt;td&gt;26&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Blog guides&lt;/td&gt;
&lt;td&gt;120+&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Deployers tracked&lt;/td&gt;
&lt;td&gt;7,197&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;KOL wallets monitored&lt;/td&gt;
&lt;td&gt;462&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Pages on site&lt;/td&gt;
&lt;td&gt;~1,580&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Google indexed pages&lt;/td&gt;
&lt;td&gt;256&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Linking websites&lt;/td&gt;
&lt;td&gt;15&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Registered users&lt;/td&gt;
&lt;td&gt;10&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Monthly cost&lt;/td&gt;
&lt;td&gt;~€140&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Revenue&lt;/td&gt;
&lt;td&gt;$0&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h2&gt;
  
  
  Tech stack
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Component&lt;/th&gt;
&lt;th&gt;Tool&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Frontend&lt;/td&gt;
&lt;td&gt;Next.js (App Router, SSG)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Database&lt;/td&gt;
&lt;td&gt;Supabase / PostgreSQL&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Real-time data&lt;/td&gt;
&lt;td&gt;gRPC stream via Kaldera RPC&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Hosting&lt;/td&gt;
&lt;td&gt;Hetzner VPS (CX23)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Email&lt;/td&gt;
&lt;td&gt;Resend&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;DNS&lt;/td&gt;
&lt;td&gt;Cloudflare&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Analytics&lt;/td&gt;
&lt;td&gt;Self-hosted Umami&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Coding partner&lt;/td&gt;
&lt;td&gt;Claude&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;I migrated from Plausible Cloud (€16/month) to self-hosted Umami to save costs. All traffic stats are public at &lt;a href="https://madeonsol.com/stats" rel="noopener noreferrer"&gt;madeonsol.com/stats&lt;/a&gt;.&lt;/p&gt;




&lt;h2&gt;
  
  
  SEO — the real challenge
&lt;/h2&gt;

&lt;p&gt;This is where I've learned the most.&lt;/p&gt;

&lt;p&gt;MadeOnSol has ~1,580 pages. Google has indexed 256 of them. The other 1,374 are stuck in "Discovered — currently not indexed." Google knows the URLs exist but won't spend crawl budget on a low-authority domain.&lt;/p&gt;

&lt;h3&gt;
  
  
  What didn't work
&lt;/h3&gt;

&lt;p&gt;Buying cheap backlinks. I evaluated Fiverr packages, Reddit offers, and someone who offered 100 .edu profile backlinks from random universities. All turned out to be spam farms with zero relevance to Solana. One link from a relevant crypto site is worth more than 1,000 of those.&lt;/p&gt;

&lt;p&gt;Writing more content without backlinks. I spent weeks adding tools and publishing guides thinking traffic would follow. It didn't. Without external links, Google won't crawl deep enough to find most of your pages.&lt;/p&gt;

&lt;h3&gt;
  
  
  What is working
&lt;/h3&gt;

&lt;p&gt;Submitting to real, relevant directories and platforms:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;DappRadar&lt;/strong&gt; — submitted as a Web3 project (under review)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;DEV.to&lt;/strong&gt; — technical deep dive on the Deployer Hunter architecture&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Medium&lt;/strong&gt; — building in public story&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Indie Hackers&lt;/strong&gt; — weekly progress updates&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Launching Next&lt;/strong&gt; — startup listing&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Product Hunt, AlternativeTo, BetaList&lt;/strong&gt; — in the pipeline&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Since starting this backlink sprint, indexed pages jumped from 205 to 256 in two days. First visitors are coming in from Google, DuckDuckGo, Bing, Reddit, and X.&lt;/p&gt;

&lt;h3&gt;
  
  
  Key SEO lesson
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Backlinks first, content second.&lt;/strong&gt; If I started over, I'd spend the first two weeks building 20 legitimate backlinks before writing a single blog post. Domain authority unlocks everything — without it, Google ignores your content no matter how good it is.&lt;/p&gt;




&lt;h2&gt;
  
  
  What's working beyond SEO
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Organic discovery.&lt;/strong&gt; People are finding Deployer Hunter and KOL Tracker without me promoting them. Wallet scans happen daily from users I've never interacted with.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Unprompted tool claims.&lt;/strong&gt; GetBlock (24K X followers) found their listing and claimed it organically. No outreach from my side.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The intelligence tools are the moat.&lt;/strong&gt; The directory alone is useful but not unique. Deployer Hunter tracking 7,197 deployers with historical data, connected to KOL Tracker monitoring 462 wallets — that dataset grows daily and gets harder to replicate.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Public stats build trust.&lt;/strong&gt; Having all traffic data public at &lt;a href="https://madeonsol.com/stats" rel="noopener noreferrer"&gt;madeonsol.com/stats&lt;/a&gt; shows I have nothing to hide. When the time comes to approach sponsors, this is proof.&lt;/p&gt;




&lt;h2&gt;
  
  
  What I'd do differently
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Start backlink work from day one&lt;/strong&gt; — not after building 1,580 pages&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Launch with fewer features&lt;/strong&gt; — I built five intelligence tools, then removed three. Should have started with two&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Ignore anyone selling cheap backlinks&lt;/strong&gt; — if it sounds too good to be true, it's spam&lt;/li&gt;
&lt;/ol&gt;




&lt;h2&gt;
  
  
  What's next
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Reaching 20+ dofollow linking domains (currently at 2)&lt;/li&gt;
&lt;li&gt;Product Hunt launch&lt;/li&gt;
&lt;li&gt;Solana ecosystem GitHub PR (solana-labs/ecosystem repo)&lt;/li&gt;
&lt;li&gt;Outreach to projects listed on MadeOnSol for backlinks&lt;/li&gt;
&lt;li&gt;Monthly data reports using Deployer Hunter + KOL Tracker data&lt;/li&gt;
&lt;li&gt;First revenue from affiliate links on trading bot pages&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Try it
&lt;/h2&gt;

&lt;p&gt;Everything is free, no registration required to browse:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;🔗 &lt;a href="https://madeonsol.com" rel="noopener noreferrer"&gt;MadeOnSol&lt;/a&gt; — the directory&lt;/li&gt;
&lt;li&gt;🔗 &lt;a href="https://madeonsol.com/deployer-hunter" rel="noopener noreferrer"&gt;Deployer Hunter&lt;/a&gt; — track pump.fun deployers&lt;/li&gt;
&lt;li&gt;🔗 &lt;a href="https://madeonsol.com/kol-tracker" rel="noopener noreferrer"&gt;KOL Tracker&lt;/a&gt; — track what smart money buys&lt;/li&gt;
&lt;li&gt;🔗 &lt;a href="https://madeonsol.com/tiktok-trends" rel="noopener noreferrer"&gt;TikTok Trends&lt;/a&gt; — viral videos → memecoins&lt;/li&gt;
&lt;li&gt;🔗 &lt;a href="https://madeonsol.com/stats" rel="noopener noreferrer"&gt;Live Stats&lt;/a&gt; — public traffic dashboard&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Happy to answer any questions about the build, the Solana ecosystem, or the SEO grind. Building in public, one day at a time.&lt;/p&gt;

</description>
    </item>
    <item>
      <title>How I built a real-time pump.fun deployer tracker and connected it to KOL wallet monitoring on Solana</title>
      <dc:creator>Lambo Poewert</dc:creator>
      <pubDate>Sun, 22 Mar 2026 10:00:09 +0000</pubDate>
      <link>https://dev.to/lambopoewert/how-i-built-a-real-time-pumpfun-deployer-tracker-and-connected-it-to-kol-wallet-monitoring-on-1lge</link>
      <guid>https://dev.to/lambopoewert/how-i-built-a-real-time-pumpfun-deployer-tracker-and-connected-it-to-kol-wallet-monitoring-on-1lge</guid>
      <description>&lt;h1&gt;
  
  
  How I built a real-time pump.fun deployer tracker and connected it to KOL wallet monitoring on Solana
&lt;/h1&gt;

&lt;p&gt;I run &lt;a href="https://madeonsol.com" rel="noopener noreferrer"&gt;MadeOnSol&lt;/a&gt;, a Solana ecosystem directory. Over the past few months I built two intelligence tools on top of it — &lt;strong&gt;Deployer Hunter&lt;/strong&gt; and &lt;strong&gt;KOL Tracker&lt;/strong&gt; — and recently connected them so they feed each other signals in real time.&lt;/p&gt;

&lt;p&gt;This post covers the architecture, the data pipeline, and what I learned building it as a solo developer.&lt;/p&gt;




&lt;h2&gt;
  
  
  The problem
&lt;/h2&gt;

&lt;p&gt;On Solana, thousands of tokens launch daily on pump.fun. Most are worthless. A few graduate to real liquidity.&lt;/p&gt;

&lt;p&gt;The question every trader asks is:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"Did a good deployer just launch something, and are smart wallets buying it?"&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Before I built this, answering that question meant manually checking deployer wallets, cross-referencing with KOL wallets, and hoping you caught the signal in time. I wanted to automate the entire pipeline.&lt;/p&gt;




&lt;h2&gt;
  
  
  Deployer Hunter — tracking 7,197+ deployers
&lt;/h2&gt;

&lt;p&gt;The core idea is simple: track every pump.fun deployer, score them by their graduation rate (how many of their tokens actually bond to Raydium), and surface the ones with real track records.&lt;/p&gt;

&lt;h3&gt;
  
  
  Data ingestion
&lt;/h3&gt;

&lt;p&gt;I use a &lt;strong&gt;gRPC stream&lt;/strong&gt; connected to Solana via a Kaldera RPC endpoint. This gives me real-time transaction data as it hits the chain — no polling, no delays. Every time a token is created on pump.fun, I capture the deployer address, token mint, and timestamp.&lt;/p&gt;

&lt;h3&gt;
  
  
  Scoring logic
&lt;/h3&gt;

&lt;p&gt;Each deployer gets two scores:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Recent rate&lt;/strong&gt; — graduation % of their last N tokens&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Lifetime rate&lt;/strong&gt; — graduation % across all their tokens&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Deployers are classified into tiers:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Tier&lt;/th&gt;
&lt;th&gt;Meaning&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;🏆 Elite&lt;/td&gt;
&lt;td&gt;Consistently high graduation rate across many deploys&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;✅ Good&lt;/td&gt;
&lt;td&gt;Solid recent track record&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;🌟 Rising&lt;/td&gt;
&lt;td&gt;New but showing promise&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  Storage
&lt;/h3&gt;

&lt;p&gt;Everything goes into &lt;strong&gt;Supabase (PostgreSQL)&lt;/strong&gt;. Deployer profiles, token history, graduation events, and peak market caps are all stored and queryable. Each deployer gets their own profile page with full history.&lt;/p&gt;

&lt;h3&gt;
  
  
  The feed
&lt;/h3&gt;

&lt;p&gt;Deployer Hunter shows a live feed of new deployments from tracked deployers, filterable by tier. When an Elite deployer launches something, it shows up immediately.&lt;/p&gt;

&lt;p&gt;Currently tracking &lt;strong&gt;6,366 deployers&lt;/strong&gt; with &lt;strong&gt;194 bonds today&lt;/strong&gt; at a &lt;strong&gt;6.3% graduation rate&lt;/strong&gt;.&lt;/p&gt;




&lt;h2&gt;
  
  
  KOL Tracker — 462 wallets monitored
&lt;/h2&gt;

&lt;p&gt;KOL Tracker watches what the biggest Solana wallets are actually buying on-chain. Not what they tweet — what they trade.&lt;/p&gt;

&lt;h3&gt;
  
  
  Wallet monitoring
&lt;/h3&gt;

&lt;p&gt;I maintain a curated list of &lt;strong&gt;462 KOL wallets&lt;/strong&gt;. The system monitors their transactions in real time using the same gRPC infrastructure. When a tracked wallet buys or sells a token, it appears in the live feed within seconds.&lt;/p&gt;

&lt;h3&gt;
  
  
  Features
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Live feed&lt;/strong&gt; of all KOL buys and sells&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;PnL leaderboard&lt;/strong&gt; — who's actually profitable&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Top buys&lt;/strong&gt; — which tokens are getting the most KOL attention&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Hot tokens&lt;/strong&gt; — tokens with multiple KOL buyers&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Compare tool&lt;/strong&gt; to put KOLs side by side&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Users can suggest new KOLs to track. I verify the wallet before adding it.&lt;/p&gt;




&lt;h2&gt;
  
  
  The connection — where it gets interesting
&lt;/h2&gt;

&lt;p&gt;The real power came from connecting these two systems. Now:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;When a tracked deployer launches a token → &lt;strong&gt;Deployer Hunter shows it&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;When KOLs start buying that same token → &lt;strong&gt;KOL Tracker shows it&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Both feeds cross-reference each other — you see KOL interest on a deployer's launch, and deployer quality on a KOL's buy&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Every KOL profile now has a &lt;strong&gt;Deployer Score&lt;/strong&gt; — what percentage of their buys come from tokens launched by tracked deployers. This tells you whether a KOL is buying from proven deployers or chasing random launches.&lt;/p&gt;

&lt;p&gt;The signal chain becomes:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Good deployer launches → KOLs buy → you see it on both feeds simultaneously.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  Tech stack
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Component&lt;/th&gt;
&lt;th&gt;Tool&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Frontend&lt;/td&gt;
&lt;td&gt;Next.js (App Router, SSG where possible)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Database&lt;/td&gt;
&lt;td&gt;Supabase / PostgreSQL&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Real-time data&lt;/td&gt;
&lt;td&gt;gRPC stream via Kaldera RPC&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Hosting&lt;/td&gt;
&lt;td&gt;Hetzner VPS (CX23)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Email&lt;/td&gt;
&lt;td&gt;Resend&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;DNS&lt;/td&gt;
&lt;td&gt;Cloudflare&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Total infrastructure cost: ~€140/month.&lt;/strong&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  What I learned
&lt;/h2&gt;

&lt;h3&gt;
  
  
  gRPC &amp;gt; polling
&lt;/h3&gt;

&lt;p&gt;The difference between polling an RPC endpoint every few seconds and having a real-time gRPC stream is massive. You catch deploys and buys within the same slot they happen.&lt;/p&gt;

&lt;h3&gt;
  
  
  Scoring is harder than detection
&lt;/h3&gt;

&lt;p&gt;Detecting a new deploy is easy. Deciding whether a deployer is "good" requires careful threshold tuning.&lt;/p&gt;

&lt;p&gt;Early on, tokens were showing as "bonded" with peak market caps of $3-5K — impossible since bonding requires ~$69K. Turned out to be false positives in the bonding detection logic. Still refining this.&lt;/p&gt;

&lt;h3&gt;
  
  
  Kill features that dilute focus
&lt;/h3&gt;

&lt;p&gt;I previously had five intelligence tools. I removed three (Wallet X-Ray, CTO Radar, Token Scanner) to focus on Deployer Hunter + KOL Tracker. The product is stronger for it.&lt;/p&gt;

&lt;h3&gt;
  
  
  The data is the moat
&lt;/h3&gt;

&lt;p&gt;Anybody can build a UI. The value is in &lt;strong&gt;7,197+ deployer profiles&lt;/strong&gt; with historical data and &lt;strong&gt;462 tracked KOL wallets&lt;/strong&gt; with real PnL. That dataset grows every day and gets harder to replicate.&lt;/p&gt;




&lt;h2&gt;
  
  
  What's next
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Publishing monthly data reports using this pipeline (graduation rates, KOL accuracy, deployer trends)&lt;/li&gt;
&lt;li&gt;Improving bonding detection to eliminate false positives&lt;/li&gt;
&lt;li&gt;Building a TikTok-to-Token analyzer that correlates viral TikTok content with token launches&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Both tools are free and require no registration to browse:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;🔗 &lt;a href="https://madeonsol.com/deployer-hunter" rel="noopener noreferrer"&gt;Deployer Hunter&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;🔗 &lt;a href="https://madeonsol.com/kol-tracker" rel="noopener noreferrer"&gt;KOL Tracker&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Happy to answer any questions about the architecture or the Solana data pipeline.&lt;/p&gt;

</description>
    </item>
  </channel>
</rss>
