Architecture

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

ContractPurpose
ChallengePayCore contract: challenge lifecycle, stakes, claims
ChallengePayLightchainAttestorVerifier — records LightChain v2 worker verdicts and implements verify() for ChallengePay
TreasuryDAO treasury with bucketed custody and pull-based claims
MetadataRegistryOff-chain metadata URI storage
EventChallengeRouterMulti-outcome event routing and resolution
ChallengeAchievementNFT achievements for challenge milestones
TrustedForwarderEIP-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 → Refundable

Verification Path

The active verification path uses ChallengePayLightchainAttestor (Option-2 lean attestor, deployed 0xb400770550Db25Af86b1c3CC380e92BC777E3360):

  1. Off-chain runner submits the prompt to a LightChain v2 worker (gateway-mediated SIWE flow)
  2. Worker emits JobCompleted on JobRegistry with the response hash
  3. Our LCAI_WORKER_PK calls attestor.attest(...) recording the verdict on chain
  4. ChallengePay.submitProofFor(challengeId, subject, proof) calls attestor.verify() which reads the row
  5. After proofDeadlineTs, anyone calls ChallengePay.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

WorkerPurposePoll Interval
progressSyncWorkerSyncs real-time progress from API providers during active challenges15 min
evidenceCollectorFinal reconciliation fetch during proof window5 min
evidenceEvaluatorEvaluates evidence → verdicts15s
challengeDispatcherQueues per-(challenge, participant) jobs in aivm_jobs (since migration 050)10s
challengeWorkerClaims jobs by (challenge_id, subject) and runs the LightChain judge → attest → submitProofFor pipeline5s
autoDistributeWorkerAfter finalize, calls autoDistribute(id, winners[], losers[]) and pushes Treasury allowances30s

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 AutoProofService pushes 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

IndexerPurposePoll Interval
statusIndexerSyncs ChallengePay status → DB6s
claimsIndexerIndexes WinnerClaimed / LoserClaimed / Refunded events → DB (drives the on-chain-truth UI)6s
achievementIndexerWatches ChallengeAchievement mints6s

The legacy aivmIndexer (which bridged v1 PoI finalization events to submitProofFor) 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

  1. JudgerunChallengePayLightchainJob(challengeId, subject) performs the SIWE → gateway → relay flow with a LightChain v2 worker
  2. Attest — On a passing verdict, ChallengePayLightchainAttestor.attest(...) records the verdict on chain (one tx)
  3. Submit proofChallengePay.submitProofFor(challengeId, subject, proof) calls attestor.verify() which returns true and the contract sets c.winner[subject] = true
  4. Finalize — After proofDeadlineTs, ChallengePay.finalize(id) snapshots winners/losers
  5. Auto-distributeautoDistributeWorker reads on-chain state and calls autoDistribute(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:

PagePathPurpose
Home/Landing page with stats and navigation
Explore/exploreBrowse and search challenges
Create/challenges/create4-step challenge creation wizard
Challenge Detail/challenge/[id]Full challenge view with actions
My Challenges/me/challengesUser’s challenge dashboard
Claims/claimsClaim rewards from finalized challenges
Proofs/proofsSubmit evidence for verification
Admin/adminTemplate, 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