A World Cup bolão — a group match-prediction pool. Friends join a pool via a share link, predict match outcomes before kickoff, and a leaderboard updates live from the TxLINE sports-data feed. Today it's free to play for points and bragging rights; group stakes with the platform as the neutral escrow + settlement layer are the planned next step.
The differentiator: users sign up with just an email. A Solana wallet is created under the hood (an invisible wallet, via Privy) — no seed phrase, no connect button. Picks are committed on-chain and locked at kickoff, so nobody — not even the pool organizer — can rig the result. That tamper-proof commitment is the only thing the blockchain does here.
programs/acertana/ Anchor program (pick commitment only)
app/ Vite + React + TypeScript frontend
src/lib/wallet/ Privy invisible-wallet setup
src/lib/txline/ TxLINE (TxODDS) data client stubs
src/features/pools/ create + join by link
src/features/picks/ pick UI + kickoff lock
src/features/leaderboard/
docs/BUILD_LOG.md step-by-step scaffold log
docs/DECISIONS.md open design questions (resolve before implementing)
- Node 22+, Rust/Cargo
- Solana CLI (
curl -sSfL https://release.anza.xyz/stable/install | sh) - Anchor CLI 0.31.1 (
cargo install anchor-cli --version 0.31.1 --locked)
# Program: build + full test suite (in-process SVM, no validator needed)
anchor build
cargo test -p acertana
# Backend (join codes, roster, fixture authority, auto-reveal, leaderboard)
cd backend
npm install
npm test # includes an e2e reveal test against the real .so
PICK_STORE_KEY=<64-hex> npm run dev
# Frontend
cd app
cp .env.example .env # fill VITE_PRIVY_APP_ID, VITE_BACKEND_URL, VITE_RPC_URL
npm install
npm test
npm run devNote: Privy's embedded wallet needs HTTPS (localhost is exempt). Deploy to an HTTPS URL early for phone / local-network testing.
Backend → Railway (Dockerfile in backend/, mount a volume and point DB_PATH
at it). App → Vercel (static Vite build of app/). Program → devnet via
FIXTURE_AUTHORITY_PUBKEY=<fresh pubkey> anchor build && anchor deploy.
Backend env:
| Var | Purpose |
|---|---|
PICK_STORE_KEY |
64-hex AES-256-GCM key for the encrypted pick store |
RPC_URL |
Solana RPC (devnet: https://api.devnet.solana.com) |
FIXTURE_AUTHORITY_KEYPAIR / _B64 |
authority keypair path, or base64 of its json |
PRIVY_APP_ID / PRIVY_APP_SECRET |
Privy server auth (required unless ALLOW_UNAUTHENTICATED=1) |
TXLINE_API_TOKEN |
TxLINE data token — mint once with npm run txline-subscribe |
TXLINE_API_ORIGIN / TXLINE_COMPETITION_ID |
optional TxLINE overrides |
CORS_ORIGIN |
comma-separated allowed origins (the app's URL) |
ADMIN_TOKEN |
enables POST /admin/results for manual result injection |
DB_PATH |
SQLite path (persistent volume in prod) |
TXLINE_STUB=1 |
local dev only: seed fixtures + fake score feed |
App env (app/.env): VITE_PRIVY_APP_ID, VITE_BACKEND_URL, VITE_RPC_URL.
Add the deployed app URL to the Privy app's allowed origins.
tests/fixtures/fixture-authority.jsonis a PUBLISHED DEV KEY whose pubkey is the hardcoded on-chainFIXTURE_AUTHORITY. Fine while local-only; MUST be rotated (fresh keypair kept out of git + program upgrade) before any real deploy.
The full flow is built and running end-to-end on devnet:
- On-chain program (Anchor) — fixture registry, pool creation, pick commit/reveal with kickoff gates; covered by a LiteSVM test suite.
- Commit-reveal client lib — keccak commitments matching the program byte-for-byte, deterministic per-fixture salts from a wallet signature.
- Backend (Fastify + SQLite) — join codes, pool roster with opt-in organizer approval, fixture-authority signer, encrypted-at-rest pick store with an auto-reveal worker, scoring (5/3/1/0) + leaderboard, persistent match results.
- Real data + auth — fixtures and live scores come from the actual TxLINE
feed (no more stubs;
TXLINE_STUB=1remains for local dev), and login is live Privy email auth with the invisible embedded wallet. - Frontend — create pool (
/novo), invite share links, join flow, pick UI with kickoff lock, live scores and ranking polled every 30s, post-kickoff view of results, points, and everyone's picks, pt-BR team names, my-bolões home. Deployed on Vercel; backend on Railway; program on devnet.
Build story: docs/BUILD_LOG.md; goal tracker: docs/GOALS.md. Historical
open questions: docs/DECISIONS.md (all resolved in the spec at
docs/superpowers/specs/2026-07-09-acertana-design-decisions-design.md).