Architecture
LightChallenge is a full-stack Web3 protocol with smart contracts, off-chain services, a webapp, and a mobile evidence collector.
Repository Structure
lightchallenge/
├── contracts/ # Solidity smart contracts
├── deploy/ # Hardhat deploy scripts
├── offchain/ # TypeScript off-chain services
│ ├── adapters/ # Evidence source adapters (OpenDota, Riot, etc.)
│ ├── connectors/ # Data provider connectors (Apple, Strava, Garmin)
│ ├── db/ # Database access layer
│ ├── evaluators/ # Evidence evaluation logic
│ ├── identity/ # Identity binding registry
│ ├── indexers/ # On-chain event indexers
│ ├── lib/ # Shared utilities
│ └── workers/ # Polling worker processes
├── webapp/ # Next.js 14 web application
│ ├── app/ # App router pages and API routes
│ ├── lib/ # Client-side utilities
│ └── public/ # Static assets, ABIs, deployments
├── mobile/ios/ # iOS HealthKit evidence collector
├── db/ # Database migrations and seed scripts
├── schemas/ # JSON schemas for challenge rules
├── scripts/ # Deploy, admin, and operations scripts
└── docs-site/ # Documentation portal (Nextra)Smart Contracts
| Contract | Purpose |
|---|---|
| ChallengePay | Core contract: challenge lifecycle, stakes, claims |
| ChallengePayLightchainAttestor | Verifier — records LightChain v2 worker verdicts and implements verify() for ChallengePay |
| Treasury | DAO treasury with bucketed custody and pull-based claims |
| MetadataRegistry | Off-chain metadata URI storage |
| EventChallengeRouter | Multi-outcome event routing and resolution |
| ChallengeAchievement | NFT achievements for challenge milestones |
| TrustedForwarder | EIP-2771 gasless transaction relay (deployed but inactive) |
| MultiSigWallet (protocol) | 2-of-3 protocol multi-sig — admin recovery path when challenges finalize with no winners |
Challenge Lifecycle (ChallengePay V1)
Active → (proof submitted) → Finalized (Success/Fail)
Active → (deadline passed) → Finalized (Expired)
Active → (admin cancel) → Canceled → RefundableVerification Path
The active verification path uses ChallengePayLightchainAttestor (Option-2 lean attestor, deployed 0xb400770550Db25Af86b1c3CC380e92BC777E3360):
- Off-chain runner submits the prompt to a LightChain v2 worker (gateway-mediated SIWE flow)
- Worker emits
JobCompletedonJobRegistrywith the response hash - Our
LCAI_WORKER_PKcallsattestor.attest(...)recording the verdict on chain ChallengePay.submitProofFor(challengeId, subject, proof)callsattestor.verify()which reads the row- After
proofDeadlineTs, anyone callsChallengePay.finalize(id); payout snapshots winners
This replaces the v1 commit-reveal-PoI ceremony (AIVMInferenceV2 + LCAIValidatorRegistry + aivmIndexer + ChallengePayAivmPoiVerifier) which was archived in 2026-04-28. The old contracts remain on chain but are unused.
Off-chain Services
Workers
| Worker | Purpose | Poll Interval |
|---|---|---|
| progressSyncWorker | Syncs real-time progress from API providers during active challenges | 15 min |
| evidenceCollector | Final reconciliation fetch during proof window | 5 min |
| evidenceEvaluator | Evaluates evidence → verdicts | 15s |
| challengeDispatcher | Queues per-(challenge, participant) jobs in aivm_jobs (since migration 050) | 10s |
| challengeWorker | Claims jobs by (challenge_id, subject) and runs the LightChain judge → attest → submitProofFor pipeline | 5s |
| autoDistributeWorker | After finalize, calls autoDistribute(id, winners[], losers[]) and pushes Treasury allowances | 30s |
Data Providers
Server-side (auto-synced by progressSyncWorker + evidenceCollector):
- Strava — OAuth, token auto-refresh (fitness: running, cycling, swimming, etc.)
- Fitbit — OAuth, token auto-refresh (fitness: steps, activities)
- OpenDota — Public API (gaming: Dota 2 matches)
- Riot — API key (gaming: League of Legends matches)
- FACEIT — API key (gaming: CS2 matches)
Upload-only (no server-side API — data from device or manual upload):
- Apple Health — iOS
AutoProofServicepushes HealthKit data during active period + proof window - Garmin — Users upload TCX/GPX/JSON export files
- Google Fit — API deprecated by Google in 2025; users upload Google Takeout JSON
Indexers
| Indexer | Purpose | Poll Interval |
|---|---|---|
| statusIndexer | Syncs ChallengePay status → DB | 6s |
| claimsIndexer | Indexes WinnerClaimed / LoserClaimed / Refunded events → DB (drives the on-chain-truth UI) | 6s |
| achievementIndexer | Watches ChallengeAchievement mints | 6s |
The legacy
aivmIndexer(which bridged v1 PoI finalization events tosubmitProofFor) is archived and no longer running. The current runner does its own on-chain submission in a single pass.
Database
PostgreSQL (Neon) with tables for challenges, participants, evidence, verdicts, AIVM jobs, identity bindings, and claims.
LightChain v2 Worker Integration
LightChallenge is a client of the LightChain v2 worker network (testnet chain ID 8200, RPC https://rpc.testnet.lightchain.ai). Workers run real LLM compute (Ollama / llama3-8b), our attestor records their verdicts on chain, and ChallengePay reads the attestor at submitProofFor time.
Request Lifecycle
- Judge —
runChallengePayLightchainJob(challengeId, subject)performs the SIWE → gateway → relay flow with a LightChain v2 worker - Attest — On a passing verdict,
ChallengePayLightchainAttestor.attest(...)records the verdict on chain (one tx) - Submit proof —
ChallengePay.submitProofFor(challengeId, subject, proof)callsattestor.verify()which returns true and the contract setsc.winner[subject] = true - Finalize — After
proofDeadlineTs,ChallengePay.finalize(id)snapshots winners/losers - Auto-distribute —
autoDistributeWorkerreads on-chain state and callsautoDistribute(id, winners[], losers[])then pushes Treasury allowances
The whole runner is single-pass: 3 on-chain transactions per participant. No commit-reveal, no PoI quorum, no event-watching indexer. The on-chain JobCompleted event from the LightChain worker is the public audit trail.
Per-participant queueing
Since migration 050_aivm_jobs_per_participant.sql, the dispatcher queues one aivm_jobs row per (challenge_id, participant_subject) pair. A challenge with three joiners and three passing verdicts becomes three independent runs in parallel — necessary because challenges.subject is the creator and not always the same as the participant being verified. See incident #13 for the bug class this fixes.
See AI Verification for the full pipeline.
Webapp
Next.js 14 with App Router. Key pages:
| Page | Path | Purpose |
|---|---|---|
| Home | / | Landing page with stats and navigation |
| Explore | /explore | Browse and search challenges |
| Create | /challenges/create | 4-step challenge creation wizard |
| Challenge Detail | /challenge/[id] | Full challenge view with actions |
| My Challenges | /me/challenges | User’s challenge dashboard |
| Claims | /claims | Claim rewards from finalized challenges |
| Proofs | /proofs | Submit evidence for verification |
| Admin | /admin | Template, model, and challenge management |
Wallet Integration
- wagmi v2 for chain interaction
- RainbowKit for connect UI
- WalletConnect v2 for mobile wallets and Safari
- Supports: MetaMask, WalletConnect, Coinbase Wallet, Rainbow, Trust Wallet, Phantom