Build log

Wallet-native dice on Solana

Garden Dice is a pay-per-roll die game on mainnet — not a casino with payouts, but a micropayment demo that is actually fun. This post covers the technical problems we hit shipping wallet pay in the browser and how we solved them.

Try it live — free demo roll, then pay from 0.001 SOL via wallet or Solana Pay QR.

What we built

Problem 1: browser RPC returns 403

Public api.mainnet-beta.solana.com rejects browser-origin POST requests with HTTP 403. Every wallet connect flow that calls getLatestBlockhash from client-side JavaScript fails silently in production — users see “transaction failed” with no obvious cause.

Fix: nginx same-origin proxy at /rpc strips the Origin header before forwarding to mainnet-beta. The frontend uses /rpc first, then solana-rpc.publicnode.com as fallback. Playwright regression tests run on every deploy so this cannot regress unnoticed.

Problem 2: mobile in-app browsers cannot connect wallets

Twitter, Discord, and Telegram embed their own WebViews. Wallet extensions do not inject there. “Connect Phantom” is a dead end for most mobile share traffic.

Fix: Solana Pay transfer URLs rendered as QR codes. User scans, pays in their wallet app, returns to the page. We poll the treasury for matching inbound transfers (“I've paid — detect & roll”) instead of requiring users to paste 88-character signatures. Paste-sig remains as fallback.

See the Solana Pay QR guide for the full flow.

Problem 3: trust before payment

Dice games trigger scam alarms. Users need to verify fairness before sending SOL.

Fix: commit-reveal scheme. Server publishes SHA-256(serverSeed) before payment. After the tx confirms, server reveals the seed; user recomputes the roll locally. Free demo rolls use the same algorithm without payment so users can test verification first.

Deep dive: provably fair dice guide with inline demo.

Problem 4: share traffic lands on the wrong page

Social crawlers read OG meta tags, not JavaScript. A bare ?roll=4 query on /dice/ gets a generic preview. Viral shares need a dedicated URL with roll-specific images and pay controls on the landing page itself.

Fix: static challenge pages at /c/1/ through /c/6/ with pre-rendered OG images, JSON-LD WebApplication schema, pay-first layout, and sticky mobile pay bar when the user scrolls past the demo.

Architecture notes

What is next

The payment stack works end-to-end (wallet + Solana Pay + verification tools). The current bottleneck is distribution — getting Solana users to try a 0.001 SOL roll. If you build wallet-native apps, I would love feedback on the payment UX.