Texas Hold'em on Solana. USDC settlement. VRF-audited shuffles. Monorepo with a lean Anchor program, Hono+Bun API, Vite+React client, and Drizzle-backed off-chain state.
apps/
api/ Hono + Bun server (WS + hand engine + settlement worker)
web/ Vite + React client (wallet-adapter, smooth animated felt)
packages/
idl/ Hand-rolled Anchor IDL helpers (discriminators, PDAs, borsh, ix builders)
db/ Drizzle schema + migrations
shared/ Cards, VRF-seeded shuffle, 7-card evaluator, wire protocol
programs/
poker/ Anchor program (Rust) + LiteSVM TS tests
7 instructions, all privileged ones guarded by has_one = operator:
| ix | signer | purpose |
|---|---|---|
initialize_table |
operator | Create table PDA + vault ATA |
buy_in |
player | Deposit USDC → seat balance |
cash_out |
player | Withdraw from unlocked seat |
begin_hand |
operator | Lock N seats for a monotonic hand_id |
settle_hand |
operator | Atomic payout; asserts sum(debits) == sum(credits) + rake; creates SettlementReceipt PDA (replay-safe via init) |
emergency_timeout_refund |
player | Unilateral refund if operator leaves a seat locked past dispute_window_slots |
withdraw_rake |
operator | Sweep accrued rake to treasury ATA |
Custody model: semi-custodial. Operator has settle authority; players always have the unilateral refund backstop.
20/20 LiteSVM tests in programs/poker/tests/poker.test.ts. Run:
bun install
cd programs/poker/tests && bun testCovers every instruction's happy path, attack paths (wrong signer, replay, locked-seat cash_out, rake cap, pot math), and the conservation invariant across 5 hands.
solanaCLI 3.x (shipscargo-build-sbf,solana-test-validator,solana-keygen)bun≥ 1.2
cargo-build-sbf
# artifact: target/deploy/poker.so
# program id: 9FeibPV2hjbkcu4YHSnUMr9ikWV7QLWMmBpVFZAcYsZHSurfpool 0.1 has a narrow RPC subset. For a real dev loop use the Solana test validator:
solana-test-validator --reset \
--bpf-program 9FeibPV2hjbkcu4YHSnUMr9ikWV7QLWMmBpVFZAcYsZH target/deploy/poker.so
# RPC: http://127.0.0.1:8899(When surfpool supports --bpf-program + full RPC it will be the preferred option — see Surfpool.toml placeholder.)
bun run scripts/bootstrap.ts
# Airdrops SOL to the operator keypair, creates a dev-USDC mint,
# initializes table id=1, and prints the env vars you need.Copy the printed env vars into apps/api/.env and apps/web/.env.
To fund a specific wallet (e.g. your Phantom):
bun run scripts/bootstrap.ts --player=<your-wallet-pubkey> --amount=1000bun install
bun --filter @lightly/api dev # API on :4000 — now submits begin_hand + settle_hand
bun --filter @lightly/web dev # Vite on :5173Open http://localhost:5173 → connect Phantom → sign the SIWS challenge → use the Deposit button to buy in → click Start hand → play.
On hand end, settle_hand lands on-chain. You'll see the tx sig in the API logs and your seat balance updates.
overflow-checks = truein release profile +checked_add/checked_subon every money line.Account<'info, T>everywhere — Anchor enforces owner = program ID.Interface<'info, TokenInterface>pins the token program for CPIs.initonSettlementReceiptmakes replay impossible: two settlements for the samehand_idcollide on PDA init.- Monotonic
hand_idprevents stale settle + rebegin. - Time-based expiry uses slots, not wall clock.
- Mints with
transfer_hook/ confidential transfers / frozen-default are rejected by spec — stick to classic USDC/USDT for MVP.
- Dealer draws a 32-byte seed per hand (dev:
crypto.getRandomValues; prod: ORAO VRF). sha256(seed)is published as a commitment before any card is dealt (shown in UI).- On showdown the seed is revealed; clients can re-derive the deck via
shuffleDeck(seed)from@lightly/shared.
- Replace dev seed with ORAO VRF on-chain call.
- Wire settlement worker to submit
begin_hand+settle_handvia Helius Sender (Jito-bundled for frontrun protection). - Swap operator authority to a Squads v4 multisig.
- LaserStream subscription on program ID → reconcile
HandSettledevents intosettlementstable. - Helius webhook on vault ATA → reconcile buy-in/cash-out into
ledger. - MagicBlock ephemeral rollup for sub-slot action latency on high-volume tables.
See the research reports for the full 2026 stack rationale.