<?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: Manpreet Brar</title>
    <description>The latest articles on DEV Community by Manpreet Brar (@manpreet_brar_264e408885a).</description>
    <link>https://dev.to/manpreet_brar_264e408885a</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.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F4004749%2F5238c4f6-2689-4003-9946-b557445c438c.png</url>
      <title>DEV Community: Manpreet Brar</title>
      <link>https://dev.to/manpreet_brar_264e408885a</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/manpreet_brar_264e408885a"/>
    <language>en</language>
    <item>
      <title>I built in-app Polymarket copy trading with MetaMask EIP-712 — no private keys, no redirects, one click</title>
      <dc:creator>Manpreet Brar</dc:creator>
      <pubDate>Sun, 02 Aug 2026 21:40:08 +0000</pubDate>
      <link>https://dev.to/manpreet_brar_264e408885a/i-built-in-app-polymarket-copy-trading-with-metamask-eip-712-no-private-keys-no-redirects-one-c21</link>
      <guid>https://dev.to/manpreet_brar_264e408885a/i-built-in-app-polymarket-copy-trading-with-metamask-eip-712-no-private-keys-no-redirects-one-c21</guid>
      <description>&lt;p&gt;I just shipped one-click copy trading on WhaleTrack — here's &lt;br&gt;
the architecture that makes it work without ever touching a &lt;br&gt;
private key.&lt;/p&gt;
&lt;h2&gt;
  
  
  The Problem
&lt;/h2&gt;

&lt;p&gt;Polymarket's CLOB API needs two auth layers:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;EIP-712 order signing&lt;/li&gt;
&lt;li&gt;HMAC-SHA256 HTTP auth&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Most tutorials store private keys server-side. That's a &lt;br&gt;
security nightmare.&lt;/p&gt;
&lt;h2&gt;
  
  
  The Solution — Split Architecture
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Backend builds the unsigned order + fetches live price&lt;/li&gt;
&lt;li&gt;MetaMask signs EIP-712 typed data client-side&lt;/li&gt;
&lt;li&gt;Backend attaches HMAC headers and proxies to CLOB&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The user's private key never leaves MetaMask. Ever.&lt;/p&gt;
&lt;h2&gt;
  
  
  The Flow
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Step 1 — Fetch unsigned order params&lt;/strong&gt;&lt;br&gt;
\&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;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="s2"&gt;`/api/trade-params?slug=&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;slug&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;
  &amp;amp;outcome=&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;outcome&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;&amp;amp;amount=&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;amount&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;&amp;amp;address=&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;address&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="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;domain&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;types&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;order&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;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="err"&gt;\&lt;/span&gt;&lt;span class="s2"&gt;```



**Step 2 — MetaMask EIP-712 sign**
\

```&lt;/span&gt;&lt;span class="nx"&gt;js&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="k"&gt;await&lt;/span&gt; &lt;span class="nb"&gt;window&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ethereum&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;request&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="s1"&gt;eth_signTypedData_v4&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;params&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;address&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;domain&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;types&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
    &lt;span class="na"&gt;primaryType&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Order&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;message&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;order&lt;/span&gt; &lt;span class="p"&gt;})]&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="err"&gt;\&lt;/span&gt;&lt;span class="s2"&gt;```



**Step 3 — Backend HMAC proxy**
\

```&lt;/span&gt;&lt;span class="nx"&gt;js&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;message&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;timestamp&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;POST&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/order&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;body&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;secret&lt;/span&gt;  &lt;span class="o"&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;apiSecret&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;base64&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;sig&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="s1"&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;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;message&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="s1"&gt;base64&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="err"&gt;\&lt;/span&gt;&lt;span class="s2"&gt;```



## Result

See whale bet → click Copy Bet → sign in MetaMask → 
order on Polymarket. Under 60 seconds.

Connect via MetaMask wallet or Polymarket API key.

👉 whaletrack.app — free, no signup

Would love feedback from devs on the architecture. 
What would you do differently?
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

</description>
      <category>webdev</category>
      <category>javascript</category>
      <category>blockchain</category>
      <category>buildinpublic</category>
    </item>
    <item>
      <title>How I built an automated YouTube video pipeline for my SaaS using Python, edge-tts, and moviepy</title>
      <dc:creator>Manpreet Brar</dc:creator>
      <pubDate>Sun, 26 Jul 2026 22:14:20 +0000</pubDate>
      <link>https://dev.to/manpreet_brar_264e408885a/how-i-built-an-automated-youtube-video-pipeline-for-my-saas-using-python-edge-tts-and-moviepy-411j</link>
      <guid>https://dev.to/manpreet_brar_264e408885a/how-i-built-an-automated-youtube-video-pipeline-for-my-saas-using-python-edge-tts-and-moviepy-411j</guid>
      <description>&lt;p&gt;How I built an automated YouTube video pipeline for my SaaS using Python, edge-tts, and moviepy&lt;/p&gt;

&lt;p&gt;I run a niche tool called WhaleTrack (whaletrack.app) — it tracks big bets on Polymarket in real time.&lt;/p&gt;

&lt;p&gt;To grow it I decided to start a faceless YouTube channel. No camera, no editing software, no designer. Just code.&lt;/p&gt;

&lt;p&gt;Here's the full pipeline I built in one day.&lt;/p&gt;




&lt;h2&gt;
  
  
  The stack
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;edge-tts&lt;/code&gt; — Microsoft's neural TTS (Andrew voice, free, no API key)&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;moviepy 1.0.3&lt;/code&gt; — video assembly&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;Pillow&lt;/code&gt; — image processing&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;Puppeteer&lt;/code&gt; — screenshot the live website&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;ffmpeg&lt;/code&gt; — underlying encoder&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Step 1: Screenshot the real website
&lt;/h2&gt;

&lt;p&gt;Instead of fake mockups I screenshot the actual live site using Puppeteer:&lt;/p&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;
js
const puppeteer = require('puppeteer');
const browser = await puppeteer.launch();
const page = await browser.newPage();
await page.setViewport({ width: 1920, height: 1080 });
await page.goto('https://whaletrack.app');
await page.screenshot({ path: 'screenshots/home.png' });

Gotcha: if your package.json has "type": "module", Puppeteer's require() breaks. Name the file .cjs not .js.

Step 2: AI voiceover

import edge_tts
comm = edge_tts.Communicate(script, voice='en-US-AndrewNeural', rate='+8%')
await comm.save('audio.mp3')

Completely free. Sounds genuinely good. Andrew Neural is the best voice I found for this style of content.

Step 3: Ken Burns effect
Static screenshots are boring. This adds a slow zoom + pan to make it feel like real video:

def apply_ken_burns(img, progress, zoom_from, zoom_to, pan_from, pan_to):
    zoom = zoom_from + (zoom_to - zoom_from) * progress
    iw, ih = img.size
    crop_w, crop_h = int(iw / zoom), int(ih / zoom)
    ox = int(pan_from[0] + (pan_to[0] - pan_from[0]) * progress) * (iw - crop_w)
    oy = int(pan_from[1] + (pan_to[1] - pan_from[1]) * progress) * (ih - crop_h)
    return img.crop((ox, oy, ox + crop_w, oy + crop_h)).resize((1920, 1080), Image.LANCZOS)

Step 4: Assemble with moviepy

from moviepy.editor import VideoClip, AudioFileClip

def make_frame(t):
    progress = t / duration
    frame = apply_ken_burns(bg_image, progress, 1.0, 1.12, (0,0), (0.03,0.02))
    return np.array(frame)

clip = VideoClip(make_frame, duration=duration)
clip = clip.set_audio(AudioFileClip('audio.mp3'))

Result

A 2.6 minute 1080p YouTube video, fully automated, from a Python script.

Total cost: $0. Total time to build the pipeline: ~1 day.

The video: https://youtu.be/iwGwUpbhABA
The tool: https://whaletrack.app

Happy to share the full script if anyone wants it. Building in public — follow along if you're into niche tools + automation.

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

</description>
      <category>python</category>
      <category>buildinpublic</category>
      <category>webdev</category>
      <category>productivity</category>
    </item>
    <item>
      <title>How I Added a Premium Telegram Alert Bot to My Polymarket Whale Tracker — And Why Speed Is the Product</title>
      <dc:creator>Manpreet Brar</dc:creator>
      <pubDate>Sat, 18 Jul 2026 14:17:32 +0000</pubDate>
      <link>https://dev.to/manpreet_brar_264e408885a/how-i-added-a-premium-telegram-alert-bot-to-my-polymarket-whale-tracker-and-why-speed-is-the-2nha</link>
      <guid>https://dev.to/manpreet_brar_264e408885a/how-i-added-a-premium-telegram-alert-bot-to-my-polymarket-whale-tracker-and-why-speed-is-the-2nha</guid>
      <description>&lt;p&gt;I built WhaleTrack a few weeks ago — a live dashboard that tracks the biggest wallets on Polymarket. Got some good traction, a few hundred users, and a free Twitter bot posting whale alerts automatically.&lt;/p&gt;

&lt;p&gt;Then someone asked me: "Why would I pay for this when you're posting the same data for free on Twitter?"&lt;/p&gt;

&lt;p&gt;Fair question. It made me rethink what the product actually was.&lt;/p&gt;

&lt;p&gt;The Problem With Twitter Alerts&lt;br&gt;
The Twitter bot works fine. It detects a whale trade and posts within the next 5-minute polling cycle. But here's the thing — by the time it posts, the window is already closing.&lt;/p&gt;

&lt;p&gt;Polymarket is liquid. If a whale drops $50K on a market and moves the price, people who see the tweet 20 minutes later are buying into an already-shifted market.&lt;/p&gt;

&lt;p&gt;Speed is the product. Not the data.&lt;/p&gt;

&lt;p&gt;Free users get the data. Premium users get it first.&lt;/p&gt;

&lt;p&gt;The Architecture&lt;br&gt;
Zero npm dependencies — same as the first article. Pure Node.js with the built-in https and crypto modules.&lt;/p&gt;

&lt;p&gt;The bot uses long polling instead of webhooks:&lt;/p&gt;

&lt;p&gt;async function poll() {&lt;br&gt;
  while (true) {&lt;br&gt;
    try {&lt;br&gt;
      const data = await fetchJson(&lt;br&gt;
        &lt;code&gt;https://api.telegram.org/bot${BOT_TOKEN}/getUpdates?offset=${offset}&amp;amp;timeout=30&lt;/code&gt;&lt;br&gt;
      );&lt;br&gt;
      for (const u of (data.result || [])) {&lt;br&gt;
        offset = u.update_id + 1;&lt;br&gt;
        const msg = u.message;&lt;br&gt;
        if (msg?.text?.startsWith('/')) {&lt;br&gt;
          handleCommand(msg.chat.id, msg.text);&lt;br&gt;
        }&lt;br&gt;
      }&lt;br&gt;
    } catch (e) {&lt;br&gt;
      await new Promise(r =&amp;gt; setTimeout(r, 5000));&lt;br&gt;
    }&lt;br&gt;
  }&lt;br&gt;
}&lt;br&gt;
Long polling holds the connection open for 30 seconds waiting for updates, then immediately reconnects. No webhooks needed, no server config, works fine from a Mac running 24/7.&lt;/p&gt;

&lt;p&gt;Two Alert Tiers&lt;br&gt;
Every whale trade fires two alerts — premium first, free after.&lt;/p&gt;

&lt;p&gt;Free alert:&lt;/p&gt;

&lt;p&gt;🐋 Whale Alert!&lt;br&gt;
somalianKing just bought&lt;br&gt;
🟢 Yes $47K @ 62¢&lt;br&gt;
📊 Will Argentina win the World Cup?&lt;br&gt;
🔓 Get win rate + instant alerts → upgrade to Premium&lt;br&gt;
🌐 whaletrack.app&lt;br&gt;
Premium alert (fires first):&lt;/p&gt;

&lt;p&gt;⚡ PREMIUM Whale Alert&lt;br&gt;
somalianKing just bet $47,000&lt;br&gt;
🟢 Yes @ 62¢&lt;br&gt;
📊 Will Argentina win the World Cup?&lt;br&gt;
🐋 This whale's track record:&lt;br&gt;
• Rank: #3 on Polymarket&lt;br&gt;
• Total P&amp;amp;L: +$284K&lt;br&gt;
• Win rate: 71% (34 resolved trades)&lt;br&gt;
🎯 Copy this bet on Polymarket →&lt;br&gt;
⏱ You're seeing this 10+ min before Twitter&lt;br&gt;
The premium alert has three things free doesn't: win rate, P&amp;amp;L context, and a direct affiliate link to copy the bet.&lt;/p&gt;

&lt;p&gt;The Win Rate Calculation&lt;br&gt;
This was the interesting part. Polymarket doesn't give you a clean "won/lost" field on activity records — you have to infer it from sell prices.&lt;/p&gt;

&lt;p&gt;The logic:&lt;/p&gt;

&lt;p&gt;Fetch last 50 trades for a wallet&lt;br&gt;
Filter for SELL transactions only&lt;br&gt;
Sold at ≥ 0.85 = win (exited near $1 resolution)&lt;br&gt;
Sold at ≤ 0.15 = loss (exited near $0 resolution)&lt;br&gt;
Everything in between = position still live or unclear&lt;br&gt;
async function fetchWinRate(address) {&lt;br&gt;
  const activity = await fetchActivity(address, 50);&lt;br&gt;
  const sells = activity.filter(t =&amp;gt; t.side === 'SELL' &amp;amp;&amp;amp; parseFloat(t.price || 0) &amp;gt; 0);&lt;/p&gt;

&lt;p&gt;let wins = 0, losses = 0;&lt;br&gt;
  for (const t of sells) {&lt;br&gt;
    const price = parseFloat(t.price || 0);&lt;br&gt;
    if (price &amp;gt;= 0.85)      wins++;&lt;br&gt;
    else if (price &amp;lt;= 0.15) losses++;&lt;br&gt;
  }&lt;/p&gt;

&lt;p&gt;const total = wins + losses;&lt;br&gt;
  return total &amp;gt; 0 ? Math.round((wins / total) * 100) : null;&lt;br&gt;
}&lt;br&gt;
I cache the result for 30 minutes per wallet since it's an expensive-ish call (50 activity records per whale).&lt;/p&gt;

&lt;p&gt;Premium Subscriber Management&lt;br&gt;
Kept it dead simple — a JSON file on disk.&lt;/p&gt;

&lt;p&gt;let premiumSubs = loadJson(PREMIUM_SUBS_FILE, []); // [chatId, chatId, ...]&lt;br&gt;
When someone pays (manually verified for now), I run /addpremium  in my own chat with the bot. It adds them to the array, saves the file, and sends them a welcome message.&lt;/p&gt;

&lt;p&gt;No Stripe, no database, no webhook. Works fine at this scale.&lt;/p&gt;

&lt;p&gt;The Speed Advantage in Practice&lt;br&gt;
The polling cycle runs every 5 minutes. Premium fires at the top of the cycle. Free fires at the bottom — after premium has already been delivered.&lt;/p&gt;

&lt;p&gt;In practice this means premium users see alerts 5–10 minutes before the Twitter bot posts, and 10–20 minutes before anyone on Twitter actually sees it and acts on it.&lt;/p&gt;

&lt;p&gt;On a fast-moving market, that gap matters.&lt;/p&gt;

&lt;p&gt;What I Learned&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;Speed is a real value prop. Not "more features" — just faster. Simple to understand, easy to sell.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Long polling is underrated. Webhooks need a server with a public URL and SSL. Long polling works from anywhere. For a side project, that simplicity is worth it.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Keep the free tier genuinely useful. The free alert is still real value. Premium isn't a paywall on the core product — it's an upgrade on timing and context. That distinction matters for trust.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Manual premium management is fine at the start. Don't build Stripe until you have 10 paying customers. A JSON file is enough to validate demand.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;What's Next&lt;br&gt;
Proper payment flow (Stripe or Gumroad)&lt;br&gt;
Filter premium alerts by whale win rate threshold (only fire if whale is 60%+ win rate)&lt;br&gt;
Web dashboard showing premium-only whale analytics&lt;br&gt;
Bot is live at @WhaleTrackBot on Telegram. Free tier available, premium is $9/month.&lt;/p&gt;

&lt;p&gt;Live site: whaletrack.app&lt;/p&gt;

&lt;p&gt;Happy to answer questions about the Telegram API, win rate logic, or the alert architecture.&lt;/p&gt;

</description>
      <category>buildinpublic</category>
      <category>javascript</category>
      <category>webdev</category>
      <category>startup</category>
    </item>
    <item>
      <title>How I built a real-time whale tracker for Polymarket using their public API</title>
      <dc:creator>Manpreet Brar</dc:creator>
      <pubDate>Mon, 13 Jul 2026 01:45:00 +0000</pubDate>
      <link>https://dev.to/manpreet_brar_264e408885a/how-i-built-a-real-time-whale-tracker-for-polymarket-using-their-public-api-3ja6</link>
      <guid>https://dev.to/manpreet_brar_264e408885a/how-i-built-a-real-time-whale-tracker-for-polymarket-using-their-public-api-3ja6</guid>
      <description>&lt;p&gt;Polymarket has a public data API that almost nobody talks about. I used it to build WhaleTrack — a live dashboard that tracks what the biggest traders on the platform are betting on.&lt;/p&gt;

&lt;p&gt;The stack:&lt;/p&gt;

&lt;p&gt;Vanilla JS frontend (no framework — just fast)&lt;br&gt;
Vercel serverless functions to proxy all API calls (avoids CORS, keeps keys server-side)&lt;br&gt;
Polymarket's data-api.polymarket.com for positions, leaderboard, and activity&lt;br&gt;
The interesting parts:&lt;/p&gt;

&lt;p&gt;The win rate calculation was tricky. Polymarket doesn't give you a clean "won/lost" field. You have to infer it:&lt;/p&gt;

&lt;p&gt;redeemable === true = won (market resolved in their favour)&lt;br&gt;
currentValue === 0 &amp;amp;&amp;amp; redeemable === false = lost&lt;br&gt;
Everything else = still open&lt;br&gt;
Also learned the hard way that slug (market-level) and eventSlug (event-level) are different things on Polymarket. Market slugs 404 on polymarket.com/event/. Took me longer than I'd like to admit to figure that out.&lt;/p&gt;

&lt;p&gt;Live at whaletrack.app — open to questions about the API or architecture.&lt;/p&gt;

</description>
      <category>polymarket</category>
      <category>javascript</category>
      <category>webdev</category>
      <category>buildinpublic</category>
    </item>
    <item>
      <title>How I built a real-time whale tracker for Polymarket using Node.js and a CLI</title>
      <dc:creator>Manpreet Brar</dc:creator>
      <pubDate>Sun, 05 Jul 2026 03:05:29 +0000</pubDate>
      <link>https://dev.to/manpreet_brar_264e408885a/how-i-built-a-real-time-whale-tracker-for-polymarket-using-nodejs-and-a-cli-862</link>
      <guid>https://dev.to/manpreet_brar_264e408885a/how-i-built-a-real-time-whale-tracker-for-polymarket-using-nodejs-and-a-cli-862</guid>
      <description>&lt;p&gt;The 2026 World Cup has $3.89 billion bet on it across Polymarket. That's not retail money — that's whales.&lt;/p&gt;

&lt;p&gt;I built WhaleTrack to track exactly what those big wallets are doing. Here's the stack:&lt;/p&gt;

&lt;p&gt;Backend: Node.js server fetching live data via Bullpen CLI&lt;br&gt;
Frontend: Vanilla JS, real-time updates&lt;br&gt;
Data: Polymarket CLOB API via Bullpen&lt;br&gt;
Analytics: Google Analytics for traffic tracking&lt;br&gt;
The hardest part wasn't the code — it was getting users. Pure SEO and content distribution (Reddit, Twitter, IH).&lt;/p&gt;

&lt;p&gt;The site is live at whaletrack.app — would love feedback from devs on the UX and performance.&lt;/p&gt;

&lt;p&gt;Happy to open source parts of it if there's interest.&lt;/p&gt;

</description>
      <category>buildinpublic</category>
      <category>polymarket</category>
      <category>cryptocurrency</category>
      <category>fintech</category>
    </item>
    <item>
      <title>How I Built a Real-Time Whale Tracker for Polymarket in a Weekend</title>
      <dc:creator>Manpreet Brar</dc:creator>
      <pubDate>Sun, 28 Jun 2026 18:05:09 +0000</pubDate>
      <link>https://dev.to/manpreet_brar_264e408885a/how-i-built-a-real-time-whale-tracker-for-polymarket-in-a-weekend-36gc</link>
      <guid>https://dev.to/manpreet_brar_264e408885a/how-i-built-a-real-time-whale-tracker-for-polymarket-in-a-weekend-36gc</guid>
      <description>&lt;p&gt;Prediction markets just hit $3.6B in volume. I wanted to know what the biggest traders were betting on — in real time. So I built WhaleTrack.&lt;/p&gt;

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

&lt;h2&gt;
  
  
  The Problem
&lt;/h2&gt;

&lt;p&gt;Polymarket has a public leaderboard. But it only shows P&amp;amp;L totals — not what whales are currently betting on, not their recent activity, not their win rate. If you want to follow smart money, you're flying blind.&lt;/p&gt;

&lt;p&gt;I wanted something that answered: what are the top traders doing right now?&lt;/p&gt;

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

&lt;ul&gt;
&lt;li&gt;Vanilla JS frontend (no framework, keeps it fast)&lt;/li&gt;
&lt;li&gt;Vercel serverless function as a backend proxy (avoids CORS issues)&lt;/li&gt;
&lt;li&gt;Polymarket's public data API — no auth required&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Step 1: Finding the Whales
&lt;/h2&gt;

&lt;p&gt;Polymarket exposes a leaderboard endpoint:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://data-api.polymarket.com/v1/leaderboard?limit=20" rel="noopener noreferrer"&gt;https://data-api.polymarket.com/v1/leaderboard?limit=20&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This returns traders ranked by P&amp;amp;L. I pull the top 10, grab their wallet addresses, and that's my whale list.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 2: Fetching Live Activity
&lt;/h2&gt;

&lt;p&gt;For each whale wallet, I hit:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://data-api.polymarket.com/activity?user=%7Baddress%7D&amp;amp;limit=20" rel="noopener noreferrer"&gt;https://data-api.polymarket.com/activity?user={address}&amp;amp;limit=20&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This returns their recent trades — market name, size in USDC, timestamp. Refreshes every 60 seconds.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 3: Calculating Win Rate (the tricky part)
&lt;/h2&gt;

&lt;p&gt;The key is the redeemable flag — redeemable: true means they won, currentValue: 0 + redeemable: false means they lost. Took a few wrong attempts with cashPnl (always negative, not useful).&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 4: The Whale Alert Banner
&lt;/h2&gt;

&lt;p&gt;Every 60 seconds I check for trades over $5,000 placed in the last 10 minutes. When it fires, a green banner slides down with the whale name, market, and amount. Auto-dismisses after 12 seconds. First time I saw it fire live with a $28K bet — genuinely exciting.&lt;/p&gt;

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

&lt;ul&gt;
&lt;li&gt;129+ users in the first few days&lt;/li&gt;
&lt;li&gt;Zero ad spend&lt;/li&gt;
&lt;li&gt;Traffic from Twitter, Reddit, Quora&lt;/li&gt;
&lt;/ul&gt;

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

&lt;ul&gt;
&lt;li&gt;More whale wallets (suggestions welcome)&lt;/li&gt;
&lt;li&gt;Click-through to open the same market on Polymarket directly&lt;/li&gt;
&lt;li&gt;Email/push alerts for big trades&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Check it out: whaletrack.app&lt;/p&gt;

&lt;p&gt;All feedback welcome — especially if you spot a whale I'm missing.&lt;/p&gt;

</description>
      <category>javascript</category>
      <category>webdev</category>
      <category>buildinpublic</category>
      <category>api</category>
    </item>
    <item>
      <title>I built a free whale tracker for Polymarket — here's what I learned</title>
      <dc:creator>Manpreet Brar</dc:creator>
      <pubDate>Sat, 27 Jun 2026 03:16:11 +0000</pubDate>
      <link>https://dev.to/manpreet_brar_264e408885a/i-built-a-free-whale-tracker-for-polymarket-heres-what-i-learned-21bi</link>
      <guid>https://dev.to/manpreet_brar_264e408885a/i-built-a-free-whale-tracker-for-polymarket-heres-what-i-learned-21bi</guid>
      <description>&lt;p&gt;The problem: I kept missing big moves on Polymarket because I had no way to see what the biggest traders were betting on in real time.&lt;/p&gt;

&lt;p&gt;So I built WhaleTrack — a free, no-signup tool that shows you exactly what top Polymarket whales are buying and selling.&lt;/p&gt;

&lt;h2&gt;
  
  
  What it does
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Live whale activity feed&lt;/strong&gt; — see the last 40 trades from top wallets, updated on refresh&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Whale leaderboard&lt;/strong&gt; — P&amp;amp;L, win rate, trade count for the biggest accounts&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;No login, no ads, no fluff&lt;/strong&gt; — just the data&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  How it works
&lt;/h2&gt;

&lt;p&gt;The whole thing is vanilla HTML/CSS/JS deployed on Vercel with two serverless functions:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;code&gt;/api/whales.js&lt;/code&gt;&lt;/strong&gt; — hits the Polymarket leaderboard API, fetches position stats for each whale, calculates win rates from closed positions&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;code&gt;/api/activity.js&lt;/code&gt;&lt;/strong&gt; — pulls recent trades for each whale wallet in parallel, filters out internal combo transactions (no title / zero price), and returns the 40 most recent trades&lt;/p&gt;

&lt;p&gt;The serverless layer solves CORS — Polymarket's data API doesn't allow browser requests, so everything goes server-side.&lt;/p&gt;

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

&lt;ul&gt;
&lt;li&gt;Frontend: Vanilla HTML/CSS/JS (zero dependencies)&lt;/li&gt;
&lt;li&gt;Backend: Vercel serverless functions&lt;/li&gt;
&lt;li&gt;Data: Polymarket public data API&lt;/li&gt;
&lt;li&gt;Deploy: Vercel (free tier)&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Biggest lesson
&lt;/h2&gt;

&lt;p&gt;Filtering bad data is half the work. The raw API returns combo trades and internal transactions that show up as "Unknown Market @ 0¢" — useless noise. Had to figure out which fields to check (title, price &amp;gt; 0) to strip them.&lt;/p&gt;

&lt;p&gt;Also: win rate calculation is tricky when most whales have unrealized profits. Showing "—" instead of 0% is more honest.&lt;/p&gt;

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

&lt;p&gt;&lt;a href="https://whaletrack.app" rel="noopener noreferrer"&gt;WhaleTrack →&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Also launched on Product Hunt today if you want to show some love: &lt;a href="https://www.producthunt.com/posts/whaletrack" rel="noopener noreferrer"&gt;Product Hunt&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Built this in a weekend. Happy to answer questions about the Polymarket API or Vercel serverless setup.&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>javascript</category>
      <category>startup</category>
      <category>productivity</category>
    </item>
  </channel>
</rss>
