Skip to content
mskradPublic

About

Resources

Stars

0 stars

Watchers

0 watching

Forks

Repository files navigation

Beat the Analyst

TxLINE-powered AI prediction arena for the Superteam hackathon — a phone-first app where you predict match outcomes against an AI analyst and the market (Solo, free), plus a paid Odds Arena tournament that settles through an on-chain USDC escrow on Solana devnet.

pnpm workspace: UI, worker, adapters, scoring, DB, and the Anchor escrow program.

Workspace

apps/
  web/          Next.js app (design UI: Arena / Odds Arena / Board / Data)
    app/api/arena/   on-chain Odds Arena routes (config/faucet/enter-tx/settle/claim-tx/status)
  worker/       replay/live ingest + local state API
packages/
  txline/       TxLINE client, auth helpers, SSE parser, IDL
  ai/           AIAnalyst contract, mock/HTTP/borrowed-reference adapters, input hashing
  scoring/      pure scoring helpers
  db/           Drizzle schema, SQL migration, local persistence fallback
  shared/       shared constants/types
fixtures/
  recorded/     deterministic demo data + real captured fixtures
programs/
  odds_arena/   Anchor escrow program (USDC tournament pool, deployed on devnet)

Quick Start

pnpm install
pnpm check
pnpm dev:web

In another terminal:

pnpm dev:worker

Phase 0 Commands

pnpm phase0:replay
pnpm replay:sample
pnpm replay:txline
pnpm replay:state-test
pnpm phase0:ai
pnpm ai:contract-test
pnpm scoring:test
pnpm db:check
pnpm db:roundtrip-test
pnpm db:generate
pnpm txline:fixture-test
pnpm phase0:txline

Live TxLINE commands require subscription activation first. The local check, replay, AI smoke, and TxLINE fixture test do not.

TxLINE WC Access Flow

pnpm txline:guest

Put the printed TXLINE_JWT into your shell or .env, run the free-tier on-chain subscribe, then set:

TXLINE_SUBSCRIBE_TX_SIG=<confirmed subscribe tx>
SOLANA_KEYPAIR=.keys/txline-devnet.json
TXLINE_SELECTED_LEAGUES=

Activate the API token:

pnpm txline:activate

Then smoke-test data access:

TXLINE_BASE_URL=https://txline-dev.txodds.com \
TXLINE_API_AUTH_MODE=jwt-plus-api-token \
TXLINE_COMPETITION_ID=72 \
TXLINE_ODDS_FIXTURE_ID=17588245 \
TXLINE_SCORES_FIXTURE_ID=17588309 \
pnpm phase0:txline

Confirmed on txline-dev: bearer API-token mode returns 401; use jwt-plus-api-token.

The fixture-backed regression checks the recorded devnet snapshot:

pnpm txline:fixture-test

Replay

The replay harness converts both the synthetic fixture and the recorded TxLINE devnet snapshot into internal beat_replay_event_v1 events:

pnpm replay:sample
pnpm replay:txline

Each replay produces odds updates, score updates, and a final result event. The worker can consume the same state reducer:

WORKER_RUN_REPLAY=1 REPLAY_SPEED=100000 pnpm dev:worker

The worker also exposes a local replay state bridge:

pnpm dev:worker
curl -fsS http://127.0.0.1:8788/health
curl -fsS http://127.0.0.1:8788/state
curl -fsS -X POST 'http://127.0.0.1:8788/replay/step?count=2'
curl -fsS -X POST http://127.0.0.1:8788/replay/start
curl -fsS http://127.0.0.1:8788/events
curl -fsS http://127.0.0.1:8788/leaderboard

The bridge persists replay events and optional pick/score records through the DB package's local JSON fallback, so demo leaderboard state survives worker refreshes without requiring hosted Supabase.

Wallet

Solo mode detects Phantom directly from window.solana in the browser. Free mode never asks for transaction signing: connecting the wallet only supplies a public key so picks can be saved under that address.

NEXT_PUBLIC_WORKER_URL=http://127.0.0.1:8788 pnpm dev:web

No-wallet recording remains supported. If Phantom is absent, the app shows a local-only state and all replay controls still work after choosing a pick.

Real-match Solo (predict in advance, settle after full time)

The Arena can run on a real TxLINE World Cup fixture (real teams + real odds):

pnpm txline:capture-real     # poll fixtures, write a real pre-match fixture -> fixtures/recorded/real-match.json
pnpm dev:web                 # Arena shows the real match; pick is persisted to .data/picks.json (survives reload)
pnpm txline:settle-real      # after FT: pull the real final score -> Arena settles on real data

apps/web/app/_data/load-arena.ts prefers fixtures/recorded/real-match.json when present (override with ARENA_FIXTURE), else the deterministic synthetic fixture so the demo always works.

Odds Arena (on-chain escrow, devnet)

The paid tournament settles through an Anchor escrow program odds_arena (programs/odds_arena), deployed on devnet at GTcSyA2M8BVd27idJJw7ajhnSxrm45tkw7R9moxpAPXc. Round cycle open_tier → enter → lock_tier → settle_entry → sweep_rake → claim: USDC into an escrow PDA, a keeper posts per-entry payouts (bounded on-chain by committed_payout ≤ pool, so escrow can't be over-paid), rake is swept to the treasury, winners claim (PDA-signed). A stuck round is recoverable via refund_entry (admin, or anyone 24 h after lock). Rake-only, no house position; TxL is never used for money.

Build + run the full round on a local validator:

pnpm anchor:build
solana-test-validator --reset \
  --bpf-program GTcSyA2M8BVd27idJJw7ajhnSxrm45tkw7R9moxpAPXc \
  programs/odds_arena/target/deploy/odds_arena.so
RPC_URL=http://127.0.0.1:8899 pnpm anchor:round

Same round on devnet (funds ephemeral keypairs from a wallet, prints explorer links):

RPC_URL=https://api.devnet.solana.com FUNDER_KEYPAIR=.keys/txline-devnet.json pnpm anchor:round

Browser staking with Phantom

The Odds tab has a "Live on devnet" panel: connect Phantom (devnet), get test-USDC, stake via a Phantom-signed enter, settle, claim — real on-chain txs. The server builds the transactions (Anchor + IDL); the browser only signs+sends (window.solana); the operator key stays server-side.

node scripts/devnet-arena-setup.mjs   # create a test mint + open a round -> .data/devnet-arena.json
pnpm dev:web                          # Odds tab -> "Live on devnet": Connect -> Get test USDC -> Enter -> Settle -> Claim

Staking commits the canonical slip_hash (borsh EntryCommit, VRE-parity) and locks the slip once staked. Routes: player /api/arena/{config,faucet,enter-tx, express,claim-tx,status}; operator (CRON_SECRET-gated) /api/arena/{settle,sweep, rotate}. The demo round is single-entry-per-wallet; rotate?force=1 opens a fresh one. Hardening smoke: node scripts/anchor-hardening-smoke.mjs (payout-bound, sweep, refund).

Verifiable scoring (VRE): committed slips + proven TxLINE facts are scored by VRE's public POST /api/conditions/score handle — verifiable S + ranking + fc7 artifact, byte-for-byte identical to our local scoreExpress. POST /api/arena/verify-round surfaces the MATCH; settlement attaches the artifact hash. Payout is allocated locally (deterministic); VRE is the independent proof layer (it never custodies funds).

Persistence

The DB package contains a Supabase Postgres-ready Drizzle schema plus a checked SQL migration:

pnpm db:check
pnpm db:generate
pnpm db:roundtrip-test

Tables covered: fixtures, market_snapshots, ai_calls, users, picks, scores, and replay_events. Local demo persistence uses BEAT_LOCAL_DB_PATH and defaults to .data/beat-local-db.json; .data/ is ignored. Hosted Supabase wiring is deferred until DATABASE_URL is available.

AI Analyst

The app depends on a stable AIAnalyst adapter contract (packages/ai), not bot internals. Pick the adapter with AI_PROVIDER:

  • mock (default) — deterministic, secret-free.
  • borrowed-reference — analysis-derived probabilities / reasoning / risk flags from the borrowed bot's pre-match pipeline; no key, no network.
  • http — call an external endpoint returning probabilities.home/draw/away, confidence, pick, reasoning, optional sources/latencyMs/costUsd/model.

Every call is tied to a canonical hashed pre-match input (packages/ai/src/input.mjs), so the read is reproducible and verifiable — provider + inputHash are surfaced on the Data screen. Invalid probabilities or picks fail fast. The borrowed repo itself stays quarantined (.quarantine/, gitignored) and is never committed or imported wholesale.

pnpm phase0:ai
pnpm ai:contract-test

Scoring

Solo scoring is pure and offline:

pnpm scoring:test

The scoring package resolves 1X2 from TxLINE soccer stats (Stats[1] / Stats[2]), computes AI-market divergence for home/draw/away, derives the market favorite from probabilities, and returns deterministic user points with a breakdown for correct pick, beat-AI, beat-market, and underdog bonus.

About

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages