<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/">
    <channel>
        <title>ChainHacker</title>
        <link>https://paragraph.com/@chainhacker</link>
        <description>undefined</description>
        <lastBuildDate>Thu, 29 Oct 2026 18:58:32 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <language>en</language>
        <image>
            <title>ChainHacker</title>
            <url>https://img.paragraph.com/cdn-cgi/image/format=auto/https://storage.googleapis.com/papyrus_images/f4023d892a9e61576c209c2f95424153.jpg</url>
            <link>https://paragraph.com/@chainhacker</link>
        </image>
        <copyright>All rights reserved</copyright>
        <item>
            <title><![CDATA[Deploy Your Own BetSwirl Affiliate Mini-App in 30 Minutes]]></title>
            <link>https://paragraph.com/@chainhacker/your-own-mini-app-in-30-minutes</link>
            <guid>hXUF4O3VAvvMsLYeLJfa</guid>
            <pubDate>Sun, 19 Oct 2025 13:45:04 GMT</pubDate>
            <description><![CDATA[You're here because you want BetSwirl games in your Farcaster mini-app. This guide documents the exact path from zero to deployed mini-app, with all the stumbles and fixes included.]]></description>
            <content:encoded><![CDATA[<br><p>You're here because you want BetSwirl casino games in your Farcaster mini-app. Maybe you've seen them elsewhere, maybe you just like the idea of on-chain dice rolling. Either way, this guide documents the exact path from zero to deployed mini-app, with all the stumbles and fixes included.</p><p><strong>What you're building:</strong> A fully functional Farcaster mini-app with integrated BetSwirl casino games (starting with dice, expandable to more).</p><p><strong>Time required:</strong> 30 minutes if everything goes smoothly, 45 if you hit the same TypeScript snags I did.</p><p><strong>Prerequisites:</strong></p><ul><li><p>Node.js 18+ (<code>node --version</code> should return v18.0.0 or higher)</p></li><li><p>A Farcaster account (you'll need this for testing)</p></li><li><p>Basic familiarity with terminal commands</p></li><li><p>Patience for one or two build errors (we'll fix them together)</p></li></ul><hr><h2 id="h-step-1-fork-first-ask-questions-later" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">Step 1: Fork First, Ask Questions Later</h2><p>The fastest path is forking the BetSwirl demo. Yes, you could start from scratch with <code>npx create-onchain --mini</code>, but why make life harder?</p><pre data-type="codeBlock" text="git clone https://github.com/BetSwirl/miniapp-ui-react-demo.git my-app
cd my-app
npm install"><code>git <span class="hljs-built_in">clone</span> https://github.com/BetSwirl/miniapp-ui-react-demo.git my-app
<span class="hljs-built_in">cd</span> my-app
npm install</code></pre><p><strong>What just happened:</strong> You've cloned a pre-configured Farcaster mini-app with BetSwirl components already integrated. The heavy lifting of provider setup, chain configuration, and component integration is done.</p><p><strong>Verify it works:</strong></p><pre data-type="codeBlock" text="npm run dev"><code>npm run dev</code></pre><p>Your terminal should light up with something like:</p><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://img.paragraph.com/cdn-cgi/image/format=auto/https://storage.googleapis.com/papyrus_images/bea9a404a725e992b21b17c8ebe66daf35fc7d52ef9844fae02389d1d6300a14.png" blurdataurl="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAMCAIAAACMdijuAAAACXBIWXMAAA7EAAAOxAGVKw4bAAACYElEQVR4nJ2Sy07bQBSGTXybzMXGNb57YhzHMUNiaAixBUlRIKACEovuu+wrdF2pi676Dl2ENmrKoo1Q20V5kT5F3yCVTelFLAKV/sWZo9F8/5zzcz/m8+/z+XD8du/N+OjDxdH0YjR5P5pMD95NR5PpcHy+fz4Zjs+PP346nV2ezGZnn7+ezi6fXl2dffmWvn528mJw+PJg7/ng+NUofbK57HtGo4ZjRoKYbHSgG3JLxOI1G2g20F1J96qGWzW8qlODbgA0W9JdYHpAd3mgi8j4LYhMuSwkYonIFIApIlMi1m1xiEZKZ0vZ7ChZrmYZoRFuMmW7p+RFrcQJYSmOkuu3/kMctCiKmxINRRqC1ZgTln+popbujFL/uJPVQncF8NDQlr3AY3Gt7Zh1129G9dT316iXQMWu3EyGBzoPdAGUtajz4spdAQIxTSeKWdbtPw7TXlTfqHnrq5T5lCmNNRhEyAkQjRCNcJTAoFF1QrKe4qRVETUe6IsBErGWoCEnrDLYkbtdUI84uMIJGocNXGwixwnDeY7zjAz6OM9hEJP9Ie7vqlmGk9bC3RQAiVjIpoRGyA2RHciqLRELag6yA+iG0KJVJ0ROAN2g6tSA7iGnaJI4QX64GAA1G/thcZs2cMKQTa/H/Xco+fJ40zRFZAig2IQAFkeLA6pBWAs/GuDOFllPCUuL1D7s3nYn3yc8fwB+sOa0O6i/q5QAZbMDg0axWJtKpEinrNqy5ojILH9Q2L8f4IHlK0GM+33c2cLbPZy0oFtMDMcMNxl0Q5zlynaPxIm6s0PaKWlvIJveHfMTinx6vzRpW0kAAAAASUVORK5CYII=" nextheight="368" nextwidth="961" class="image-node embed"><figcaption htmlattributes="[object Object]" class="hide-figcaption"></figcaption></figure><p>Navigate to <code>http://localhost:3000</code>. You should see a dice game. If yes, you're already 40% done. If no, check that port 3000 isn't already occupied.</p><p><strong>Note:</strong> Ignore the <code>@farcaster/frame-sdk is deprecated</code> warning - it's from a dependency, not your code.</p><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://img.paragraph.com/cdn-cgi/image/format=auto/https://storage.googleapis.com/papyrus_images/c0e5b1c17ab4b8cff927c87816fd35cde84873708da9d4511f5f8595d67b2fc1.png" blurdataurl="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABsAAAAgCAIAAABsC5RsAAAACXBIWXMAAA7EAAAOxAGVKw4bAAAFJ0lEQVR4nJWWe0xTVxzHT7L96bJ/ZmZMtsxkxrFliXPLFt2cEYyZYepwqEOZGonoVDbRUbBYHqVFRYVaqpTePrm0txRKab1tbcutFQtUOspDQKtVsEpByqMPKKu9pUvbSTAxbf3km5N7Tk4+J79zT04OGJ+YMlv62zp7UY0Bu21StxrbOnttTyesTxyJx/Z0wtw7ZDIPBPAgGLE/bzN2NkjR6hoOF27gwo3SG7oHTxyDj54lnkcjE1jbXblKvxAKAfwVoSXgrxFIOGHAmxSv2d8WEHfG62ssBHH/QswlQWxdIBCuxe9/6fHORjP3MjD3rz/67Ysw5/NFu37/y/jGEfszCvW8DsNmXC7njNf+uJ9PAB3yCu1tc4tcUSdu4Qqb6yVymRxVa7STU1PxjaOjjoKCAh5fGK10zHrn8IdAWpbS3nUPbW6sZAgolyEGsw5VyBUKhWNsPL7R4527b3e4Z+wqYwfUgnGVbWyJlKfUU9iSS7ym2kaUJVUxpSqaUCbVtPl8vlhGHA+GQqHOwZGPvv/BKMnKLcoE768DH6wDvxwDyZngqx3g0xTw2abk3Wng2xSw4muQ8tvE1HQs40KktTwe3fxr6i3e9oqKfeCLbWDz3vUk6iEGewupEuw8Cj5eC1ashKpzt5w8/k76Ydvoi/hVO53OLSdydx7N+PHUmayr/BwGj8iBKyRNJxhQFo1DrKg8Ty661UCFaonJeaQnjgSMTx1jeeWUrX/l/cHge7yzhg6zocPc1Ttwb+ihZfBB18D9gYc2Aqlwfeahcui61x2z6iiuaSfhQlkmIX8/iUxmC4pYfMI17kUYoQpEdEkTHUGqROLfiynHS4vqxawXk5PxjePjY0g9jVBesic3L/lITsaZfEJ5aTax8G9K8YH8/C/3Zx85S6DSqUQqCRFWJ3Qe7c8dF6+UlZafZTBKKZeKz1FJ6SdPJe3J2nTo6LbsnI0Hjh08fSa3uGDD/qx9ecTJ2P86isc7a7L0dXaZenu7h4b6bA/7/+mxSG4aJCrM0N6uN3ZqDO2626aWmwajuWcu9nmMMuNyq9RaISJplqE8PpxPulJSVqVSKo13jBhm0OvbJEo5iUU7z6/BDAa325uQUdwgraQxIC6/upqeujdn++5sgQDWaDFUpVardXSRYHvp6cxyolgu97g9iRkbmyGIyxPUQxBfq9UY7xhbMcNiNFpMp9Or1bpWzDDjcidklMoULIjD4fJYEAdpaJTKFEvTEr4iUJlMkahxenpagaqqaNU1TBYL4tQwWUvDgjjksgt5hLPFJWSVWpuo8QaqZkEcGEZ4fJjHh2EYEYoaol0YRiAOr4YJ1TBZGi32FkYBjNCv1bK5sEjcVC+SXGdxIA6vXiQRiZuusyD6tVoBjCRqnJyaRlElKmuEahlSiVCvQVtVcqSO3YTUYTcVBp2Sx65hXL3cIhVrNLqEjC6XC5GqvttXujajdG1GSdKeolVphat3kVbvOrcqrXDNLuI34XHyxoNkOapzxTXieDCIz6f9WQXAGgA+jyQZgG0AbA1neToAmwBIiownHSMxg/g8jgdjGRdCoXnfbEmlALz3M1iWGs7KdLBydyTp4JOMcLssFbz7E1i+gwZJ5n2+hbhVe7yzVuuDwYH/6e62LKbL3G2x9ETHBwfuWa1Wr3cu/j5G3ihB/FXeuDM4HgzgQb8/EB2J/6ZYxG63m0x3+/r6LWF6LBZLe3uH0+mMLrw47S2MTufk8PCw3f7MHmF4eNhms3m94ftmKf8B9Pw7CpzYN7oAAAAASUVORK5CYII=" nextheight="689" nextwidth="572" class="image-node embed"><figcaption htmlattributes="[object Object]" class="hide-figcaption"></figcaption></figure><hr><h2 id="h-step-2-environment-configuration-your-apps-identity" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">Step 2: Environment Configuration - Your App's Identity</h2><p>Time to give your app some personality. Create a <code>.env</code> file with these contents:</p><pre data-type="codeBlock" text="NEXT_PUBLIC_URL=https://your-app-name.vercel.app
NEXT_PUBLIC_ONCHAINKIT_PROJECT_NAME='Your App Name'
NEXT_PUBLIC_APP_ICON=$NEXT_PUBLIC_URL/icon.png
NEXT_PUBLIC_APP_HERO_IMAGE=$NEXT_PUBLIC_URL/hero.png
NEXT_PUBLIC_APP_DESCRIPTION='Your tagline here'
NEXT_PUBLIC_APP_SUBTITLE='BetSwirl Games'
NEXT_PUBLIC_APP_SPLASH_IMAGE=$NEXT_PUBLIC_URL/splash.png
NEXT_PUBLIC_SPLASH_BACKGROUND_COLOR=#8B5CF6
NEXT_PUBLIC_APP_PRIMARY_CATEGORY=games
NEXT_PUBLIC_APP_TAGLINE='Roll the dice, spin the wheel!'"><code><span class="hljs-attr">NEXT_PUBLIC_URL</span>=https://your-app-name.vercel.app
<span class="hljs-attr">NEXT_PUBLIC_ONCHAINKIT_PROJECT_NAME</span>=<span class="hljs-string">'Your App Name'</span>
<span class="hljs-attr">NEXT_PUBLIC_APP_ICON</span>=<span class="hljs-variable">$NEXT_PUBLIC_URL</span>/icon.png
<span class="hljs-attr">NEXT_PUBLIC_APP_HERO_IMAGE</span>=<span class="hljs-variable">$NEXT_PUBLIC_URL</span>/hero.png
<span class="hljs-attr">NEXT_PUBLIC_APP_DESCRIPTION</span>=<span class="hljs-string">'Your tagline here'</span>
<span class="hljs-attr">NEXT_PUBLIC_APP_SUBTITLE</span>=<span class="hljs-string">'BetSwirl Games'</span>
<span class="hljs-attr">NEXT_PUBLIC_APP_SPLASH_IMAGE</span>=<span class="hljs-variable">$NEXT_PUBLIC_URL</span>/splash.png
NEXT_PUBLIC_SPLASH_BACKGROUND_COLOR=<span class="hljs-comment">#8B5CF6</span>
<span class="hljs-attr">NEXT_PUBLIC_APP_PRIMARY_CATEGORY</span>=games
<span class="hljs-attr">NEXT_PUBLIC_APP_TAGLINE</span>=<span class="hljs-string">'Roll the dice, spin the wheel!'</span></code></pre><p><strong>Pro tip:</strong> Pick your URL now, even before deployment. You'll claim it on Vercel later. Keep it short and memorable - <code>my-lucky-dice</code> or <code>mini-vegas</code> work great.</p><p>Restart your dev server to load the new environment:</p><pre data-type="codeBlock" text="npm run dev"><code>npm run dev</code></pre><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://img.paragraph.com/cdn-cgi/image/format=auto/https://storage.googleapis.com/papyrus_images/bc1661646de18bc923ffb7b96dcd32f7a1e1678d249e7bd21a0f0d10d413a461.png" blurdataurl="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAMCAIAAACMdijuAAAACXBIWXMAAA7EAAAOxAGVKw4bAAACzUlEQVR4nGNQ0jO18AmKb2i0C41Kbmv3Ss1yjU92jkn0TE3Pmza9fNHCBdcvGTl5eaVmhZaWh5aWZ06YICShwCcsTSRiMHLycoqOa9m80Ss1q2D6jIpFC0zd/aRVdeQ1DS18gix8gpyj46RVdVSMLVWMLVVNbPQdPYg3HWSBsr6Frq2rX3aBuLKmopGFR0qaqLwqA4sgA5MAAwjwMDAwcPCJM7EJc/CJc/CJM7IKkWYBRA8To4ChsZ2unoWMlIq8kpa0goamjoWhsZ28irahsZ2YlDJ+Uzj4xDn5JLBbwMglom9k094yZe/mE//f/M/MKOvvmFVa2NjWMGHT8j2pSYUr52/S1LFgYBJgYhPmE5YWk1GGeAXO4BOWllDUEFdQF5KQw2IBn7C0kISCvKahiZd/RFW1V2aea0QcKEy4RTkEJVh5xNgEJGPqG6KqauIbGlWMrYMKikJLyxMaW+IbGuMbGpPb2jUt7KccPbTq8V19Rw8GBgaIO1As4BOWFldQr1y8qG7F8uS29pj6Btf4ZAYGbgYGPiY2YSEJhbTu7uS29tK58/QdPTInTurbvzeqqqZh7eq+/XuiqmoMnTz69u+pXbEssbVt07uX5QvnI3sF6gMFPbOcSZNzJk2OrqkPLS0vnjPbKTrONT5Z08JeREbFPjQyua3dNytHTEYZkpDkNA0MnTz0HT3kNQ2lVXUMnTwgycwtIc3AyROLD8RklM28g5yi47RsHO1CoxIaWyA22YVGiSuoa1rYh5aWK+mZMrIKQRIFNGkgJS0mNmFGViEGBnYsQSQkIaekZyokoSCuoC4mA0owDAzsEMQEVs3BJ87AwA2JT/wIM54ZZJTUtCysyxctDC0tj29oDCooql2xTMfGUV7TUEpVC55OsKYQovKBjJKajqVtw9rVOZMmF06fJq2qE9/QaODkGVVV07d/b+H0aTo2jsS4HZcFAKub4bpJPEOHAAAAAElFTkSuQmCC" nextheight="473" nextwidth="1217" class="image-node embed"><figcaption htmlattributes="[object Object]" class="hide-figcaption"></figcaption></figure><hr><h2 id="h-step-3-the-manifest-sanity-check" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">Step 3: The Manifest Sanity Check</h2><p>Before we ship this to production, let's make sure Farcaster will know what to do with it.</p><pre data-type="codeBlock" text="curl http://localhost:3000/.well-known/farcaster.json | jq"><code>curl http://localhost:3000/.well-known/farcaster.json | jq</code></pre><p>You should see JSON with your app configuration. The <code>accountAssociation</code> fields will be empty - that's normal, we're not VIP yet.</p><pre data-type="codeBlock" text="{
  &quot;accountAssociation&quot;: {
    &quot;header&quot;: &quot;&quot;,
    &quot;payload&quot;: &quot;&quot;,
    &quot;signature&quot;: &quot;&quot;
  },
  &quot;frame&quot;: {
    &quot;version&quot;: &quot;1&quot;,
    &quot;name&quot;: &quot;My Little Casino&quot;,
    &quot;subtitle&quot;: &quot;BetSwirl Games&quot;,
    &quot;description&quot;: &quot;Knock yourself out!&quot;,
    &quot;iconUrl&quot;: &quot;https://my-little-casino.vercel.app/icon.png&quot;,
    &quot;splashImageUrl&quot;: &quot;https://my-little-casino.vercel.app/splash.png&quot;,
    &quot;homeUrl&quot;: &quot;https://my-little-casino.vercel.app&quot;,
    &quot;webhookUrl&quot;: &quot;https://my-little-casino.vercel.app/api/webhook&quot;,
    &quot;primaryCategory&quot;: &quot;games&quot;,
    &quot;heroImageUrl&quot;: &quot;https://my-little-casino.vercel.app/hero.png&quot;,
    &quot;tagline&quot;: &quot;Roll the dice, spin the wheel!&quot;
  }
}"><code>{
  "accountAssociation": {
    "<span class="hljs-selector-tag">header</span>": <span class="hljs-string">""</span>,
    <span class="hljs-string">"payload"</span>: <span class="hljs-string">""</span>,
    <span class="hljs-string">"signature"</span>: <span class="hljs-string">""</span>
  },
  "frame": {
    "version": <span class="hljs-string">"1"</span>,
    <span class="hljs-string">"name"</span>: <span class="hljs-string">"My Little Casino"</span>,
    <span class="hljs-string">"subtitle"</span>: <span class="hljs-string">"BetSwirl Games"</span>,
    <span class="hljs-string">"description"</span>: <span class="hljs-string">"Knock yourself out!"</span>,
    <span class="hljs-string">"iconUrl"</span>: <span class="hljs-string">"https://my-little-casino.vercel.app/icon.png"</span>,
    <span class="hljs-string">"splashImageUrl"</span>: <span class="hljs-string">"https://my-little-casino.vercel.app/splash.png"</span>,
    <span class="hljs-string">"homeUrl"</span>: <span class="hljs-string">"https://my-little-casino.vercel.app"</span>,
    <span class="hljs-string">"webhookUrl"</span>: <span class="hljs-string">"https://my-little-casino.vercel.app/api/webhook"</span>,
    <span class="hljs-string">"primaryCategory"</span>: <span class="hljs-string">"games"</span>,
    <span class="hljs-string">"heroImageUrl"</span>: <span class="hljs-string">"https://my-little-casino.vercel.app/hero.png"</span>,
    <span class="hljs-string">"tagline"</span>: <span class="hljs-string">"Roll the dice, spin the wheel!"</span>
  }
}</code></pre><p><strong>What you're looking for:</strong></p><ul><li><p>Your app name appears correctly</p></li><li><p>Description and tagline are there</p></li><li><p><code>primaryCategory</code> is set to "games"</p></li><li><p>URLs point to your chosen domain</p></li></ul><p>If it looks good, we're ready to go live.</p><hr><h2 id="h-step-4-deploy-to-vercel-the-point-of-no-return" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">Step 4: Deploy to Vercel (The Point of No Return)</h2><h3 id="h-install-vercel-cli" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0">Install Vercel CLI</h3><pre data-type="codeBlock" text="npm i -g vercel"><code>npm <span class="hljs-selector-tag">i</span> -g vercel</code></pre><h3 id="h-create-your-vercel-account" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0">Create Your Vercel Account</h3><pre data-type="codeBlock" text="vercel login"><code></code></pre><p>Enter your email, click the verification link. Welcome to the deployment club.</p><h3 id="h-the-big-deploy" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0">The Big Deploy</h3><pre data-type="codeBlock" text="vercel --prod"><code>vercel <span class="hljs-operator">-</span><span class="hljs-operator">-</span>prod</code></pre><p><strong>The interrogation begins:</strong></p><ul><li><p>Set up and deploy? → <strong>Y</strong> (we're committed now)</p></li><li><p>Which scope? → Select your username (it's probably your email)</p></li><li><p>Link to existing project? → <strong>N</strong> (this is fresh)</p></li><li><p>Project name? → <strong>your-app-name</strong> (or press Enter for auto-naming)</p></li><li><p>Directory? → <strong>Press Enter</strong> (current directory)</p></li><li><p>Override settings? → <strong>N</strong> (trust the defaults)</p></li></ul><p>Deployment takes about 2 minutes. Perfect time for a coffee refill.</p><p>You'll get a URL that looks like:</p><p><span data-name="check_mark_button" class="emoji" data-type="emoji">✅</span> Production: <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://your-app-name-gclocssni-ets-projects.vercel.app">https://your-app-name-gclocssni-ets-projects.vercel.app</a></p><p>Don't worry, we'll make it prettier.</p><hr><h2 id="h-step-5-environment-variables-the-tedious-but-necessary-part" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">Step 5: Environment Variables - The Tedious But Necessary Part</h2><p>Your app is live but it doesn't know who it is. Time to fix that.</p><p><strong>Option A: Via Dashboard (Recommended for sanity)</strong></p><ol><li><p>Go to your Vercel dashboard: <code>https://vercel.com</code></p></li><li><p>Click your project</p></li><li><p>Navigate to Settings → Environment Variables</p></li><li><p>Add all variables from your <code>.env</code> file</p></li><li><p>Click "Save"</p><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://img.paragraph.com/cdn-cgi/image/format=auto/https://storage.googleapis.com/papyrus_images/b5c0684db80fef3f09e610ed55493e7db961338b5ce57b2a87a8baa2c162d02b.png" blurdataurl="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAXCAIAAADlZ9q2AAAACXBIWXMAAA7EAAAOxAGVKw4bAAACmElEQVR4nJ1WwZHkIAykajLg7zd//v4TAAEoAAIgARIgASWgBIiACIiACByBr0zvMKxn92p3++GiPB61pG7JVjFG51wIIQ8457z3IkJEzExEOeeUklLKOXee53Ec549xHIcSEWYWkTrQWqu1llLm+TzPWiuSOH4PVUph5pRSG+i9t9ZKKTlna61zLufMzD/Pek2/935VEGMUkbKg1or7IQTnHBH9IXfkehEYY9BrZl45Ho/HNqC1VkqFEH6rwXmeF8E+YIwJIax1tNZuSYEAKKWs3fhWAxHZtg2hZQDR0aI8kAZwYObeOzOHEJg55ywi73m01tCPjxahFdbanPNskffeOYfiUKW1lohEJISAq/d+tm6FiKSURORFgP+sGiDiPq455ykA0vfe7/seQiAi+A1PYlx67zC6Yma1YBUANgBWAfpACCHGON2CmzjMJHLOF0GMEb5cFS6loG9m1KeUMsbMCojIOWeMQRvXCvZ9n6wfNkWUW3T8XJ/zjMOs4DgOdB+B6mfMJK4KRERrDY593981cCNBrbUxBuXDYCkl7z0ewKjPEYHIMcar/yJircWCu4lMT/iBUgpClFJgXIy6iHzpohfBvu/rDK8EbixXHGqtK0GMEfdn+jcCjMgHARFhia6w1hpjoJsx5vF4IFk4JITwblM8WWt92RQib9sGL73btD/9t4rcWsMebJ8BkY/jeNkUBHDhrUUzKTtKgRcwyTFG770x5rtFjTl/iYwlcyOAx/1zYcBOmJicMxF9uSS+FfldAwT1YyVs2+a937Zt7hnY/D8E3vvrXYtdBJuu0VGZ1pqI/vA6+/Q+QHNvBMyM9LGoMVlxYC33NsMT16IG8AGRUroJEGPUWltrp9+nGDHGWzNvpeMKgn/Fj6Q5eQwylwAAAABJRU5ErkJggg==" nextheight="873" nextwidth="1222" class="image-node embed"><figcaption htmlattributes="[object Object]" class="hide-figcaption"></figcaption></figure></li></ol><p><strong>Option B: Via CLI (For terminal purists)</strong></p><pre data-type="codeBlock" text="vercel env add NEXT_PUBLIC_URL production
# Enter: https://your-app-name.vercel.app
vercel env add NEXT_PUBLIC_ONCHAINKIT_PROJECT_NAME production
# Enter: Your App Name
# ... repeat for each variable (yes, all of them)"><code>vercel <span class="hljs-built_in">env</span> add NEXT_PUBLIC_URL production
<span class="hljs-comment"># Enter: https://your-app-name.vercel.app</span>
vercel <span class="hljs-built_in">env</span> add NEXT_PUBLIC_ONCHAINKIT_PROJECT_NAME production
<span class="hljs-comment"># Enter: Your App Name</span>
<span class="hljs-comment"># ... repeat for each variable (yes, all of them)</span></code></pre><p><strong>Redeploy with Identity Crisis Resolved</strong></p><pre data-type="codeBlock" text="vercel --prod --force"><code>vercel <span class="hljs-operator">-</span><span class="hljs-operator">-</span>prod <span class="hljs-operator">-</span><span class="hljs-operator">-</span>force</code></pre><hr><h2 id="h-step-6-claim-your-pretty-url" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">Step 6: Claim Your Pretty URL</h2><p>That auto-generated URL is embarrassing. Let's fix it:</p><pre data-type="codeBlock" text="vercel alias set [that-long-ugly-url] your-app-name.vercel.app"><code>vercel alias set [that<span class="hljs-operator">-</span>long<span class="hljs-operator">-</span>ugly<span class="hljs-operator">-</span>url] your<span class="hljs-operator">-</span>app<span class="hljs-operator">-</span>name.vercel.app</code></pre><p>Example:</p><pre data-type="codeBlock" text="vercel alias set https://your-app-name-gclocssni.vercel.app your-app-name.vercel.app"><code>vercel <span class="hljs-built_in">alias</span> <span class="hljs-built_in">set</span> https://your-app-name-gclocssni.vercel.app your-app-name.vercel.app</code></pre><p>Now you have <code>https://your-app-name.vercel.app</code> - much better for sharing at parties.</p><hr><h2 id="h-step-7-the-moment-of-truth-farcaster-integration" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">Step 7: The Moment of Truth - Farcaster Integration</h2><h3 id="h-verify-your-manifest-is-public" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0">Verify Your Manifest is Public</h3><pre data-type="codeBlock" text="curl https://your-app-name.vercel.app/.well-known/farcaster.json | jq"><code>curl https:<span class="hljs-comment">//your-app-name.vercel.app/.well-known/farcaster.json | jq</span></code></pre><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://img.paragraph.com/cdn-cgi/image/format=auto/https://storage.googleapis.com/papyrus_images/158deaf9ca12b745c93cc0e661d2b3d467444c35ee6977d0a34d78bacb970499.png" blurdataurl="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAKCAIAAABaL8vzAAAACXBIWXMAAA7EAAAOxAGVKw4bAAAB8klEQVR4nGPoW7I6sLnNNjnTv6HZv6HZvazSv64pY9bc5NlzU2bPi+mZ6FdT7Joa6lOUaODhqGRprevnL2NgxicuKyCpAEeCMkoQEoyUIYJ8olKcghIMDCLSAhJKnHwSwrLqAlIqDMz8DAzsjDyirAISDOxCDOwCjByijDySTLxSbHxSfOKy4uo6ApIKTBwiaIiZUxRThI1XnEFUWi2pa3lYzWRNO09pTRMBSQUuQUk2XnF2Pgk2XnEkJMbGK87MKcrAIgTRSSRiEJJVNfVP0HEK4BaRYWASYGARJkk/G0ELRGQ1fPLadFwDxZWUxeSVRaTkhWQUmDlFqWUNA5egpH9Rl1tKlZads7qZnrqZvraNqai8oqisAregJCSiICR5VjJwCUr55LVG1M50TS41cPfXdXRUNzc2cLJQNzeW19JU1NWW1dKQ19KUUVcXlVcUlJQj2QIhCaX4toUpfatckossA0MN3d00rcy0bS2UDfXltbVV9HTltLTElZSlVFUlVFQg8U+aBQwMDLqOoTah2aZeCUIScuJS4iJSkgwMrBycAqyc/Kyc/LyC4gLisnyiUnyiUkwcIiRbcPvP//ipsxOmzY+fPDOqb0pY7+TkabMrV61PnzU/a+7CoqVrwpvL/HMCg4rCbSO8BeXVSLKDU1ACAH6ee0LzYtbkAAAAAElFTkSuQmCC" nextheight="613" nextwidth="1878" class="image-node embed"><figcaption htmlattributes="[object Object]" class="hide-figcaption"></figcaption></figure><p>If this returns JSON, you're golden. If it returns HTML, your environment variables didn't stick - go back to Step 5.</p><h3 id="h-test-in-the-official-validator" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0">Test in the Official Validator</h3><ol><li><p>Visit: <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out underline" href="https://farcaster.xyz/~/developers/mini-apps/manifest">https://farcaster.xyz/~/developers/mini-apps/manifest</a></p></li><li><p>Enter your domain: <code>your-app-name.vercel.app</code> (no https://, it's not invited)</p></li><li><p>Click "Refresh" or press Enter</p></li></ol><p>You'll see two things:</p><ul><li><p><strong>Mini App Configuration:</strong> Should show "<span data-name="check_mark_button" class="emoji" data-type="emoji">✅</span> Mini App configuration is valid"</p></li><li><p><strong>Account Association:</strong> Will show "<span data-name="cross_mark" class="emoji" data-type="emoji">❌</span> No valid account association" (that's fine for now)</p></li></ul><p>Below the validation, you'll see a preview of your mini-app with an "Open" button. Click it to test your games.</p><p><strong>Note:</strong> The "Generate account association" button is for Step 8. Your app works without it - association just lets users save it to their mini-apps list.</p><h3 id="h-go-live-in-a-cast" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0">Go Live in a Cast</h3><p>Open Warpcast and create a new cast:</p><blockquote><p>Check out my new mini-app: <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://your-app-name.vercel.app">https://your-app-name.vercel.app</a></p></blockquote><p><a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://farcaster.xyz/chainhacker/0x25932720">https://farcaster.xyz/chainhacker/0x25932720</a></p><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://img.paragraph.com/cdn-cgi/image/format=auto/https://storage.googleapis.com/papyrus_images/42f184376815e9d357804f35e69794efe7765e3712af1bf45d8b9fed9c9f6928.png" blurdataurl="data:image/png;base64,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" nextheight="946" nextwidth="1038" class="image-node embed"><figcaption htmlattributes="[object Object]" class=""><br></figcaption></figure><p>The URL automatically embeds as a playable mini-app. Your followers can roll dice directly from their feed. You've made it.</p><hr><h2 id="h-step-8-account-association-make-it-official" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">Step 8: Account Association (Make It Official)</h2><p>When you test your manifest in Step 7, you'll likely see "No valid account association" and a "Generate account association" button. Let's fix that.</p><h3 id="h-generate-your-association-keys" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0">Generate Your Association Keys</h3><ol><li><p>Click "Generate account association" on the validator page</p></li><li><p>Scan the QR code with Warpcast on your phone</p></li><li><p>Sign the message in Warpcast (you're basically autographing your app)</p></li><li><p>Return to the validator page - you'll see something like this:</p><pre data-type="codeBlock" text="{
  &quot;accountAssociation&quot;: {
    &quot;header&quot;: &quot;eyJmaWQiOjY3OTYzNSwidHlwZS...&quot;,
    &quot;payload&quot;: &quot;eyJkb21haW4iOiJteS1saXR0bGU...&quot;,
    &quot;signature&quot;: &quot;4dSikmqgOc6bOjgdPjsirfRPvVBJWs...&quot;
  }
}"><code>{
  "accountAssociation": {
    "<span class="hljs-selector-tag">header</span>": <span class="hljs-string">"eyJmaWQiOjY3OTYzNSwidHlwZS..."</span>,
    <span class="hljs-string">"payload"</span>: <span class="hljs-string">"eyJkb21haW4iOiJteS1saXR0bGU..."</span>,
    <span class="hljs-string">"signature"</span>: <span class="hljs-string">"4dSikmqgOc6bOjgdPjsirfRPvVBJWs..."</span>
  }
}</code></pre></li></ol><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://img.paragraph.com/cdn-cgi/image/format=auto/https://storage.googleapis.com/papyrus_images/ac78be57a6165cf0ce75b1e1ffd57913e3bc7a0715f53458af8a1a54b3d5d1b3.png" blurdataurl="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAeCAIAAADCaIt+AAAACXBIWXMAAA7EAAAOxAGVKw4bAAAFcElEQVR4nJWW22/TVhjAzbS1ZMDaLHUTO65t7Lr2QtdxeaOgvlLQVtqul4fxur9hlbanSTxWQ2VjDzBgDwU6oSykFwFqmbKGdAk1aZp6FiaNFEwgTUsuc1I3TXum2Et6H+Gno5OT7+H8cr7P/k6gx76Zh39MPZr0+WYE1xQ/7vK5PPy4qxBxe/2TU4UxNR2cmg56n/7t8jyZDjyLLirywj9lDqiru0B7eydDHT9z5vOe3p6u7u6u7u6e3t7W1rMdHR2dnZ2nW1qam5tPNZ8609ra/Y5AJElS1GEUrYMgqPojM8PU4zihBamKigoURQkch4oYjUaaoggcJ8sGshXBCRzDrDRNNzY26pGmpiaO4+pwnKLrCZIkSBJBsYYG1mY7wnG2MoG4IgiCGI1GFEUZhmFZVg82MIy5prq2+kO4MPZ/fOgDmkCOHbV9dvTTcnbnOG5DgOM4DMMmk6mQB5ouhllrrbX6vZoag6XGUFtjsEDQPgiqOHTAQpIE/jZYlt0QoChqNptRFIVhuCRoYFhoK/sPnO7t/PZCzzc//nR5YGDgssbAVi5dujQwMNDf33/y5MktAhiGEQRBUbSUIo7jjIewfZDlfQiphKwQZLp545b4bPaxe9Ltdj948GBiYsLpdMqyDLayptHe3r4hwDBMTxFN03r6dJqajhw73njseOOJE03cEWZsbBQAkEqlVI2shqqqqzvYXaAfohTkOI4twnE2kjxst9sTiaQoipIkybIciUTC4XAoFJIkaW1tbds5tgj0FOkwDMPtwGazURTldDoTiUQ4HI7FYlENucjS0lKiyC4CDMNMJpM+kyS5OUubBQ6HIxQK8TwfDAYDgUAkEtF3XNL4PwGCIHoNMAzbuTunCUiSHBsbAwCUEr25qtrn+rq20L9uF5hMJv1F27k7t1WwEI9nssuZbFablxUlo63/GwvxRUXJAADa2to2BGazedefzxRhWRbH8ZGREQDAC/kl75/x+2e9viczgdnpaf55aH4hvvgmkUwrmVevY8lUevcawDC8bfdKgwGC9hkMhkpDJQRBQ0NDAIBMdvmxZ+rqtV/cHo/LNXnlys/Xr9+4dfvOXbv91u07N27eHJ+Y2C5gWXZzFyoFLQRmOVxnpXBTneUgXO0cHgYApJXMTGD2nnN4fOLRM+n5/fsP79rtQ0O/3XMO2x33fnc4Xa7J7YJN/Wdjd47j8DoCrjFbrXX19SyKYMOaYCG+mFYyam5VVVfSSiatZJKpdDKVSqbSbxLJZCqtqisAgHPnvtgQ7IXVihurasy1KE2xFgvmdBZq4PXxU395vb4nvN+fTKV0hz4y2eV0WklpNTjbWoaApmgEQRiGsX1iI3BidLTQKgRBcLvdHo+H53m9K+iU1qqqAgDOt51/u4BhGIIgCgLtMR0ZGVlbW1MURW9BqqqmNLLZrKIopTa3y3uwFxRFoSiqXyAURdntdkmSeJ73aQiCENAQ5gRBEF7KL7PZ7O69aM8U0TRJkptbRTQaDQQCwWBQEARRFPVZFMU5YS4UChU6az6/ms+/wwkQvQZbW8W2xvki8+ppXOTjovd10Pd6zv2Cj+cSne0dZdWAoiiWZUs1AACU7gCtCiuFKbeSy6/m1vN5beTW3+UEMAzjOF6qQSwW01MUDAZ5nhdF0e/3z4fmAQB5rQMuZ9XcStkCRjuBniL9PgiHw5Ikbb4M4vF4JBKRZTkajc6H52ML8urqSrkCkiSrqqr0JqgLFEWJxWKlx3EbuZXVP8cl+6A/LC12flleDXT0vzZ7FVl7A9YBAFE5edDyHQR9fbFv9KsLXW8X4DhuNBpNJpP+yPb19TkcjsHBwdt780P/1YvfX7l29deWlpZ/AUItAC50iZDQAAAAAElFTkSuQmCC" nextheight="857" nextwidth="903" class="image-node embed"><figcaption htmlattributes="[object Object]" class="hide-figcaption"></figcaption></figure><h3 id="h-add-to-vercel-environment" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0">Add to Vercel Environment</h3><p>Add these three values to your Vercel environment:</p><pre data-type="codeBlock" text="vercel env add FARCASTER_HEADER production
# Paste the header value (the long eyJ... string)
vercel env add FARCASTER_PAYLOAD production
# Paste the payload value
vercel env add FARCASTER_SIGNATURE production
# Paste the signature value"><code>vercel <span class="hljs-built_in">env</span> add FARCASTER_HEADER production
<span class="hljs-comment"># Paste the header value (the long eyJ... string)</span>
vercel <span class="hljs-built_in">env</span> add FARCASTER_PAYLOAD production
<span class="hljs-comment"># Paste the payload value</span>
vercel <span class="hljs-built_in">env</span> add FARCASTER_SIGNATURE production
<span class="hljs-comment"># Paste the signature value</span></code></pre><h3 id="h-final-deployment" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0">Final Deployment</h3><pre data-type="codeBlock" text="vercel --prod --force"><code>vercel <span class="hljs-operator">-</span><span class="hljs-operator">-</span>prod <span class="hljs-operator">-</span><span class="hljs-operator">-</span>force</code></pre><h3 id="h-verify-association" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0">Verify Association</h3><p>Return to the manifest validator and click "Refresh". You should see:</p><ul><li><p><span data-name="check_mark_button" class="emoji" data-type="emoji">✅</span> "Associated with your account"</p></li><li><p>A "Transfer ownership" button appears</p></li></ul><p>Your app is now officially associated with your Farcaster account.</p><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://img.paragraph.com/cdn-cgi/image/format=auto/https://storage.googleapis.com/papyrus_images/ebd90bd14bc6068f6d0d07b0f1143833b4f6eb699fc5be22d80e82ba8fd8453a.png" blurdataurl="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAICAIAAAAX52r4AAAACXBIWXMAAA7EAAAOxAGVKw4bAAAB9ElEQVR4nK2Sz2sTURCA9yxo8Vjo0R7qHxC13uIhhXqsxxzqSRNKPVTIpcGk+CR5OSyYijwPr80UDeJe0uASXA277j51F+kTEaWxsBrtJkFde1Db7Y8nzdqa6k38mNsM8w0zIzHGAEBRFAAghHDOxf8jCAJJ0zSEkCzLCKFMJqMoCmPM6YExZtu2aVm2bTc/NIUQjUZDVdWwjHPu+/7Hg/i+H+whrbxd0TSNc24YhuM8Z8wKKzqddqfTaXutttf68ulz22t5q97a1zUhxOzsjaGh44lEEuMCQtccx8EYF4tFWZYJIQghXdeFEN+6SIyxVCqVy+dy+RzG+enpNMCCqtYqlcXqYlV3TOPlk9qzRw/sen3JrD19uPT6xRydi8VGYrGRSOTE1NTl1Zb392ZCdgWWZY6Pn08mJxKJZDcuYlwol++WAAAWqvX7d2r3biklWrkN1TJR5h871jwtRaNnxsbOTU5eymZn3iwvm5al6zpjTFVV13X3HbuCw4ci0kGEED++r++Ps7W51TuaEIJSOjx8Oh6PY1zAuMA5p5QCAKUUIRS+yW/BzevlviMnB/qjA/3Ro32nJi5c7aY3w47bOzs9sb2+sRHe4Njg4Ojo2XT6SjY78675nhACAH/s55fgH57PdV3DsDjnr7r4e4RX7RUEQfATo6RWW2aK0y8AAAAASUVORK5CYII=" nextheight="226" nextwidth="951" class="image-node embed"><figcaption htmlattributes="[object Object]" class="hide-figcaption"></figcaption></figure><p><strong>What this unlocks:</strong> Users can now save your mini-app to their personal collection and launch it anytime from their mini-apps list.</p><hr><h2 id="h-troubleshooting-common-hiccups" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">Troubleshooting Common Hiccups</h2><h3 id="h-my-manifest-returns-html-instead-of-json" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0">"My manifest returns HTML instead of JSON"</h3><p>Your environment variables aren't deployed. Go back to Step 5, add them in Vercel dashboard, and redeploy with <code>--force</code>.</p><h3 id="h-the-dice-game-wont-load" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0">"The dice game won't load"</h3><p>Open browser console (F12). If you see provider errors, the BetSwirl SDK might not be initializing. Check that <code>app/providers.tsx</code> exists and matches the demo repo.</p><h3 id="h-vercel-keeps-asking-for-authentication" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0">"Vercel keeps asking for authentication"</h3><p>If you hit a Vercel auth page when accessing your manifest, your deployment protection is on. Either:</p><ul><li><p>Make the deployment public in Vercel settings</p></li><li><p>Or add the bypass token to your URL (check Vercel docs)</p></li></ul><h3 id="h-my-app-wont-embed-in-casts" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0">"My app won't embed in casts"</h3><p>Make sure your manifest is accessible publicly. Test with:</p><pre data-type="codeBlock" text="curl -I https://your-app-name.vercel.app/.well-known/farcaster.json"><code>curl <span class="hljs-operator">-</span>I https:<span class="hljs-comment">//your-app-name.vercel.app/.well-known/farcaster.json</span></code></pre><p>Should return <code>HTTP/2 200</code>, not 404 or 30x.</p><hr><h2 id="h-victory-lap-what-youve-built" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">Victory Lap - What You've Built</h2><p>You now have:</p><ul><li><p><span data-name="check_mark_button" class="emoji" data-type="emoji">✅</span> A live Farcaster mini-app</p></li><li><p><span data-name="check_mark_button" class="emoji" data-type="emoji">✅</span> Integrated BetSwirl games</p></li><li><p><span data-name="check_mark_button" class="emoji" data-type="emoji">✅</span> Shareable in any cast</p></li><li><p><span data-name="check_mark_button" class="emoji" data-type="emoji">✅</span> Account association (if you did Step 8)</p></li><li><p><span data-name="check_mark_button" class="emoji" data-type="emoji">✅</span> Bragging rights</p></li></ul><h3 id="h-next-steps" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0">Next Steps</h3><p><strong>Customize the games:</strong> Modify bet limits in the game components, add more games from the BetSwirl SDK.</p><p><strong>Make it yours:</strong> Update <code>app/theme.css</code> with your brand colors, swap out the placeholder images in <code>/public</code>.</p><p><strong>Go multi-game:</strong> Import additional components from <code>@betswirl/ui-react</code>: </p><pre data-type="codeBlock" text="import { DiceGame, RouletteGame, CoinFlipGame } from &quot;@betswirl/ui-react&quot;"><code><span class="hljs-keyword">import</span> { <span class="hljs-title class_">DiceGame</span>, <span class="hljs-title class_">RouletteGame</span>, <span class="hljs-title class_">CoinFlipGame</span> } <span class="hljs-keyword">from</span> <span class="hljs-string">"@betswirl/ui-react"</span></code></pre><p><strong>Track everything:</strong> Add analytics to see how users interact with your games.</p><p><strong>Get paid:</strong> Contact BetSwirl about their affiliate program for revenue sharing.</p><hr><h2 id="h-resources" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">Resources</h2><ul><li><p><a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out underline" href="https://github.com/BetSwirl/sdk">BetSwirl SDK Documentation</a></p></li><li><p><a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out underline" href="https://miniapps.farcaster.xyz/">Farcaster Mini Apps Docs</a></p></li><li><p><a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out underline" href="https://github.com/BetSwirl/miniapp-ui-react-demo">The Demo You Just Forked</a></p></li><li><p><a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out underline" href="https://vercel.com/docs">Vercel Deployment Docs</a></p></li></ul><hr><p><em>Built with BetSwirl SDK and a surprising amount of environment variables.</em></p>]]></content:encoded>
            <author>chainhacker@newsletter.paragraph.com (ChainHacker)</author>
            <category>betswirl</category>
            <category>sdk</category>
            <category>devtools</category>
            <category>farcaster</category>
            <category>mini-app</category>
            <category>web3</category>
            <enclosure url="https://img.paragraph.com/cdn-cgi/image/format=auto/https://storage.googleapis.com/papyrus_images/dd69b9972754f2fd3fe1465b4434013280e335dc9c9ab45710f8b7f87af46071.jpg" length="0" type="image/jpg"/>
        </item>
        <item>
            <title><![CDATA[E2E Tests with Playwright + Synpress: A Developer's Journey]]></title>
            <link>https://paragraph.com/@chainhacker/e2e-tests-with-playwright-synpress</link>
            <guid>EDrhf9njHQRjr6cXPORO</guid>
            <pubDate>Thu, 24 Jul 2025 18:04:06 GMT</pubDate>
            <description><![CDATA[So you've decided to dive into end-to-end testing for your Web3 app? Excellent choice!]]></description>
            <content:encoded><![CDATA[<p>So you've decided to dive into end-to-end testing for your Web3 app? Excellent choice! Getting started with e2e testing is straightforward with the right tools, and once you get the hang of it, you'll wonder how you ever lived without it.</p><h2 id="h-why-playwright-synpress" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">Why Playwright + Synpress?</h2><p>Before we jump into code, let's talk about why this combo works well. <strong>Playwright</strong> gives you a solid foundation for browser automation with great debugging tools, while <strong>Synpress</strong> adds Web3-specific features – MetaMask integration, wallet caching, and transaction handling.</p><h2 id="h-getting-started-the-setup-dance" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">Getting Started: The Setup Dance</h2><p>First things first, let's get everything installed. It's surprisingly straightforward:</p><pre data-type="codeBlock" text="npm install @synthetixio/synpress"><code>npm install @synthetixio/synpress</code></pre><p>Now, here's where it gets interesting. Your basic test setup looks something like this:</p><pre data-type="codeBlock" text="import { testWithSynpress } from '@synthetixio/synpress';
import { MetaMask, metaMaskFixtures } from '@synthetixio/synpress/playwright';
import basicSetup from './wallet-setup/metamask.setup';

const test = testWithSynpress(metaMaskFixtures(basicSetup));
let metamask: MetaMask;
const { expect } = test;"><code><span class="hljs-keyword">import</span> { testWithSynpress } <span class="hljs-keyword">from</span> <span class="hljs-string">'@synthetixio/synpress'</span>;
<span class="hljs-keyword">import</span> { <span class="hljs-title class_">MetaMask</span>, metaMaskFixtures } <span class="hljs-keyword">from</span> <span class="hljs-string">'@synthetixio/synpress/playwright'</span>;
<span class="hljs-keyword">import</span> basicSetup <span class="hljs-keyword">from</span> <span class="hljs-string">'./wallet-setup/metamask.setup'</span>;

<span class="hljs-keyword">const</span> test = <span class="hljs-title function_">testWithSynpress</span>(<span class="hljs-title function_">metaMaskFixtures</span>(basicSetup));
<span class="hljs-keyword">let</span> <span class="hljs-attr">metamask</span>: <span class="hljs-title class_">MetaMask</span>;
<span class="hljs-keyword">const</span> { expect } = test;</code></pre><p>This setup tells Synpress to use MetaMask in your tests with your specified configuration.</p><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://img.paragraph.com/cdn-cgi/image/format=auto/https://storage.googleapis.com/papyrus_images/ba05ee336232049212192c1df6df5ca8.png" blurdataurl="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAQCAIAAAD4YuoOAAAACXBIWXMAAA7EAAAOxAGVKw4bAAAESklEQVR4nJ2UXUxbZRjHG4GZrWX2tOMc2tKW9nz0fLXn9PS0h9IDpbXtCpxSoB+wrtsKSKFAy4Q5IGEgm0PRZW53i15AZHihGCIuRiCoQXZhSEjmhRovjJoYL8xu3JVuF4aVmGVBR0z+eS/ei/f3/z/v8zyy6ZmrQ4URXyAcDEvFs1FqrfE0BKV0fUPAKdSCkFauAP63ZLFkqjHSFgxLr868di7T63DVil6f6A14vCF/sDHZccYfbIJhC0ExBMWgGGmxUChG4qSVIG0gpFUoK/5bsr5cfnTs0vTM1V/vP7z/x5+rn25cmX0rlkzJZKVqFZRukYL+0OjQ+EBPIdEUa4vGpagUz3S2ZVKJl9LS6Vi8J4VS1JEj5f+aoLt3oDD8Sk+2cHt5c+OL7Y3NrTfevNkUaZcrAAjSiE4Bt5A1vMizbgzBcIvFTOC00251cZUGPaACXzgBqsHKkjKF7LnnS8oUBwB8gbDgEc+PTn68/vXE5NRQYUT0Blw1dYAKhCCNjaZRxOKkBMrCMJyzviFkY3gryXG0y4rbEYTwBZth2AKbUI52oWa89OjxpwGJXEe8N56fuTD/yfLr78zNvXv92vzb6aGMXAEAKrBKV200wghCmMyI0QxjBIWglipdtRlGYRh7fEMbTLDeYNJqDFDFAe0g6zx/Kj7Q3n959MeHf337+72dX3Y3vtl4+fJIuRI0GeGEFEUtdpzhcMZmRlEEJwiGQUkCpSicseGMDcFxVnBiOFF8DlCBT0peri4CYr1Tgx9sry+tL360vTJxcyI3OcQ6PATFtEWiUksyebq7riEU8kc64+dC/kg0kjjT0x9p6/AInmg08WKwud53UmpJZLpybtHPC3W8UMc63DTDVxnQfcDglYs/PHiw9d2X83cWMuOZgUuDOp2x7JiSwKw867Y6RAwnSJZ1iSLJsgzPkyyL4YRGqwchrU5vAiEtoAL3J6ZcDahAJVDxZIn2EtzZvbu49t7cwrXsVLZ3rB+EdGXHlCRO1rjc/pMtNo63s/ZgWAqGm0VvgHM4nUKt21PPcM5QY9QMoyVligObdf8Pigl2ft5ZXFtKj6YjmRYQ0skVAIYSHCPQnNvKO6y8gxVc1sf2GZ4vupaXq58xyf8kWN5eW1q/vbi2dOP9G5FMC6CC5ApAbzARFFNtRnDSiqCWovQG2Gg0a7T6Q62K9Gg6WUgOzF786dGje7/tfvX93Vsrt1p7WkFIV3r0eNgb6E71nO0byebz2fxgNp/vzHTFTqWDYalSU3UoQLw3Ge1q77qQ+/Dzz+ZXFoanRyKZ1lCi6QSokSsAxIyRmA23OkmWxWnayjtwmmYFFyu4zCh6KEAwLInewNj41Mbm1urqWrJjz53oDRQNGo2wjeFphrdxvNEMK5RqNVipUD6j7k8DfIFwX254du566myXLxBujLSJ3gCgAott6rJ7KLubqxEox964UQ671mg8/A7/G0yYT1Io8DV4AAAAAElFTkSuQmCC" nextheight="577" nextwidth="1173" class="image-node embed"><figcaption htmlattributes="[object Object]" class="hide-figcaption"></figcaption></figure><div data-type="embedly" src="https://github.com/BetSwirl/sdk/blob/main/packages/ui-react/test/wallet-setup/basic.setup.ts" data="{&quot;provider_url&quot;:&quot;https://github.com&quot;,&quot;description&quot;:&quot;SDK to interact with BetSwirl's smart-contracts. Contribute to BetSwirl/sdk development by creating an account on GitHub.&quot;,&quot;title&quot;:&quot;sdk/packages/ui-react/test/wallet-setup/basic.setup.ts at main · BetSwirl/sdk&quot;,&quot;author_name&quot;:&quot;BetSwirl&quot;,&quot;thumbnail_width&quot;:1200,&quot;url&quot;:&quot;https://github.com/BetSwirl/sdk/blob/main/packages/ui-react/test/wallet-setup/basic.setup.ts&quot;,&quot;thumbnail_url&quot;:&quot;https://img.paragraph.com/cdn-cgi/image/format=auto/https://storage.googleapis.com/papyrus_images/a86123f28875211d38ff8a94bd7663f1.png&quot;,&quot;author_url&quot;:&quot;https://github.com/BetSwirl&quot;,&quot;version&quot;:&quot;1.0&quot;,&quot;provider_name&quot;:&quot;GitHub&quot;,&quot;type&quot;:&quot;link&quot;,&quot;thumbnail_height&quot;:600,&quot;image&quot;:{&quot;base64&quot;:&quot;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAQCAIAAAD4YuoOAAAACXBIWXMAAAsTAAALEwEAmpwYAAACt0lEQVR4nK1UUUgUQRge6ClQDAlKkIiMkiJ6UBEv6aHHAiuQgogeDMOoDoJee1WKnqLo0QIJQu300pA7dtvT9Xbdu/OuVTvzOC/x7PB0b/fudNxxZ3fjvLvd9fLEBz9+ln9m/plv/n+/f4C+A7wbxRkVY9W6qu8LjFWEFIQUGSnGJLAsm0dYafTd3/0JDF8p+gUCkiR5npckyecPMAyDMU7msBaen08kEpquOxxOjuNKTik5OvTzT8WFp6D+0f2XfWYG+Xs1NLWcrKmtPV13o+12Y7Otsdl2/uKljoedXY+fnDl7DhRh5LdXAho4cgtU3wE190D1zdYXH/J5FPYQBNk/MDgy8n3Syzid33o/fuofGGSnOJ6f+epw2O3P3757393zCkJY7vosswBAGwDXQXU7aHoGrtlznPkSYYyz2Y10JrOFEIRwC+UAIUwm19YFIZ3JILSNEMIYp0QxJYqSJCWTa4KQSomiQRBfFkBlO6jrBA12UN9R1fUmv5Qj0LBGUbTLTU4Hg5Ne1uUmCYKkaYaiaDdBDjtHR0bHOF+AnZoiSMrnD/j8gfEJmqQ8gemQkQdC2z2vh3MlOvXg+N3uIXrGJDgspNOQ80WHxoKh3yv5+pgq0sqrEO8lm/2BsVoq00MB3mlMoz0PRIDN1tu17QBkuJQgr5P/+xmbL0fB3xHV9kF6u0CAEGIYludnrE8CQVIuN8kwTD5udu7X+AS9tLSk6/riYuwH5VmOx/e6eC7LuXCk74uzQABltAFlUcrMhhcmWY72sgzrkzLZ1fVUJBpzER7OH1pdF7KbMBKNuQnPalKAMor/TXgmvJFoTFYwlJFhmzLCmhbkw72fHRtQRki2/gNNxYqKFV231lpVMSpKLjcs45cvEai0FexYq2mVNlDRYk5aY0rijaHVjraAqisAXL5ad+IfDPYVO0sKjgwAAAAASUVORK5CYII=&quot;,&quot;img&quot;:{&quot;width&quot;:1200,&quot;height&quot;:600,&quot;src&quot;:&quot;https://img.paragraph.com/cdn-cgi/image/format=auto/https://storage.googleapis.com/papyrus_images/a86123f28875211d38ff8a94bd7663f1.png&quot;}}}" format="small"><link rel="preload" as="image" href="https://img.paragraph.com/cdn-cgi/image/format=auto/https://storage.googleapis.com/papyrus_images/a86123f28875211d38ff8a94bd7663f1.png"><div class="react-component embed my-5" data-drag-handle="true" data-node-view-wrapper="" style="white-space:normal"><a class="link-embed-link" href="https://github.com/BetSwirl/sdk/blob/main/packages/ui-react/test/wallet-setup/basic.setup.ts" target="_blank" rel="noreferrer"><div class="link-embed"><div class="flex-1"><div><h2>sdk/packages/ui-react/test/wallet-setup/basic.setup.ts at main · BetSwirl/sdk</h2><p>SDK to interact with BetSwirl's smart-contracts. Contribute to BetSwirl/sdk development by creating an account on GitHub.</p></div><span><svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-link h-3 w-3 my-auto inline mr-1"><path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71"></path><path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71"></path></svg>https://github.com</span></div><img src="https://img.paragraph.com/cdn-cgi/image/format=auto/https://storage.googleapis.com/papyrus_images/a86123f28875211d38ff8a94bd7663f1.png"></div></a></div></div><h2 id="h-writing-tests-connect-that-wallet" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">Writing Tests: Connect That Wallet!</h2><p>Let's start with something every Web3 app needs – connecting a wallet. Here's what a basic test looks like:</p><pre data-type="codeBlock" text="test.describe('My Awesome DApp Tests', () =&gt; {
  test('should connect wallet successfully', async ({ page }) =&gt; {
    // Navigate to your app
    await page.goto('http://localhost:3000');    
    // Click that connect button
    await page.click('[data-testid=&quot;connect-wallet&quot;]');
    
    // Synpress handles the MetaMask popup automatically

    // Check if we're connected
    await expect(page.locator('[data-testid=&quot;wallet-address&quot;]')).toBeVisible();
  });
});"><code>test.describe(<span class="hljs-string">'My Awesome DApp Tests'</span>, () <span class="hljs-operator">=</span><span class="hljs-operator">&gt;</span> {
  test(<span class="hljs-string">'should connect wallet successfully'</span>, async ({ page }) <span class="hljs-operator">=</span><span class="hljs-operator">&gt;</span> {
    <span class="hljs-comment">// Navigate to your app</span>
    await page.goto(<span class="hljs-string">'http://localhost:3000'</span>);    
    <span class="hljs-comment">// Click that connect button</span>
    await page.click(<span class="hljs-string">'[data-testid="connect-wallet"]'</span>);
    
    <span class="hljs-comment">// Synpress handles the MetaMask popup automatically</span>

    <span class="hljs-comment">// Check if we're connected</span>
    await expect(page.locator(<span class="hljs-string">'[data-testid="wallet-address"]'</span>)).toBeVisible();
  });
});</code></pre><p>What I love about this is how <strong>clean</strong> it is. No wrestling with popup windows or iframe nightmares – Synpress handles all that for you.</p><h2 id="h-the-transaction-test-where-things-get-real" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">The Transaction Test: Where Things Get Real</h2><p>Once you've mastered wallet connection, it's time for the main event – testing transactions:</p><pre data-type="codeBlock" text="test('should send tokens successfully', async ({ page }) =&gt; {
    // Navigate to coin toss game
    await page.goto(&quot;/coinToss.html&quot;)

    // Connect wallet
    const connectButton = page.getByTestId(&quot;ockConnectButton&quot;)
    await connectButton.click()

    // Find and click play button
    const playButton = page.locator('button:has-text(&quot;Place Bet&quot;)')
	// Click play button
    await playButton.click()
	await metamask.confirmTransaction()
  
    // Verify success
      const resultModal = page.locator('[role=&quot;dialog&quot;]').filter({ hasText: /You (won|lost)/i })
      const hasResultModal = await resultModal.isVisible({ timeout: 10000 }).catch(() =&gt; false)
});"><code>test(<span class="hljs-string">'should send tokens successfully'</span>, async ({ page }) <span class="hljs-operator">=</span><span class="hljs-operator">&gt;</span> {
    <span class="hljs-comment">// Navigate to coin toss game</span>
    await page.goto(<span class="hljs-string">"/coinToss.html"</span>)

    <span class="hljs-comment">// Connect wallet</span>
    const connectButton <span class="hljs-operator">=</span> page.getByTestId(<span class="hljs-string">"ockConnectButton"</span>)
    await connectButton.click()

    <span class="hljs-comment">// Find and click play button</span>
    const playButton <span class="hljs-operator">=</span> page.locator(<span class="hljs-string">'button:has-text("Place Bet")'</span>)
	<span class="hljs-comment">// Click play button</span>
    await playButton.click()
	await metamask.confirmTransaction()
  
    <span class="hljs-comment">// Verify success</span>
      const resultModal <span class="hljs-operator">=</span> page.locator(<span class="hljs-string">'[role="dialog"]'</span>).filter({ hasText: <span class="hljs-operator">/</span>You (won<span class="hljs-operator">|</span>lost)<span class="hljs-operator">/</span>i })
      const hasResultModal <span class="hljs-operator">=</span> await resultModal.isVisible({ timeout: <span class="hljs-number">10000</span> }).catch(() <span class="hljs-operator">=</span><span class="hljs-operator">&gt;</span> <span class="hljs-literal">false</span>)
});</code></pre><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://img.paragraph.com/cdn-cgi/image/format=auto/https://storage.googleapis.com/papyrus_images/25107dbaed68737f0a5649d0a916b0dc.png" blurdataurl="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAARCAIAAAAzPjmrAAAACXBIWXMAAA7EAAAOxAGVKw4bAAAEWklEQVR4nKVT204bVxTd5xwsy3Iw2B7PfTy+celDn/oFadS39hMqoUZNH/vWH6iqSu1L1YdGVaVWlaJWQWoaEkKaGFxEHEogGHwBEyCG2GB8GTs2YHBDzFTbNvSitqrU0dbRnpFmrb3XWgc++OJzuHwZrlyBkXdg5Aq8PQIj78LIe/DWq9Ilnrsoia9LwhuicFESL2Ejvym6X5OI4qe+AAkOgt19/c5ser0YiRUfrR8vZ08WMyfLmeNKpV4yantFA8B+AZw8ODiwuYD14wkXgPWBzQNWERwy9EpY9vNTBodEnSLlpB5OArtr+uGcaZqHjeNq/Whrp7qdr+UK9aKxv3/c2j96CTQ0CKEh5vUzXu1RfNQbZILKJC9VdCYqTNKYpFCPwhQvlqThR05kLhk/8irY3dPR6MnJi0q1UqlUDMN4XnterVaNSrVcNg4OG4CzKD4ialRQqeSlXHc66kFcyqvEo9B+gQka5VXE7eUJcxKLm1jcPRY3kL6JyUiz2azXDxqNRqFQyO3sFAp7ZaPcaBw1m02gehCGhqk3QAIDVA+SwADxB4k/SBUf5RQqaEzVCS9TSaOSlzgE0ivQfgEbh9DjEMDuunF7otb4tXH84qRlhn+OhiMP5pZSTzYyZvsBGB4mwRBTdRgYhMFBeGWYajpiiYhIBblDAC6ROEWE5mUqyMCJwEmUk8AhXLt5o5i7//7d+EfzTz+czXw6tfhtdP7a8jPTPEUCxPIo+L+oUUXD/zmJeBTmkhlqIqJEnNTRhFjb1W6ojSNWN1jdV0evT6cmry5tjT4pfJcufjObnl+Y+CGx0kJ8E4ioQWiA6n7q9VFfgKk6U3Wq6OjEuRnONo2goQGWLjSxupmVA4vz+1s/3k4/Dadz4dWd6fW9B5ul+/F4KpPsSkSCIQgNUc2HHngDSOYLUN2PNO1ViIjOE1HDnVwy2u4UwYUS9bhksHNjE2OrW4mZRCy8mhyPx28+Xp6ML8Y21s48GBoiwVDXZEkjIhZ6K6jY8CpuI6i4jZ3Hsx0qzJVLZk4RbK7xcKRcNjY30nOPUkux3Wy+sLmdz5Wqp10PNB9CKDr1BqiiE1FBG/DVh7hKVzHiwqnPZye8TDwKpoj13boXzmb3luLJhV8S6cVsvlDYKxZKZeP0tE2AIQmG2hnF24986LCIs3euQttk1nnlVWT1KLiNjWM2Diz9Y3fDK6vp2FJidW09lU4vx9aikdh2NtuVqJN0HFPyUsl71rRV8qgMQ6lST9tqDq8uFVQM6B8Ixu9Nba5vJOKJZGIlmUgm46tzMwvPzglwa05ClHau0UYBbezG0fL3RW3c7wThyFqm9Di1FU/nlle242u5hVQmu1s6uwe9fDfdf64OBP3XQgJr/+idqa+jh59MFL+MlL6KGJ/9VP54vDy3ZpgmugD/BeifCTzA+qZmovWjlzulWr58mC/v543D3fJ+7aBxtsH/IOix80B6H84vmKbZarU6ov/l+Q367gWVO2ORbQAAAABJRU5ErkJggg==" nextheight="694" nextwidth="1328" class="image-node embed"><figcaption htmlattributes="[object Object]" class="hide-figcaption"></figcaption></figure><div data-type="embedly" src="https://github.com/BetSwirl/sdk/blob/main/packages/ui-react/tests/coinToss.spec.ts" data="{&quot;provider_url&quot;:&quot;https://github.com&quot;,&quot;description&quot;:&quot;SDK to interact with BetSwirl's smart-contracts. Contribute to BetSwirl/sdk development by creating an account on GitHub.&quot;,&quot;title&quot;:&quot;sdk/packages/ui-react/tests/coinToss.spec.ts at main · BetSwirl/sdk&quot;,&quot;author_name&quot;:&quot;BetSwirl&quot;,&quot;thumbnail_width&quot;:1200,&quot;url&quot;:&quot;https://github.com/BetSwirl/sdk/blob/main/packages/ui-react/tests/coinToss.spec.ts&quot;,&quot;thumbnail_url&quot;:&quot;https://img.paragraph.com/cdn-cgi/image/format=auto/https://storage.googleapis.com/papyrus_images/756403da0e3c1315bd22522f42f39524.png&quot;,&quot;author_url&quot;:&quot;https://github.com/BetSwirl&quot;,&quot;version&quot;:&quot;1.0&quot;,&quot;provider_name&quot;:&quot;GitHub&quot;,&quot;type&quot;:&quot;link&quot;,&quot;thumbnail_height&quot;:600,&quot;image&quot;:{&quot;base64&quot;:&quot;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAQCAIAAAD4YuoOAAAACXBIWXMAAAsTAAALEwEAmpwYAAACt0lEQVR4nK1UUUgUQRge6ClQDAlKkIiMkiJ6UBEv6aHHAiuQgogeDMOoDoJee1WKnqLo0QIJQu300pA7dtvT9Xbdu/OuVTvzOC/x7PB0b/fudNxxZ3fjvLvd9fLEBz9+ln9m/plv/n+/f4C+A7wbxRkVY9W6qu8LjFWEFIQUGSnGJLAsm0dYafTd3/0JDF8p+gUCkiR5npckyecPMAyDMU7msBaen08kEpquOxxOjuNKTik5OvTzT8WFp6D+0f2XfWYG+Xs1NLWcrKmtPV13o+12Y7Otsdl2/uKljoedXY+fnDl7DhRh5LdXAho4cgtU3wE190D1zdYXH/J5FPYQBNk/MDgy8n3Syzid33o/fuofGGSnOJ6f+epw2O3P3757393zCkJY7vosswBAGwDXQXU7aHoGrtlznPkSYYyz2Y10JrOFEIRwC+UAIUwm19YFIZ3JILSNEMIYp0QxJYqSJCWTa4KQSomiQRBfFkBlO6jrBA12UN9R1fUmv5Qj0LBGUbTLTU4Hg5Ne1uUmCYKkaYaiaDdBDjtHR0bHOF+AnZoiSMrnD/j8gfEJmqQ8gemQkQdC2z2vh3MlOvXg+N3uIXrGJDgspNOQ80WHxoKh3yv5+pgq0sqrEO8lm/2BsVoq00MB3mlMoz0PRIDN1tu17QBkuJQgr5P/+xmbL0fB3xHV9kF6u0CAEGIYludnrE8CQVIuN8kwTD5udu7X+AS9tLSk6/riYuwH5VmOx/e6eC7LuXCk74uzQABltAFlUcrMhhcmWY72sgzrkzLZ1fVUJBpzER7OH1pdF7KbMBKNuQnPalKAMor/TXgmvJFoTFYwlJFhmzLCmhbkw72fHRtQRki2/gNNxYqKFV231lpVMSpKLjcs45cvEai0FexYq2mVNlDRYk5aY0rijaHVjraAqisAXL5ad+IfDPYVO0sKjgwAAAAASUVORK5CYII=&quot;,&quot;img&quot;:{&quot;width&quot;:1200,&quot;height&quot;:600,&quot;src&quot;:&quot;https://img.paragraph.com/cdn-cgi/image/format=auto/https://storage.googleapis.com/papyrus_images/756403da0e3c1315bd22522f42f39524.png&quot;}}}" format="small"><link rel="preload" as="image" href="https://img.paragraph.com/cdn-cgi/image/format=auto/https://storage.googleapis.com/papyrus_images/756403da0e3c1315bd22522f42f39524.png"><div class="react-component embed my-5" data-drag-handle="true" data-node-view-wrapper="" style="white-space:normal"><a class="link-embed-link" href="https://github.com/BetSwirl/sdk/blob/main/packages/ui-react/tests/coinToss.spec.ts" target="_blank" rel="noreferrer"><div class="link-embed"><div class="flex-1"><div><h2>sdk/packages/ui-react/tests/coinToss.spec.ts at main · BetSwirl/sdk</h2><p>SDK to interact with BetSwirl's smart-contracts. Contribute to BetSwirl/sdk development by creating an account on GitHub.</p></div><span><svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-link h-3 w-3 my-auto inline mr-1"><path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71"></path><path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71"></path></svg>https://github.com</span></div><img src="https://img.paragraph.com/cdn-cgi/image/format=auto/https://storage.googleapis.com/papyrus_images/756403da0e3c1315bd22522f42f39524.png"></div></a></div></div><h2 id="h-debugging-when-tests-go-wrong-and-they-will" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">Debugging: When Tests Go Wrong (And They Will)</h2><p>Let's be real – tests won't work perfectly the first time. Here are essential debugging techniques:</p><h3 id="h-1-the-debug-flag" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0">1. The Debug Flag</h3><pre data-type="codeBlock" text="DEBUG=synpress:* npm test"><code><span class="hljs-attr">DEBUG</span>=synpress:* npm test</code></pre><p>This gives you <strong>detailed logs</strong> about what's happening under the hood.</p><h3 id="h-2-slow-things-down" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0">2. Slow Things Down</h3><p>When a test fails and you need to see what's happening:</p><pre data-type="codeBlock" text="SLOW_MO=1000 npm test"><code><span class="hljs-attr">SLOW_MO</span>=<span class="hljs-number">1000</span> npm test</code></pre><p>This adds a 1-second delay between actions, making it easier to follow the test execution.</p><h3 id="h-3-screenshots-on-failure" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0 first:!mb-0">3. Screenshots on Failure</h3><p>Add this to your test for automatic failure documentation:</p><pre data-type="codeBlock" text="test.afterEach(async ({ page }, testInfo) =&gt; {
  if (testInfo.status !== 'passed') {
    await page.screenshot({ path: `screenshots/${testInfo.title}.png` });
  }
});"><code>test.afterEach(async ({ page }, testInfo) <span class="hljs-operator">=</span><span class="hljs-operator">&gt;</span> {
  <span class="hljs-keyword">if</span> (testInfo.status <span class="hljs-operator">!</span><span class="hljs-operator">=</span><span class="hljs-operator">=</span> <span class="hljs-string">'passed'</span>) {
    await page.screenshot({ path: `screenshots<span class="hljs-operator">/</span>${testInfo.title}.png` });
  }
});</code></pre><h2 id="h-the-setup-script-that-wouldnt-quit" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">The Setup Script That Wouldn't Quit</h2><p>Here's a fun one that had me pulling my hair out. When running <code>pnpm test:e2e-setup</code>, the process would just... hang. Forever. No errors, no timeout, just an eternal loading state staring back at me.</p><p>The setup script looked innocent enough:</p><pre data-type="codeBlock" text="test(&quot;Setup wallet&quot;, async ({ context, metamask }) =&gt; {
  await metamask.connectToDapp()
  await metamask.switchNetwork('base')
  // ... rest of setup
})"><code>test(<span class="hljs-string">"Setup wallet"</span>, async ({ context, metamask }) <span class="hljs-operator">=</span><span class="hljs-operator">&gt;</span> {
  await metamask.connectToDapp()
  await metamask.switchNetwork(<span class="hljs-string">'base'</span>)
  <span class="hljs-comment">// ... rest of setup</span>
})</code></pre><p>After what felt like hours of staring at a frozen terminal, I finally ran it with debug flags:</p><pre data-type="codeBlock" text="DEBUG=synpress:* pnpm test:e2e-setup"><code>DEBUG<span class="hljs-operator">=</span>synpress:<span class="hljs-operator">*</span> pnpm test:e2e<span class="hljs-operator">-</span>setup</code></pre><p>And there it was! The debug logs revealed MetaMask was showing a network switch confirmation dialog that the script couldn't handle:</p><pre data-type="codeBlock" text="synpress:metamask Switching to network: base
synpress:metamask Waiting for network switch confirmation...
[hanging here forever]"><code>synpress:metamask Switching to network: <span class="hljs-keyword">base</span>
synpress:metamask Waiting <span class="hljs-keyword">for</span> network <span class="hljs-keyword">switch</span> confirmation...
[<span class="hljs-meta">hanging here forever</span>]</code></pre><p><strong>The solution?</strong> Remove the network switch from the setup entirely. The cached wallet doesn't need to be on a specific network – let each test handle its own network requirements. Sometimes the simplest solutions are the best ones.</p><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://img.paragraph.com/cdn-cgi/image/format=auto/https://storage.googleapis.com/papyrus_images/ba3ab469ad5c530d12dd6a020392c0ce.png" blurdataurl="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAGCAIAAAAt7QuIAAAACXBIWXMAAA7EAAAOxAGVKw4bAAABx0lEQVR4nFWRXWvTUBjHY9laZprXtjk5zTl5OScnb11jmqVt5prRdZKtizciKggyEXYng+KtN34EwYEIc3rjF/ODTGKhdPC7+D//i+fHw8P9vb+vbn5UN9/Pvn579+vPy9u7V7d3b37+Pvl8uVwtTz9elNfL80/Py1VVXlfnq+rFl9fDi1yjVGZhJ4o7UawOk5YKWyrcFUDjcW/DjqDtCBrHtRXR9Xkd8TpqE9a23d0ubOkGb/pCdNCmAzFIhGC0Z4diMBLYsCGjFrBEiwnQFLFbY9J1rtc1ZK6pbms4fDxXglhyfIlFksVEGtTB8QWTSG4ouaFCPdnxZBbWOK5CPcmiQt8WEBWxW4e+zQNcmxxPpIFo0geC/av3aFZo6QHIJt1BDCdTfTI18kOQZvZZacwKmB/ByRQvFuBpIe+n4Hiuz09AftSfFf1ZAfJDLRurw8SYz/Fi0Z8VWjbePoKDXhAlA+L7yKFe6KfjmDCWZk9YGKR5FiWDURYn2VDBRIIWcplOWM+mAkB7PWPN+gE8wJvmwQUmYSrGOnF62NSwqROnAw2d2KqB4P8SEqdrILWHsOVcXZYf3j6rylxSdK6pbnjU7G6P24J/S9RRnfUi3AMAAAAASUVORK5CYII=" nextheight="206" nextwidth="1092" class="image-node embed"><figcaption htmlattributes="[object Object]" class="hide-figcaption"></figcaption></figure><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://img.paragraph.com/cdn-cgi/image/format=auto/https://storage.googleapis.com/papyrus_images/8b3c258ab072245842d451ca3feb1506.png" blurdataurl="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAARCAIAAAAzPjmrAAAACXBIWXMAAA7EAAAOxAGVKw4bAAAEKElEQVR4nJVVz4/bRBS2ULS/vGuPx854Jk7suHacxD8Sx3EcJ7aTdbMbd7ddbdWKClS4lAMSEivtoRISqjgg7hy4IEAVVEIcupelcEDiH+DMkRv/ABfOrSZuy65UKpA+jd48S++b931vxswff/19/O2jw6++Lr748u3vvr/5zcPbDx/d/eEs++Te4ele/sH+4sPl8nS5d3pt/6RYni5vfHzk30ykcCI5fakfANOBDU1umYLlinZftPvA6KzvyJsAs5CwkDCsagDH48wOp7W4tgOc/lZd32yaYrcHvaFo96E3RMFYcgeSOxD9kNfbvNbiNZNXdBrrbWB0OdLktdYmwJUKXNtCmwC/BCN6A8nuC3obWi40bWh0oWlXLRdoFt2aK1gOr7cFywFGh1cMnjQ5rFMCReewutqu1oZBk0S7RGC8eRtnmeT0URQDyxW8QPRDcTQRHF9OZtUkg4MhDEYky+RxLIdRbb6r5DkKQpLNSDaTQ5oUvQHJZvXFHklSOYxKiZ4TYD+wo6Hl97Su03Kd3jjQ2u1RNjZcN5hNnDj0RkGYjiXb47QW0Cxg2sDoUon0FjA6XKPFNQyuYQCjQ/N6m2tQrf4hqNebpKk1TF2uN5Qrev2KjlVVbxtY1VRTr6mqZplKU5PEmiiQN9ZgpQIr69LaVrWyLl3CGs2XWNtCJdZ3ZAYFoej50HIozA7QDKCZgt5axSak56Inrdp9aTUhguWujO0I5boy5rlVRqdsUXhhIU80xkmnKM9RFAuWK7kDYHTLZunYdXvAtFEQoiASLIfDDWqmom+jeunqDmpso/pL7KAGKykcVjmsbiOFfpUURp/NSJ6XjpEsI4uFkufyOMbThOS5sljQTFGgKCYrb/E0rfqh1A9QEPKKXurwb9jYwQzOr5IkIUlKK2YzJc/r1w/lOMHTRA4jOjxhhIIITxMU0YHB01QOY0FvsbDGwtpFP18Jpjaf09JJKsd0pRzFUo4TFEQkSUVvII9paRTFojdAo6iMWUguzuLrCXZpB9msJKjNd+sH10oCukaxPEnpDRhTglUHCZ6mguVyWP1PBCS71AG9R8WSKp6kKIqrfki1eqEYCkf0PhpdFMXQcjZ2/gdBIo9pUbwiwNO0Np+XrHJIW6n6YdUP6XPW7QGjCy2XV3S4GlAOq6+Ri2kWBVUpv0rFWew1ikI7PqLVsxmlzDI5HNXms9p8LgdDqesIhlU+k6v3kvpcxhfBQSKIBIqEBZhhrDZ0XOj1ql4PeD1xOKxOYr7dEf0RGKUgmLC6zep2GQB/zJr+BtKA0WFh7ZUS0aIVmWEQw1SZDZn58+nTd85/vPP47M7js3s//Xz3/PzdJ0/e++XX/c9Pbn126/qnbxX3D5an9Jewf1IU9w+OHtzwjycsuvRkXqrOkff3W78/mP720Xjuas8Ahlru1oj2TVkAAAAASUVORK5CYII=" nextheight="578" nextwidth="1098" class="image-node embed"><figcaption htmlattributes="[object Object]" class="hide-figcaption"></figcaption></figure><h2 id="h-writing-better-tests" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">Writing Better Tests</h2><p>Here's what separates good e2e tests from great ones:</p><ol><li><p><strong>Test user journeys, not implementation details</strong> – Think "user connects wallet and sends tokens" not "button click triggers function X"</p></li><li><p><strong>Keep tests independent</strong> – Each test should work on its own. No test should depend on another test running first.</p></li><li><p><strong>Use data-testid attributes</strong> – They're more stable than CSS selectors:</p><pre data-type="codeBlock" text="&lt;button data-testid=&quot;connect-wallet&quot;&gt;Connect&lt;/button&gt;"><code><span class="hljs-operator">&lt;</span>button data<span class="hljs-operator">-</span>testid<span class="hljs-operator">=</span><span class="hljs-string">"connect-wallet"</span><span class="hljs-operator">&gt;</span>Connect<span class="hljs-operator">&lt;</span><span class="hljs-operator">/</span>button<span class="hljs-operator">&gt;</span></code></pre></li><li><p><strong>Be generous with assertions</strong> – But not too generous. Check what matters, ignore what doesn't.</p></li></ol><h2 id="h-wrapping-up" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0 first:!mb-0">Wrapping Up</h2><p>E2E testing with Playwright and Synpress might seem daunting at first, but it's genuinely worth the investment. Start small – maybe just test your wallet connection flow. Then gradually add more complex scenarios.</p><p>Remember: <strong>done is better than perfect</strong>. Tests don't need to cover every edge case right away. They just need to catch the obvious breaks while you focus on building features.</p><p>The best part? Once you have a solid test suite, you can refactor with confidence, ship features faster, and actually enjoy your weekends without worrying about production issues.</p><p>Happy testing!</p><br>]]></content:encoded>
            <author>chainhacker@newsletter.paragraph.com (ChainHacker)</author>
            <category>betswirl</category>
            <category>devtools</category>
            <category>e2e</category>
            <category>tests</category>
            <category>web3</category>
            <enclosure url="https://img.paragraph.com/cdn-cgi/image/format=auto/https://storage.googleapis.com/papyrus_images/256083bf2ecb08264927f9cd78d3cedc.jpg" length="0" type="image/jpg"/>
        </item>
        <item>
            <title><![CDATA[Ship It: React Integration in 5 Minutes]]></title>
            <link>https://paragraph.com/@chainhacker/betswirl-sdk-react-integration</link>
            <guid>hkGu3XEz1YnFXMrsrBFC</guid>
            <pubDate>Sun, 22 Jun 2025 21:58:37 GMT</pubDate>
            <description><![CDATA[Remember when we said we were "nearly package-ready"? Well, we shipped it. The npm package is live, the React integration is smoother than butter, and we've got a complete demo app deployed on Vercel. ]]></description>
            <content:encoded><![CDATA[<p>Remember when we said we were "nearly package-ready"? Well, we shipped it. The npm package is live, the React integration is smoother than butter, and we've got a complete demo app deployed on Vercel. Here's how the journey unfolded.</p><h2 id="h-from-storybook-to-npm" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0">From Storybook to npm</h2><p>Last week we had two games (CoinToss and Dice) living happily in our Storybook demo. This week? They're on npm, ready to drop into any React app.</p><p><span data-name="package" class="emoji" data-type="emoji">📦</span><strong> </strong><a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://www.npmjs.com/package/@betswirl/ui"><strong>@betswirl/ui on npm</strong></a></p><h2 id="h-the-5-minute-integration-challenge" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0">The 5-Minute Integration Challenge</h2><p>We wanted to prove that integrating BetSwirl games could be stupidly simple. So we set ourselves a challenge: from zero to deployed casino dApp in 5 minutes. Here's what happened.</p><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://img.paragraph.com/cdn-cgi/image/format=auto/https://storage.googleapis.com/papyrus_images/36eed1b2cc729fa185c09e683e81f539.png" blurdataurl="data:image/png;base64,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" nextheight="1950" nextwidth="3865" class="image-node embed"><figcaption htmlattributes="[object Object]" class="hide-figcaption"></figcaption></figure><br><h3 id="h-minute-1-create-the-app" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0">Minute 1: Create the App</h3><pre data-type="codeBlock" text="npm create vite@latest betswirl-ui-react-demo -- --template react-ts
cd betswirl-ui-react-demo
npm install"><code>npm create vite@latest betswirl<span class="hljs-operator">-</span>ui<span class="hljs-operator">-</span>react<span class="hljs-operator">-</span>demo <span class="hljs-operator">-</span><span class="hljs-operator">-</span> <span class="hljs-operator">-</span><span class="hljs-operator">-</span>template react<span class="hljs-operator">-</span>ts
cd betswirl<span class="hljs-operator">-</span>ui<span class="hljs-operator">-</span>react<span class="hljs-operator">-</span>demo
npm install</code></pre><p>Vite + React + TypeScript. The holy trinity of modern web dev.</p><h3 id="h-minute-2-install-betswirl" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0">Minute 2: Install BetSwirl</h3><pre data-type="codeBlock" text="npm install @betswirl/ui"><code>npm install @betswirl/ui</code></pre><p>One package. All the games. All the Web3 complexity handled.</p><h3 id="h-minute-3-wire-up-the-providers" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0">Minute 3: Wire Up the Providers</h3><p>Remember our provider jungle from the Storybook days? We cleaned it up:</p><pre data-type="codeBlock" text="// main.tsx
// Full code: https://github.com/chainhackers/betswirl-ui-react-demo/blob/main/src/main.tsx
// ... imports for React, Wagmi, OnchainKit, BetSwirl
import '@betswirl/ui/styles.css'  // Don't forget this!

const queryClient = new QueryClient()
const config = createConfig({
  chains: [base],
  transports: { [base.id]: http() },
})

const onChainKitConfig: AppConfig = {
  wallet: {
    display: &quot;modal&quot;,
  }
}

createRoot(document.getElementById('root')!).render(
  &lt;WagmiProvider config={config}&gt;
    &lt;QueryClientProvider client={queryClient}&gt;
      &lt;OnchainKitProvider chain={base} config={onChainKitConfig}&gt;
        &lt;BetSwirlSDKProvider initialChainId={base.id}&gt;
          &lt;App /&gt;
        &lt;/BetSwirlSDKProvider&gt;
      &lt;/OnchainKitProvider&gt;
    &lt;/QueryClientProvider&gt;
  &lt;/WagmiProvider&gt;
)
"><code><span class="hljs-comment">// main.tsx</span>
<span class="hljs-comment">// Full code: https://github.com/chainhackers/betswirl-ui-react-demo/blob/main/src/main.tsx</span>
<span class="hljs-comment">// ... imports for React, Wagmi, OnchainKit, BetSwirl</span>
<span class="hljs-keyword">import</span> <span class="hljs-string">'@betswirl/ui/styles.css'</span>  <span class="hljs-comment">// Don't forget this!</span>

<span class="hljs-title">const</span> <span class="hljs-title">queryClient</span> <span class="hljs-operator">=</span> <span class="hljs-title"><span class="hljs-keyword">new</span></span> <span class="hljs-title">QueryClient</span>()
<span class="hljs-title">const</span> <span class="hljs-title">config</span> <span class="hljs-operator">=</span> <span class="hljs-title">createConfig</span>({
  <span class="hljs-title">chains</span>: [<span class="hljs-title">base</span>],
  <span class="hljs-title">transports</span>: { [<span class="hljs-title">base</span>.<span class="hljs-title">id</span>]: <span class="hljs-title">http</span>() },
})

<span class="hljs-title">const</span> <span class="hljs-title">onChainKitConfig</span>: <span class="hljs-title">AppConfig</span> <span class="hljs-operator">=</span> {
  <span class="hljs-title">wallet</span>: {
    <span class="hljs-title">display</span>: <span class="hljs-string">"modal"</span>,
  }
}

<span class="hljs-title">createRoot</span>(<span class="hljs-title">document</span>.<span class="hljs-title">getElementById</span>(<span class="hljs-string">'root'</span>)<span class="hljs-operator">!</span>).<span class="hljs-title">render</span>(
  <span class="hljs-operator">&lt;</span><span class="hljs-title">WagmiProvider</span> <span class="hljs-title">config</span><span class="hljs-operator">=</span>{<span class="hljs-title">config</span>}<span class="hljs-operator">&gt;</span>
    <span class="hljs-operator">&lt;</span><span class="hljs-title">QueryClientProvider</span> <span class="hljs-title">client</span><span class="hljs-operator">=</span>{<span class="hljs-title">queryClient</span>}<span class="hljs-operator">&gt;</span>
      <span class="hljs-operator">&lt;</span><span class="hljs-title">OnchainKitProvider</span> <span class="hljs-title">chain</span><span class="hljs-operator">=</span>{<span class="hljs-title">base</span>} <span class="hljs-title">config</span><span class="hljs-operator">=</span>{<span class="hljs-title">onChainKitConfig</span>}<span class="hljs-operator">&gt;</span>
        <span class="hljs-operator">&lt;</span><span class="hljs-title">BetSwirlSDKProvider</span> <span class="hljs-title">initialChainId</span><span class="hljs-operator">=</span>{<span class="hljs-title">base</span>.<span class="hljs-title">id</span>}<span class="hljs-operator">&gt;</span>
          <span class="hljs-operator">&lt;</span><span class="hljs-title">App</span> <span class="hljs-operator">/</span><span class="hljs-operator">&gt;</span>
        <span class="hljs-operator">&lt;</span><span class="hljs-operator">/</span><span class="hljs-title">BetSwirlSDKProvider</span><span class="hljs-operator">&gt;</span>
      <span class="hljs-operator">&lt;</span><span class="hljs-operator">/</span><span class="hljs-title">OnchainKitProvider</span><span class="hljs-operator">&gt;</span>
    <span class="hljs-operator">&lt;</span><span class="hljs-operator">/</span><span class="hljs-title">QueryClientProvider</span><span class="hljs-operator">&gt;</span>
  <span class="hljs-operator">&lt;</span><span class="hljs-operator">/</span><span class="hljs-title">WagmiProvider</span><span class="hljs-operator">&gt;</span>
)
</code></pre><p>Same providers we've been using, but now they're properly packaged and documented.</p><h3 id="h-minute-4-add-a-game" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0">Minute 4: Add a Game</h3><pre data-type="codeBlock" text="// App.tsx
// Full code: https://github.com/chainhackers/betswirl-ui-react-demo/blob/main/src/App.tsx
import { CoinTossGame } from '@betswirl/ui'

function App() {
  return (
    &lt;div className=&quot;App&quot;&gt;
      &lt;h1&gt;My Casino dApp&lt;/h1&gt;
      &lt;div style={{ margin: '2rem 0' }}&gt;
        &lt;CoinTossGame /&gt;
      &lt;/div&gt;
    &lt;/div&gt;
  )
}
"><code><span class="hljs-comment">// App.tsx</span>
<span class="hljs-comment">// Full code: https://github.com/chainhackers/betswirl-ui-react-demo/blob/main/src/App.tsx</span>
<span class="hljs-keyword">import</span> { <span class="hljs-title">CoinTossGame</span> } <span class="hljs-title"><span class="hljs-keyword">from</span></span> <span class="hljs-string">'@betswirl/ui'</span>

<span class="hljs-title"><span class="hljs-keyword">function</span></span> <span class="hljs-title">App</span>() {
  <span class="hljs-title"><span class="hljs-keyword">return</span></span> (
    <span class="hljs-operator">&lt;</span><span class="hljs-title">div</span> <span class="hljs-title">className</span><span class="hljs-operator">=</span><span class="hljs-string">"App"</span><span class="hljs-operator">&gt;</span>
      <span class="hljs-operator">&lt;</span><span class="hljs-title">h1</span><span class="hljs-operator">&gt;</span><span class="hljs-title">My</span> <span class="hljs-title">Casino</span> <span class="hljs-title">dApp</span><span class="hljs-operator">&lt;</span><span class="hljs-operator">/</span><span class="hljs-title">h1</span><span class="hljs-operator">&gt;</span>
      <span class="hljs-operator">&lt;</span><span class="hljs-title">div</span> <span class="hljs-title">style</span><span class="hljs-operator">=</span>{{ <span class="hljs-title">margin</span>: <span class="hljs-string">'2rem 0'</span> }}<span class="hljs-operator">&gt;</span>
        <span class="hljs-operator">&lt;</span><span class="hljs-title">CoinTossGame</span> <span class="hljs-operator">/</span><span class="hljs-operator">&gt;</span>
      <span class="hljs-operator">&lt;</span><span class="hljs-operator">/</span><span class="hljs-title">div</span><span class="hljs-operator">&gt;</span>
    <span class="hljs-operator">&lt;</span><span class="hljs-operator">/</span><span class="hljs-title">div</span><span class="hljs-operator">&gt;</span>
  )
}
</code></pre><p>That's it. The game handles wallet connection, betting, on-chain randomness, everything.</p><h3 id="h-minute-5-deploy-to-vercel" class="text-2xl font-header !mt-6 !mb-4 first:!mt-0">Minute 5: Deploy to Vercel</h3><p>Push to GitHub, connect to Vercel, click deploy. Done.</p><p><span data-name="controller" class="emoji" data-type="emoji">🎮</span><strong> </strong><a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://betswirl-ui-react-demo.vercel.app"><strong>Live Demo</strong></a><br><span data-name="laptop" class="emoji" data-type="emoji">💻</span><strong> </strong><a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://github.com/chainhackers/betswirl-ui-react-demo"><strong>GitHub Repo</strong></a></p><h2 id="h-beyond-the-5-minutes-all-games-live" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0">Beyond the 5 Minutes: All Games Live</h2><p>Want to see all three games in action? Check out our full demo showcasing CoinToss, Dice, and our latest addition:</p><br><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://img.paragraph.com/cdn-cgi/image/format=auto/https://storage.googleapis.com/papyrus_images/eca728f3060e5206a9f0c57cdceb7bec.png" blurdataurl="data:image/png;base64,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" nextheight="1950" nextwidth="3865" class="image-node embed"><figcaption htmlattributes="[object Object]" class="hide-figcaption"></figcaption></figure><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://img.paragraph.com/cdn-cgi/image/format=auto/https://storage.googleapis.com/papyrus_images/b2a1e271f706acf80bb602ab3fe4e240.png" blurdataurl="data:image/png;base64,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" nextheight="956" nextwidth="1546" class="image-node embed"><figcaption htmlattributes="[object Object]" class="hide-figcaption"></figcaption></figure><p><span data-name="slot_machine" class="emoji" data-type="emoji">🎰</span><strong> </strong><a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out underline" href="https://betswirl-react-demo.vercel.app/"><strong>Play All Games Demo</strong></a><br><span data-name="high_voltage" class="emoji" data-type="emoji">⚡</span><strong> </strong><a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out underline" href="https://stackblitz.com/github/chainhackers/betswirl-ui-react-demo"><strong>Edit on StackBlitz</strong></a></p><h2 id="h-whats-different-now" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0">What's Different Now?</h2><p>Remember our first post about "stuffing an elephant into a telephone booth"? Well, we found a way. The components are:</p><ul><li><p><strong>Self-contained</strong>: All Web3 logic bundled</p></li><li><p><strong>Theme-aware</strong>: Light/dark/custom themes just work</p></li><li><p><strong>Mobile-ready</strong>: Finally sorted the responsive layouts</p></li><li><p><strong>TypeScript-first</strong>: Full type safety, autocomplete heaven</p></li></ul><h2 id="h-whats-next" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0">What's Next?</h2><ul><li><p><strong>Keno</strong></p></li><li><p><strong>Token Selector</strong>: v0.2.0 target</p></li></ul><h2 id="h-for-the-builders" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0">For the Builders</h2><p>If you're thinking about adding games to your dApp:</p><ol><li><p><strong>Try the demo first</strong>: See if the UX fits your vibe</p></li><li><p><strong>Check your audience</strong>: BetSwirl supports multiple chains, but components work with one chain at a time</p></li><li><p><strong>Revenue share is automatic</strong>: 30% of house edge to you, handled on-chain (<a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out underline" href="https://www.betswirl.com/affiliate/info">see docs</a>)</p></li><li><p><strong>Join our Discord</strong>: We're helping early integrators personally</p></li></ol><h2 id="h-ship-it-culture" class="text-3xl font-header !mt-8 !mb-4 first:!mt-0">Ship It Culture</h2><p>A few weeks ago, we were picking cat backgrounds in Figma. Now we have a production npm package ready for integration. The lesson? Ship early, iterate in public, see what happens.</p><p><strong>Ready to build?</strong></p><pre data-type="codeBlock" text="npm install @betswirl/ui"><code>npm install @betswirl/ui</code></pre><p><strong>Need help?</strong></p><ul><li><p>Twitter: <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://x.com/ChainHackerClan">@ChainHackerClan</a></p></li><li><p>Farcaster channel: <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://farcaster.xyz/~/channel/betswirl">/betswirl</a></p></li><li><p>Discord: <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://discord.gg/betswirl">Join the BetSwirl builders</a></p></li></ul><br>]]></content:encoded>
            <author>chainhacker@newsletter.paragraph.com (ChainHacker)</author>
            <category>react</category>
            <category>betswirl</category>
            <category>sdk</category>
            <category>devtools</category>
            <category>web3</category>
            <enclosure url="https://img.paragraph.com/cdn-cgi/image/format=auto/https://storage.googleapis.com/papyrus_images/ee5626bc4722fd3a2bf2a099f7e1e7d6.jpg" length="0" type="image/jpg"/>
        </item>
        <item>
            <title><![CDATA[🎲 Rolling the Dice: Second Game Component Ready]]></title>
            <link>https://paragraph.com/@chainhacker/rolling-the-dice</link>
            <guid>ajKNiFvXvgLpXu8VD1q6</guid>
            <pubDate>Mon, 09 Jun 2025 00:01:41 GMT</pubDate>
            <description><![CDATA[This week, we didn’t just roll the dice—we shipped it. DiceGame is live, and it’s the second fully working, on-chain BetSwirl game component we’ve added to the SDK.]]></description>
            <content:encoded><![CDATA[<p>This week, we didn’t just roll the dice—we shipped it. DiceGame is live, and it’s the second fully working, on-chain BetSwirl game component we’ve added to the SDK.</p><h2 id="h-brainpower-bonus" class="text-3xl font-header">Brainpower Bonus</h2><p>We got backup: a core dev from BetSwirl joined the party and walked us through how to structure React hooks for cleaner, custom UI integration. Hooks, providers, separation of concerns—it clicked. Expect a blog post or recipe guide soon, especially for those building skins and extensions.</p><h2 id="h-visual-boost" class="text-3xl font-header">Visual Boost</h2><p>We added a bunch of new backgrounds—ranging from tasteful to completely chaotic—and started putting Playwright + Synpress to work for proper E2E tests. It’s no longer just a weekend project; we’re locking in real infra.</p><h2 id="h-the-game" class="text-3xl font-header">The Game</h2><p>Pick a number from 1 to 99 and bet that the dice will roll under it. Pick 20? You’ve got a 20% chance of winning ~5x. Pick 80? You’ve got an 80% chance of winning ~1.25x. Classic high risk vs low risk.</p><h2 id="h-one-sdk-two-games" class="text-3xl font-header">One SDK, Two Games</h2><p>DiceGame follows the same architecture as CoinToss. Same GameFrame, same theming system, same <code>usePlaceBet</code> logic behind the scenes. Swapping games is as simple as changing one component name. Yes, you can render both in the same app.</p><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://img.paragraph.com/cdn-cgi/image/format=auto/https://storage.googleapis.com/papyrus_images/beacf7a18db729bfac41b9b2e7835c60.png" blurdataurl="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAANCAIAAABHKvtLAAAACXBIWXMAABCcAAAQnAEmzTo0AAAEmUlEQVR4nH2QbUwTBxiA3ywa7ERBJoHNbJmTETUsTkVAUfzI3IzOObMYndMxtwkqDm0Qa4FeW6gONWJEIm4FKz2wyNTKUavXcqWUUirn1fJRWim9nqXUAomiBaQgdCn8XLLk+fHmeZ+8P17w+YaHR0amGX358tXA4ODA4OAbn294ZPTV0NDoaFB6Xgx4+we8/f2vXwc3//XT/cgbn2/Ge/sHPF7vy6GhNz4fdFosnRZbIBDw+Ybb2zsbW/VEcxPDuNra2i1d9kAgoGumiopKFAqlFK22dFnN5jabnQ4EAgRhLL5yDcMUlVU13Xb7E5Kip331ffX5SyXyO3LZrZoeew9UyGSbU5ZBVAx8mQzLkyD6U4j+5PT5qxW3qpLXxkB0LCt+IyxNhEUx8NHis9dvisv+il+zBKJiQ+JTIDYeFi2Gj5eUSm4LiNuQtxdEqcjdop9uIJCfCnl7b7SogCssAIDl23bvSjv2zW8Z67Z/O4cFew4fzcrNAYDPt323+dCBhP374zavB4B0Tu7h48cAYMWufbuP/LHl5/R1X20FgDPIuURUBHti4feE7cWZ0aJUSEuEg3FZCgkUXrwA77GOZHGOn+JkcrI5/PzwRR9mZGYIzhZASNgh9sn9Gb/+ciIt/czJWR+EIwIem5MN8xZk5uQdy8o+yeGwc/IgfO6VwsK1skI4sBzY2yB7J3C/hxNb4WDcKSUKNyTlC6MiWJERMD8M5oRCaBiwZp/h88Xi6/Mi3g+NioQFkcAKg/kLIGTWn5eKii5fmBMewopcCOERMHsuzA+HUJb4mngfVgpH1gdP5/8IOT9A1g44uqFQKwd3n0ej0ajUTY0GijK1mc1teiPZQzMM49JotI+UiqoaJWnqCHpDq5Ppc9BOjUajVCpkd3HK1D7j3Z4Xz3ocSoKoqb0vleMGk8n49KmBojxeLzCMC8MUCiVhNJLkkycUZSJJ0kE7HTSNYQpcra4nmoIrkmwxtjoZl81mn/GaBkNrKzXTu/s8lq4uDHugVtc3NBhI0kRRVOvjoAdXb+99rO4hjitxHFfhNfcelJRWPOt+5mSYApHo8tXi6jv/VFXLSsvQcxeLbTabw0GjlZXyWkxNELgKv1WjuC6Wunp7rTabBK2QY7V4veqh6hF6u+5vidTtdgdfRKi0eq1BrzPoGvQ6bQsmV9rtDobpvVN171GtSl1H4LUqZS0uLZfRDqeDZnBlvV5r0GmbddpmTb1OgT109bpph1On1ptazGQTZTKaWxpIAm/09L2AHprOv3kOkQh5ZQKeWCC8WcAr45ssbR12awJnUwJ30yr2uhUn1iZwN67O3mDoeGzt6eaV8QVSEU8sQMoFAmkBUp5PM4yGalx1OmU9/+v4rA3x2SlJeVu2Cnda7FZwup4LURGCCnMlCFeC8CoEPJRvtrV32q1f5CQv5SbGsFfGsFcu5SbG5SQ3dxi7HXYeykdQIVeC5EoQpFLIR/Odruf1Js1nnNXLuEkzfQx3TRJ/i5XuhrG3Y1OTU+NvJybG302Mv3s3FsTvH/f7x6cmpoJMTjM9T3v/TPP//eT4ZCAQGPP7/wWqyfmtkiXU7wAAAABJRU5ErkJggg==" nextheight="810" nextwidth="2058" class="image-node embed"><figcaption htmlattributes="[object Object]" class="hide-figcaption"></figcaption></figure><br><h2 id="h-whos-it-for" class="text-3xl font-header">Who’s It For?</h2><p>We’ve seen interest from memecoin communities, token projects, and even DeFi dashboards. The use case is always the same: plug in a game, give users something fun to do, and earn a revenue share without building a casino from scratch.</p><p>Each game call is fully on-chain, randomness is handled by Chainlink VRF, and users can verify every outcome.</p><h2 id="h-where-we-are-now" class="text-3xl font-header">Where We Are Now</h2><p>Storybook hosts both games with full theme toggles and wallet integration. Want to test them? Go for it. The SDK is nearly package-ready—we're preparing the npm release and polishing the final DX.</p><h2 id="h-coming-up" class="text-3xl font-header">Coming Up</h2><ul><li><p><span data-name="package" class="emoji" data-type="emoji">📦</span> npm packaging + docs</p></li><li><p><span data-name="currency_exchange" class="emoji" data-type="emoji">💱</span> Token selector (currently only ETH)</p></li><li><p><span data-name="bullseye" class="emoji" data-type="emoji">🎯</span> Next games: Roulette + Keno</p></li><li><p><span data-name="android" class="emoji" data-type="emoji">📱</span> Farcaster frame experiments</p></li></ul><p>We’re still early, but getting more confident with each release. Curious what the web will do with on-chain games this easy to drop in.</p><p>Try the games: <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://demo.betswirl-sdk.chainhackers.xyz/?path=/docs/game-dicegame--docs">Storybook link</a></p><hr><p><em>Want to build with us? Ping us anytime:</em><br>Twitter: <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://x.com/ChainHackerClan">https://x.com/ChainHackerClan</a><br>Farcaster: <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://farcaster.xyz/chainhacker">https://farcaster.xyz/chainhacker</a></p>]]></content:encoded>
            <author>chainhacker@newsletter.paragraph.com (ChainHacker)</author>
            <category>betswirl</category>
            <category>sdk</category>
            <category>devtools</category>
            <category>web3</category>
            <enclosure url="https://img.paragraph.com/cdn-cgi/image/format=auto/https://storage.googleapis.com/papyrus_images/a5e9677d3be7057659fff86440cc6b15.jpg" length="0" type="image/jpg"/>
        </item>
        <item>
            <title><![CDATA[First Game Component Goes Live!]]></title>
            <link>https://paragraph.com/@chainhacker/game-component-goes-live</link>
            <guid>GfnNydwjDvGbUk0BlPvs</guid>
            <pubDate>Wed, 28 May 2025 18:05:32 GMT</pubDate>
            <description><![CDATA[We’re back with another dose of dev progress. This week’s highlight? You can now play the game right in the browser via our demo Storybook — no need to integrate anything just yet. After last week’s layout and styling adventures, we wrapped the entire game flow into two clean exports.]]></description>
            <content:encoded><![CDATA[<p>We’re back with another dose of dev progress. This week’s highlight? You can now <strong>play the game</strong> right in the browser via our <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="http://demo.betswirl-sdk.chainhackers.xyz/">demo Storybook</a> — no need to integrate anything just yet.</p><h2 id="h-what-we-shipped" class="text-3xl font-header">What We Shipped</h2><p>After last week’s layout and styling adventures, we wrapped the entire game flow into two clean exports:</p><ul><li><p>A single hook: <code>usePlaceBet</code></p></li><li><p>A React component: <code>&lt;CoinTossGame /&gt;</code></p></li></ul><p>All the on-chain complexity — randomness, wallet checks, error handling, event listening — is baked in. Just import and go.</p><br><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://img.paragraph.com/cdn-cgi/image/format=auto/https://storage.googleapis.com/papyrus_images/abbd9461fa2f3e4e3a797615608eb054.png" blurdataurl="data:image/png;base64,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" nextheight="1599" nextwidth="2058" class="image-node embed"><figcaption htmlattributes="[object Object]" class="hide-figcaption"></figcaption></figure><h2 id="h-example-integration-is-tiny" class="text-3xl font-header">Example: Integration is Tiny</h2><p>Here's all it takes to wire up the game component in your app:</p><pre data-type="codeBlock" text="const config = createConfig({
  chains: [base],
  transports: { [base.id]: http() },
})

export function CoinTossGamePage() {
  return (
    <WagmiProvider config={config}>
      <QueryClientProvider client={new QueryClient()}>
        <OnchainKitProvider chain={base}>
          <BettingConfigProvider value={{ affiliate: &quot;0x0&quot; }}>
            <CoinTossGame />
          </BettingConfigProvider>
        </OnchainKitProvider>
      </QueryClientProvider>
    </WagmiProvider>
  )
}
"><code>const config <span class="hljs-operator">=</span> createConfig({
  chains: [base],
  transports: { [base.id]: http() },
})

export <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">CoinTossGamePage</span>(<span class="hljs-params"></span>) </span>{
  <span class="hljs-keyword">return</span> (
    <span class="hljs-operator">&lt;</span>WagmiProvider config<span class="hljs-operator">=</span>{config}<span class="hljs-operator">&gt;</span>
      <span class="hljs-operator">&lt;</span>QueryClientProvider client<span class="hljs-operator">=</span>{<span class="hljs-keyword">new</span> QueryClient()}<span class="hljs-operator">&gt;</span>
        <span class="hljs-operator">&lt;</span>OnchainKitProvider chain<span class="hljs-operator">=</span>{base}<span class="hljs-operator">&gt;</span>
          <span class="hljs-operator">&lt;</span>BettingConfigProvider value<span class="hljs-operator">=</span>{{ affiliate: <span class="hljs-string">"0x0"</span> }}<span class="hljs-operator">&gt;</span>
            <span class="hljs-operator">&lt;</span>CoinTossGame <span class="hljs-operator">/</span><span class="hljs-operator">&gt;</span>
          <span class="hljs-operator">&lt;</span><span class="hljs-operator">/</span>BettingConfigProvider<span class="hljs-operator">&gt;</span>
        <span class="hljs-operator">&lt;</span><span class="hljs-operator">/</span>OnchainKitProvider<span class="hljs-operator">&gt;</span>
      <span class="hljs-operator">&lt;</span><span class="hljs-operator">/</span>QueryClientProvider<span class="hljs-operator">&gt;</span>
    <span class="hljs-operator">&lt;</span><span class="hljs-operator">/</span>WagmiProvider<span class="hljs-operator">&gt;</span>
  )
}
</code></pre><p>That’s it. If your wallet is connected, you can play. If not, OnchainKit handles it.</p><h2 id="h-developer-notes" class="text-3xl font-header">Developer Notes</h2><ul><li><p>Most stable: Desktop browsers with browser wallets (e.g. Coinbase Wallet, MetaMask)</p></li><li><p>Mobile: Coming soon — we’re starting Farcaster Frame support!</p></li><li><p>Also keeping an eye on <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://www.base.org/builders/minikit">Base minikit</a> — hoping to plug into that as well.</p></li></ul><h2 id="h-next-up" class="text-3xl font-header">Next Up</h2><p>In the coming week:</p><ul><li><p><span data-name="package" class="emoji" data-type="emoji">📦</span> Publish the first npm package for easy component installs</p></li><li><p><span data-name="art" class="emoji" data-type="emoji">🎨</span> Polish token selection and mobile layout</p></li><li><p><span data-name="android" class="emoji" data-type="emoji">📱</span> Begin Farcaster frame integration using <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://www.base.org/builders/minikit">Base minikit</a></p></li></ul><p>The component is real, live, and working—no mockups. And it’s not just for meme sites. Anyone building with on-chain vibes can plug this in and start earning from gameplay.</p><p>More soon!</p>]]></content:encoded>
            <author>chainhacker@newsletter.paragraph.com (ChainHacker)</author>
            <enclosure url="https://img.paragraph.com/cdn-cgi/image/format=auto/https://storage.googleapis.com/papyrus_images/192b2139f35c922afdfe055c9cc7dee0.jpg" length="0" type="image/jpg"/>
        </item>
        <item>
            <title><![CDATA[Simplifying Casino Games for Mobile]]></title>
            <link>https://paragraph.com/@chainhacker/simplifying-casino-games-for-mobile</link>
            <guid>lIobDmzWPOLPP3FJ90IO</guid>
            <pubDate>Wed, 14 May 2025 00:17:18 GMT</pubDate>
            <description><![CDATA[Trying to fit a decentralized Web3 casino into every corner of the internet is like stuffing an elephant into a telephone booth.]]></description>
            <content:encoded><![CDATA[<h2 id="h-the-elephant-in-the-telephone-booth" class="text-3xl font-header">The Elephant in the Telephone Booth</h2><p>Trying to fit a decentralized Web3 casino into every corner of the internet is like stuffing an elephant into a telephone booth. <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://betswirl.com/">BetSwirl</a>, already killing it in decentralized gaming, is making it possible with reusable components. Now, developers can slap these components into any webpage and bring decentralized gaming to their users without breaking a sweat. Here’s the scoop on how they’re doing it.</p><h2 id="h-cooking-up-the-vibe" class="text-3xl font-header">Cooking Up the Vibe</h2><p>We kicked off with our imaginary memecoin hats on, dreaming up quirky backgrounds—wacky cats, space apes, and more—to set the stage for the memecoiners’ vibe. The first token in the mix? Just ETH on Base, keeping it simple yet powerful. </p><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://img.paragraph.com/cdn-cgi/image/format=auto/https://storage.googleapis.com/papyrus_images/fcfbff509b91b55ff9efcaf0415a80fb.jpg" blurdataurl="data:image/png;base64,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" nextheight="853" nextwidth="1280" class="image-node embed"><figcaption htmlattributes="[object Object]" class="hide-figcaption"></figcaption></figure><hr><h2 id="h-storybook-magic" class="text-3xl font-header">Storybook Magic</h2><p>As components piled up, <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://storybook.js.org/">Storybook</a> became our hero. It let us test "CoinToss" in light and dark themes, tweak designs live, and deploy demos in a snap—perfect for keeping the team in sync.</p><h2 id="h-styling-showdowns" class="text-3xl font-header">Styling Showdowns</h2><p>Enter Coinbase’s <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://github.com/coinbase/onchainkit">OnchainKit</a> for wallet magic—and a styling clash with <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://tailwindcss.com/">Tailwind CSS</a>. Overlapping styles broke our design until Tailwind layers stepped in. By isolating OnchainKit’s styles, we kept "CoinToss" looking sharp.</p><h2 id="h-lessons-learned" class="text-3xl font-header">Lessons Learned</h2><ul><li><p><strong>Integrate Early</strong>: Digging into OnchainKit docs sooner would’ve saved us rework.</p></li><li><p><strong>Layer Up</strong>: Tailwind layers are our new go-to for CSS peace.</p></li><li><p><strong>Hackathon Mode</strong>: Rapid prototyping catches hiccups fast.</p></li></ul><h2 id="h-sneak-peek" class="text-3xl font-header">Sneak Peek</h2><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://img.paragraph.com/cdn-cgi/image/format=auto/https://storage.googleapis.com/papyrus_images/8b2857ab05302daccf2f9282173a1d48.png" blurdataurl="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAMCAIAAACMdijuAAAACXBIWXMAAAsTAAALEwEAmpwYAAAENklEQVR4nC2Py2vbdgDH5TixbMmSZUuRFNtSZOv908Oyno4lx3Gy1EmbrLSHElrauglb06yPrIe1Y6W0exSalsHYYLDLupYyFhgbZbeywdihh9ARKLv1sD9gl8Fgo1A0ygbf0+fw+fCFREk1zRZRosbGcjhGKKqnWzFNswW0WKlUcjkYgqBsNlvEKjhBw3AeRYsEUZ54xTMlrE5TDE1PYhheZTkg6QSCF/OkrRwN7XZgerKoQpNys2wqsCFQiVnrOdWlGbzjUZKQg9FMoQyNI9k8juBlkqYnUALOF/IIjpEUVsKKJaJI8HmcLpTosbFC80DcvbGlXVwDZ15PtjY718/2P7pkra1AWB/gC4b5wer8vXcW7r27/OXG+7trzKwGweUWYAiGQWtamVOKbA0q1zN5YoKYROoNnGEFYFOymyHl8UkZhqnk/vVL6W9b6dPNR7fPf759Lt27kO4vP/4Mgv1GPhB6u6N//tj66dH6zV9uPN8/TnT5VwGTIVh2GNdWwvoETkJFegwty7opqka9IXGiLOm2pBsNFYh1Mfri6mb65MxfP158sff2i6frf/98Nn0y+O42NN6YGge89+nhP/dOPX44Sr66IG73CzPNTA6DMAYqkqth9YBZh/I4hJJZhFAcR/E9WTccP4iTfpCsWH5gNEF8/8Z2+uxc+uuV9Pl76e9vpfvb6bPh408gjGUIaZq0NNiQiFmn9ppb8tUix2azuQJazKOlDIxn4OI4jI7BaDZXaLotKfRFRQdWy3L9sHfYcDygO/wwDj48717b6Ny84F5707p6un1zs3HkACRKmu+H0cxBTXX42nR9imsKIk1XEQQlKyRJskSpgiIIiqAogsL5AlB1oBuiqqvAVAzTjw9ppq1bbp3hbMlqSXoSDKer6hRB1cosS7KQqOjd3iDsxlE8azhty4scP2RqtQKCUBRDkhRJTpZKBEFUykQFQVFdBxowVGAaVstwXD9Z0UxbMx1elOK5hSiZC+Oe5fmW13GjmBdlSFL0hcWlTjyb9AZxkiwfPrl6ZK3GcViJ4Dienaq+GjtV48RpQaKoScm2Fbetm7bhuLbrB8kqsFq65fFCc2FxKU7+9ywePHbk2GlJVqFpoTGYG3ieH3USL4yiKJifn2erdeC4jh+Kii5Iit+JHT9Qgck3JEcDLQ3IugGsFrCcsLdq2G3guFWO7/f7YRT5YRREM57nLS4u8I0mZA83NnbT0cP08qP00rfp6Ot09OCl6A9dz+0kc5ppC5LS7Q16c4tm22uIsuR7UuhrwDSctgqsqH/UcDzDNORg+fSDl298k17+Pr3yQ3puN93YTe3hOlTXu9Gpu8GJnf7objLaCU/ciY7fogXT8fxubyAqQJBUL+p2kjkZmLwgSu227Lb/eyDrhhcfApZjWDYjWsHxW9HJO73RzmD9TvfkTnTqY07v/guAPPvxKprYjgAAAABJRU5ErkJggg==" nextheight="768" nextwidth="2010" class="image-node embed"><figcaption htmlattributes="[object Object]" class="hide-figcaption"></figcaption></figure><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://img.paragraph.com/cdn-cgi/image/format=auto/https://storage.googleapis.com/papyrus_images/4e3485eaa82798e83139f1d6d97f0e77.png" blurdataurl="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAOCAIAAADBvonlAAAACXBIWXMAABCcAAAQnAEmzTo0AAAFP0lEQVR4nCWR/VPSBxyAPz/sh21dd7X9Upzbul62yq1rK8vS1NaLbrLMzHdUFHzBExJMUBS+8iYvCgLyjomlRlMRBSPRwalJOvMlO1/my7BECAVrt7d29cuu7e75B57ngZXV1X77gMXat+7xsBFD212TxWrt7bWse/1XEeUhLKOQLYkq4ezAVKJy6Kgc+i5MBSqHHkUTh+Qy/ucAHvm8oGY/HtmbyzhC4MSU18XRZSlcbUqt3jQ8CRter8XaZzR2bAb8VTR9c7PRZOq29Fqml9bgUFwqIWdjQdFsoML+KNgVBh+Hwc4T8Ek0YMshJBL2nAXUf3x6DkLj4Nt0OHUVvk6AWBxcwkLiDaq+A968fev1+V/4/c6R6cDL7UAw6PNvvdp+OeP2wan4OUfFn56mv31SbAUejl6BL2LhTAokFUN+BVzMhmNoOJkIJ69BWNK5ZAyfe7OVR/gyIR3C0wCdD3gGp70H1j0+k6nHYu0TK1rtAyNPZmenpmdnZ578/Mva+/GYyU6Ce5wVmK8hcwogPAMiUiGTHClQF7Z0EFrN8SL9zuIaSCPD6esQchI+++aDI0dH7apIKhOuFQO+urLFBF7fps02aO6x2mwPOjrNwyOPhkcejbrGNwOBSBIDYWDnrXlz1qIzJSRIJEIa8T0iI5IrZff2aYYfUu/1RLMUl2s1kIA/GBG948DhWPTF1y+6VxZMiIyfKJSWt/WCfzMolsilMoVWZzAYbtdLVOnYsho2dzsYIDeqIIOIZdIiKFTAUABbtjefdozEQCO1RVIpSducLm2KRepQJVVAqE4ryC/ISuLRi4bNQt+MQaLih1bV8U397wwaFRqVWq9S69mcWovVNj4x5XKNr2346GqNsEl1nV0HePqHxdWAo0JyMaBxEJ8bmk0UIeEy4m4sGfdVtQRVzj1dRI6+cCEtCV2CT61hlITROGpr1/ziLASC2yZTj/Fe131b/+07d+12x9LyrwuLyx6vj6XRSvQq8S0VhidOQASXEUE4lYvCUeByYQHuI3UOVByHHBTE3Kw6z5NfYonOlzPDC0hRuYR4Mq2wXj4xM+h2z4F/c8tud4yNP15YXJ54PN1u/NHcY7HbHb+9CnZZWr+nssn1DXi+GMsV4fmSPKH8KqsuhsrnYUGdDcI4SAcoLMoqUhqoMjlDp00VyBg6HVOn5emUZod1dGry3YNeq+0n5+ioa8I54mozdnaaLX12x3Zwyz5grFU3SvUKLJuPprEJAjGpXkYQiEuVBlrWwSyA7wBSAYS3dEylAscT4XgitkpRUtdQLKgfGmzbejbsWZ9/l8husw4NPlhbWVx4Ov2gzzL20LnxbNXj9bmc7ZOuezf4IrZCViYS/VCGZDC4TLm8q7tF02YozIxF0iLVugZSvezKTeYhbBmBW2u1GIxdTQKNVCAXDzq75+anYMMf/KG0MQIvRlO0scXSyDzh4WRGcpl6yb0u08gctmZ9i0KilU2OGGsbxZxGMU8pvU5jZdDZCYz6NI6EyBdimexzRTRMBUITCoUqcVu7miYUYirZDLnCPOiEwMs/YF8eQDzsyYTjpRCSA4CGfdhF90ZyGb1KwFFqJWK1xNFvsPTqWtsVpi5tg056Ko+yOxG/80ruCSwJx6zBI6wUKvMapTq7Eom/UXkWRwnNLIkhMtrtQ/DPm7cbXv/Kc++y27Ps9qw+964893p9m7//9Zpt6OA2tYlajNqObovd5nrkcAwN9A3ax8acRtt9RN9a2tBEazSIWowsfXul8natwSi601GlMBDr9aSGJor81ujTpX8BY0++H84gM0gAAAAASUVORK5CYII=" nextheight="810" nextwidth="1806" class="image-node embed"><figcaption htmlattributes="[object Object]" class="hide-figcaption"></figcaption></figure><figure float="none" data-type="figure" class="img-center" style="max-width: null;"><img src="https://img.paragraph.com/cdn-cgi/image/format=auto/https://storage.googleapis.com/papyrus_images/fc1eb3743fa42bc93554abd47970cdfd.png" blurdataurl="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAOCAIAAADBvonlAAAACXBIWXMAABCcAAAQnAEmzTo0AAAFGUlEQVR4nBWR/VPSBwCHP7vddu3cbXVtu517aXeZS9eLuSt12ovT1ZplZ+S7TqezidMyyrda6lJRe/EFXyhRzFBRBCElDJGvgBCkQiYqLsVXFEGE3HbdNfth51/w3PM8mFtYUCiVBCFbNpurK7u6uiWETNbbR9jt9vWJx4aHVUZRwwthybO2ZD23ZFLInOhm6Hn3xngMvaBeL2C+EDfNyZqnetu07LqBBxcIZlz/vZzFAb5liL+s5rxcMmDFYiEImVAostlXy27yuVyRWCwW9UjXHdZZSc6TsghVpZ+sHH3F4FNA3DiuqUyUU6Pk1Gg5NVZ162d1efyTsphO8m5WBnqaIGahPh/sHC9tXfyc5IZ9rAcbG28sVtuqzaYeMqy9dNjt9lXbmtW2/upvm1GUaeBeEFPBTEVTGoqCIMh6R8dIVdFi1RWx6oqYJ2URT6sSlLdJgkw05qGzAo0FYFbghDMyPDDKOm8aFGDZbBX3SglCVs8SKpXDBsPkxMTk6Pjk2sqCriFskH6Wdxm8LJSH4XYwZOVfTnZSDJys6Ucli0SNWcW0Dt+d7EzvzX+7rxAcMnjpSPDFsQ9x/Ri0dfGzKi4s1jWZXCWR9svlClGPZHBoZGho5Kn2+b92m7DGLf8y2rK2cy6i5BRaUjHIDNa1krWcjGmi2va8xTHeatffX5AW8i9BmA12IjrIKPYFCaiPc56X5pp1fKzaHMxG1v2mFnY7j8fjNzSy0zNvVlTfXXdYxh7GUH5Ba+nb8io36W0ndf0xoyhD0ZTUx/xV2pgka05TNKcomsja+h+LQkDxx6NspO8D1R8ZX4OTvWOgwWtW1bxpwGKxW1o7Wlo7qmvuEYRidHRMqxtbWjYWpaGrFmoRJPdxKxNsmvtMb+q4IEXJTlGwf1cx9ov+hIZBmhalTfDCukp92df26hqDBFfdC0/iWgRqrmNKwYLd7hD3SoWPxDL5AF8gHFBqZmbnjEbT/OJUbCBkXKgeQidGUhSS49Fe8elffLKWk65hfiG4gtqfUHkUj2v3ELU+mlpv4tbeqkjkHQcvf0tXqauiNtA2LsSqbW1AqRkZ0U8b50b1E11CkaRPqlIPOV6aY4/gzgVoJVDxEBGEiFCo205OdVHGWlO6syGgoCUOuZ8gNwrFv2HwrlcPdVfCRwgGqkNAi0RDlotpmLf5QErIVRqtVqfXDOm6hI97JIRCpV1ZeRF6Bp5AnD9IfgjwRWI0WAUuA80fdFSjORX5O0AGrm5HRTpogaAdR1kAig/i0mebjJPvg5r0sUndsZlIqSAGVXLT3LRxclzRL302qFoxL60uzzJyPycF44ALwkNxPg5RoTjtgbKLaKehg45qMorP4k420qIRsxfJuxHnjDNOOPEudgFu29BddMQxKoJ51ZFY2E661pJA5cfms0NzmgLT6ElF3LUVk4YeXnXJKcQbIUeRHIMbfyCHjKhvEeqKCFeEuSPYFQFb4fPeJj7gexw9BM+dcHsL3p7ICncepJMsz3iwr7+CHxUuFBzMQ1AlfArgegW+hTbrkoGXoKFF0MKcyXs2lc+4ItEfoYdxaitOAz6AH/DdDni744ATvgEOAYe34NQ20Mn7huuiJzriHWMCvN54Y7bYZpetMybLjMkyv2ydX7YuWdY2/ntt03OWVPQpKUPLKeXmhdPJR+qSvfJJbtVJB2qTPUsi3WuSPS6f25ke4kL54aviCI+Cc/sf5ESO8KumJYyZfvqUpOqfxfH/AQkzwWUCTXrHAAAAAElFTkSuQmCC" nextheight="810" nextwidth="1806" class="image-node embed"><figcaption htmlattributes="[object Object]" class="hide-figcaption"></figcaption></figure><h2 id="h-try-it-yourself" class="text-3xl font-header">Try It Yourself</h2><p>Want to embed "CoinToss" in your own project? We’ve made it simple! We’ll be dropping documentation with step-by-step instructions on adding the game to any web page, and we’ll share the link to our example page once it’s live. Just remember, adding tokens to the bankroll is handled exclusively by the BetSwirl team.</p><h2 id="h-whats-next" class="text-3xl font-header">What’s Next?</h2><p>"CoinToss" is just the start—a reusable piece pre-set with chains like Base and tokens like BETS. We’re diving into OnchainKit, Base minikit, and Farcaster miniapps next. Ready to play? Check us out on <a target="_blank" rel="noopener noreferrer nofollow ugc" class="dont-break-out" href="https://github.com/BetSwirl/sdk">GitHub</a>, embed "CoinToss" yourself, or toss us your ideas. Join the ride!</p><p>Happy hacking!</p><br>]]></content:encoded>
            <author>chainhacker@newsletter.paragraph.com (ChainHacker)</author>
            <category>devlog</category>
            <category>web3</category>
            <category>betswirl</category>
            <category>chainhackers</category>
            <enclosure url="https://img.paragraph.com/cdn-cgi/image/format=auto/https://storage.googleapis.com/papyrus_images/f3570f30baaeb6c4467c5bf6709378b6.jpg" length="0" type="image/jpg"/>
        </item>
    </channel>
</rss>