Skip to content

Repository files navigation

GigaPrix

The esports layer for Gigling Racing. GigaPrix turns isolated Gigaverse races (up to 8 pets each) into organized championships and leagues: a host creates a competition, players join with their wallet, the field auto-seeds into groups of 8, each group runs a real on-chain race, and the results are read back from the Abstract chain (chainId 2741) to auto-advance the stages until one champion remains.

It orchestrates races — it never creates them itself. Players race on Gigaverse; GigaPrix links the on-chain race to a heat, verifies it, and advances the bracket / standings.


Features

  • Two formatsKnockout Championship (staged groups → grand final) and League (round-robin matchdays, ranked by points). Coming soon: Time Trial, Swiss, Guild Cup.
  • Race types — Dash (500m), Sprint (1200m), Marathon (2400m), Grand Prix (3000m).
  • Wallet auth (SIWE) — connect an Abstract Global Wallet, then sign in once; the server verifies the signature (EOA + smart-account EIP-1271/6492) and never trusts a client-supplied address.
  • Host & co-host tools — invite links, bulk-add wallets, whitelist, open/close registration, per-stage qualifier edits, timezone-aware scheduling, manual result overrides, reopen/simulate heats, test-mode auto-run, co-hosts.
  • Live everything — link an on-chain race → spectate it embedded → fetch the result → qualifiers advance, with the bracket/standings updating in real time.
  • Profiles — display name, Discord, Twitter, bio, owned giglings (rarity + ELO), competition history.
  • Host dashboard with analytics (competitions, live, completed, total racers, champions crowned).
  • Private "Creator Cups" — unlisted, invite-only competitions.

The flow (how a competition runs)

  1. Create a competition (format, race type, size, rules).
  2. Players connect + sign in, then join with their wallet (they pick a gigling at race time).
  3. The host starts it → the field auto-seeds into groups of 8.
  4. For each group: Create race on Gigaverse (same wallet) → Fetch race ID (auto-detects it) or paste it → SpectateFetch result.
  5. The top N qualify and advance automatically, stage by stage, to a final and a champion. Standings publish on completion (live for leagues).

Roles

  • Host — creates and runs a competition (full control).
  • Co-host — a wallet the host grants manager rights to (everything except cancel / managing co-hosts).
  • Player — joins with a wallet, races their gigling.
  • Spectator — anyone; watches live and browses results.

Stack

  • Next.js 16 (App Router, RSC) · React 19 · TypeScript
  • Tailwind v4 (CSS theme in src/app/globals.css)
  • Prisma 6 · PostgreSQL (Neon in prod — IPv4, Vercel-friendly)
  • viem (Abstract reads + signature verification) · wagmi + Abstract Global Wallet · pusher-js (GigaSocket lobby)
  • TanStack Query · zod · Vitest

Gigaverse integration

GigaPrix doesn't own any race data — it reads everything from Gigaverse over two channels and stores only the tournament structure (who's in which heat) itself.

1. Gigaverse REST API

Base URL: GIGAVERSE_API_BASE (default https://gigaverse.io/api/racing). All calls live in src/lib/gigaverse/api.ts, go through one apiFetch helper (6s timeout, 1 retry), and fall back to mock data when NEXT_PUBLIC_USE_MOCK_GIGAVERSE="true".

Function Endpoint Used by Purpose
fetchGlobalStats GET /stats landing (app/page.tsx) Hero stat cards (total races, entries, racers, resolved)
fetchGiglingsByIds GET /pets?ids=… tournament + standings + match pages, api/giglings Batch-enrich entrants: name, image, rarity, faction, ELO, record
fetchPlayerGiglings GET /races/{address}GET /pets?ids=… profile page, api/giglings/[address], lib/users.ts (ELO sync) A wallet's racing pets, derived from races it entered
fetchWalletRaces GET /races + GET /races/{address} actions/match.ts (detectLatestRaceAction) Auto-detect the race a host just created on Gigaverse so it can be linked to a heat
fetchPetStats GET /pets/{id}/stats (helper) Per-pet race count / wins / recent finishes
fetchPlayerRaces GET /races/{address} (helper) Recent races for a wallet
fetchEloLeaderboard GET /leaderboard/elo (helper) ELO leaderboard
fetchRace GET /race/{id} (helper) Single race lookup

2. On-chain reads (PetRacingSystem contract)

Result resolution is read straight from the contract on Abstract (chainId 2741) via viem, not the REST API — it's the source of truth for finishing order. Address = NEXT_PUBLIC_PET_RACING_ADDRESS, RPC = NEXT_PUBLIC_ABSTRACT_RPC. Helpers in src/lib/gigaverse/contracts.ts:

Read Contract fn Used by Purpose
getRacePhase getRacePhase lib/race/link-service.ts, api/cron/sync-races Is the linked race resolved yet? (phase 3 = done)
getRacePets getRacePets lib/race/result-processor.ts Which pets ran
getRaceFinalRanking getRaceFinalRanking lib/race/result-processor.ts Finishing order → who advances
getRaceFinishTimes getRaceFinishTimes lib/race/result-processor.ts Finish times for telemetry/standings

When a heat's linked race reaches phase 3, the cron (or the in-app Fetch result button) reads the ranking on-chain, advances qualifiers, and updates standings.

3. Wallet signature verification

SIWE sign-in (api/auth/verify) verifies the wallet signature with viem's publicClient.verifyMessage, which supports EIP-1271/6492 smart accounts (so Abstract Global Wallet works, not just EOAs).


Getting started

npm install

1. Configure .env (single file at the repo root; .env* is gitignored):

Var Required Notes
DATABASE_URL / DIRECT_URL Supabase Postgres connection strings
AUTH_SECRET Signs wallet session cookies (long random string)
CRON_SECRET ✅ in prod Protects the race-sync cron
NEXT_PUBLIC_ABSTRACT_RPC https://api.mainnet.abs.xyz
NEXT_PUBLIC_PET_RACING_ADDRESS Gigaverse PetRacingSystem
GIGAVERSE_API_BASE https://gigaverse.io/api/racing
NEXT_PUBLIC_PUSHER_KEY / _CLUSTER optional Live lobby
NEXT_PUBLIC_SENTRY_DSN optional Remote error reporting
NEXT_PUBLIC_SITE_URL optional Absolute URL for OG/social cards
NEXT_PUBLIC_USE_MOCK_GIGAVERSE optional "true" to run without the live API

2. Database:

npm run db:push     # sync schema to the DB
# Demo data is optional and DESTRUCTIVE (wipes the DB), so it's guarded:
SEED_RESET=true npm run db:seed

3. Run:

npm run dev

Scripts

Script Description
npm run dev Dev server
npm run build prisma generate + production build
npm run start Production server
npm run typecheck tsc --noEmit
npm run lint ESLint
npm run test / test:watch Vitest (bracket/league/permissions logic)
npm run db:push / db:migrate Apply schema
npm run db:seed Seed demo data (needs SEED_RESET=true to wipe)
npm run db:clear-demo Remove only the synthetic demo rows
npm run db:studio Prisma Studio

Project structure

src/
  app/                 routes (landing, /tournaments, /dashboard, /profile, api/*)
    api/auth/          SIWE: nonce / verify / session / logout
    api/cron/          race-sync cron
  actions/             server actions (tournament, match, profile) — session-gated
  components/          UI (tournament/, profile/, shared/, ui/)
  hooks/               useWallet, useAuth, useTournament, useGiglings
  lib/
    auth/              wallet session (HMAC cookie) + sign-in message
    tournament/        seeding, advancement, standings (Knockout + League)
    race/              on-chain race linking + result processing
    gigaverse/         contract reads (viem) + REST API client
    permissions.ts     host / co-host checks
    rate-limit.ts      in-memory limiter
prisma/                schema, seed, clear-demo

Security

Wallet auth is signature-based (SIWE) — every server action derives the caller from a verified session cookie, not from client arguments. Plus rate limiting, cron hardening, and HTTP security headers. See SECURITY.md.

Deploy

Push to GitHub and import the repo in Vercel; set the env vars and the cron in vercel.json runs automatically. Full checklist in DEPLOY.md.

Roadmap

  • Formats: Time Trial, Swiss, Guild Cup (team scoring) — arrive with unlimited racing.
  • Prizes / payouts, notifications, deeper analytics, and a full a11y pass.

Note on on-chain reads: the live getRace struct differs from the reference ABI, so status is read via getRacePhase and results via the typed array reads (getRaceFinalRanking, getRaceFinishTimes, getPetOwnerInRace, getRacePets) — all verified against mainnet.

About

Resources

Security policy

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages