🏏 Betting App — Claude Skill
A production-tested Claude skill that teaches Claude how to build full-stack sports betting and prediction market apps — with correct odds math, atomic wallet logic, and zero data integrity bugs.
What Is This?
This is a Claude skill — a knowledge file that makes Claude dramatically better at building betting and prediction market apps. Instead of giving generic boilerplate, Claude uses this skill to apply patterns proven in a real production app that ran a full cricket tournament with 15+ players and 200+ bets with zero data integrity incidents.
Reference implementation: github.com/sklls/BPL_BET
Who Is This For?
- Developers building sports betting platforms, fantasy leagues, or office pools
- Anyone adding prediction markets or wagering to an existing app
- Developers who want to avoid the painful bugs that come with financial logic (double-spend, race conditions, stale odds)
What Problems Does This Skill Solve?
Building a betting app looks simple until you hit these walls:
| Problem | What Goes Wrong Without This Skill |
|---|---|
| Race conditions | Two users bet at the same time, one overdrafts their wallet |
| Wrong odds preview | The odds shown to the user before placing don't match the odds recorded after placing |
| Stale data | Admin sees yesterday's totals; leaderboard doesn't update after new bets |
| RLS bypasses | Leaderboard and admin pages silently return empty data because of Row Level Security |
| TypeScript errors | Supabase join types come back as arrays but are actually objects — causes runtime crashes |
| Settlement bugs | Winners don't get paid, or get paid the wrong amount |
Every one of these is handled in the skill with the exact fix.
What's Inside the Skill?
1. Complete Database Schema
Full PostgreSQL schema for profiles, matches, markets, bet_options, bets, and transactions — with correct types, constraints, RLS policies, and foreign keys.
2. Atomic Bet Placement RPC
The place_bet Postgres function uses SELECT FOR UPDATE to lock the user's wallet row before deducting — preventing any possibility of double-spend, even under concurrent load.
SELECT wallet_balance INTO v_balance
FROM profiles WHERE id = p_user_id FOR UPDATE; -- locks the row
3. Pari-Mutuel Odds Engine (TypeScript)
The live odds formula — including the critical extraAmount trick that makes the preview odds match the final recorded odds:
// Include the player's own stake in the preview pool
// so what they see is what they get
export function calculateOdds(
options: BetOption[],
selectedId: string,
extraAmount: number, // <-- this is the key
houseEdgePct: number
): number { ... }
4. Market Settlement with Early Bird Bonus
settle_market pays out winning bets with an optional +10% bonus for bettors who placed their bet within the first 30 minutes of a market opening — encouraging early participation over last-minute odds manipulation.
v_early_bird_cutoff := v_market_created_at + INTERVAL '30 minutes';
IF bet.placed_at < v_early_bird_cutoff THEN
v_payout := v_payout * 1.10; -- early bird bonus
END IF;
5. Two Supabase Clients (and When to Use Each)
// Public pages — respects RLS
createServerComponentClient() / createClientComponentClient()
// Admin, leaderboard, bettors API — bypasses RLS with service role key
createAdminClient() // uses SUPABASE_SERVICE_ROLE_KEY
6. Stale Data Prevention
Every page serving financial or live data needs this to prevent Vercel from caching old results:
export const dynamic = 'force-dynamic'
7. TypeScript Join Type Fix
Supabase types .select('profiles(display_name)') as an array, but the join returns a single object. The safe cast pattern:
const name = (bet.profiles as unknown as { display_name: string } | null)
?.display_name ?? 'Unknown'
8. Per-Match + Overall Leaderboard
Full pattern for a leaderboard with tab switching — server-rendered for speed, with client-side fresh fetch to avoid stale dropdown data when new matches are added.
9. Admin Financial Dashboard
Direct table queries (not RPC) for the financial overview, so numbers always reflect the current state:
// Don't use RPC — it can be stale
// Query the tables directly
const { data: topups } = await admin.from('transactions')
.select('amount').eq('type', 'topup')
10. Common Pitfalls Table
A cheat sheet of every painful mistake, what caused it, and the exact fix — built from real production debugging sessions.
How to Install
Option A: Use the .skill file (recommended)
- Download
betting-app.skill - In Claude Code, run:
/skills install betting-app.skill
Option B: Manual install
Copy SKILL.md into your Claude skills directory:
~/.claude/skills/betting-app/SKILL.md # Mac / Linux
%APPDATA%\Claude\skills\betting-app\SKILL.md # Windows
Once installed, Claude will automatically apply these patterns whenever you ask it to:
- Build a betting or wagering app
- Add prediction markets to an existing app
- Write pari-mutuel odds logic
- Create wallet or transaction systems
How It Was Built
This skill was extracted from BCL Bet — a real cricket tournament betting platform built from scratch over 2 weeks. Every pattern in the skill was learned the hard way:
- The
FOR UPDATElock came from catching a wallet race condition in testing - The
extraAmountodds trick came from users seeing different odds before and after confirming a bet - The
force-dynamicfix came from the admin dashboard showing week-old totals - The early bird bonus came from the problem of everyone betting at the last second to manipulate odds
- The admin client pattern came from the leaderboard returning zero rows due to RLS
The skill was validated with 3 eval prompts, each run with and without the skill. Average improvement: +60% on key correctness criteria.
Eval Results
| Test | With Skill | Without Skill |
|---|---|---|
| Postgres schema + RPCs | 6/6 ✅ | 2/6 ❌ |
| Pari-mutuel odds engine (TypeScript) | 5/5 ✅ | 4/5 ⚠️ |
| Stale data fixes (Next.js + Supabase) | 4/4 ✅ | 3/4 ⚠️ |
The biggest gaps without the skill: FOR UPDATE row lock, extraAmount in odds preview, force-dynamic on financial pages — subtle but critical.
Tech Stack
| Layer | Technology |
|---|---|
| Frontend | Next.js 14 (App Router), TypeScript, Tailwind CSS |
| Backend | Supabase (PostgreSQL + RLS + Realtime) |
| Auth | Supabase Auth |
| Deployment | Vercel |
| Payments | Manual wallet top-up (admin-controlled) |
Related
- Reference App: github.com/sklls/BPL_BET — the actual betting platform this skill was extracted from
- Claude Code Docs: docs.anthropic.com
License
MIT — use freely, attribution appreciated.
Built from a real production app. Every pattern here was written in anger at 2am after something broke.