GuidesMainnet Migration

Mainnet Migration: LightChain Testnet v2 → Mainnet

This guide documents what changes when LightChallenge moves from LightChain testnet v2 (chain 8200) to mainnet. The verification architecture itself doesn’t change — we keep the lean ChallengePayLightchainAttestor + LightChain v2 worker network. The migration is mostly: address swaps, multi-sig signer rotation, attestor key rotation, and re-pointing the off-chain workers at mainnet endpoints.

Current State (Testnet v2, chain 8200)

ComponentWhat we controlAddress / endpoint
ChallengePayDeployed by us0xeC651C299E978667fCDeF706Ef5Dd285e56EFd0b
ChallengePayLightchainAttestorDeployed by us0xb400770550Db25Af86b1c3CC380e92BC777E3360
TreasuryDeployed by us0xF8E32344CC311A82f20112484F686b1038122FF3
MultiSigWallet (protocol + admin recovery)Deployed by us, 2-of-3 signers0xd9e56435290A2e8f93D6F8a0e329478D8E851469
LightChain v2 gatewayOperated by LightChain Foundationhttps://chat-api.testnet.lightchain.ai
LightChain v2 relayOperated by LightChain Foundationwss://relay.testnet.lightchain.ai/ws
LightChain JobRegistryOperated by LightChain Foundation0x531b3a87c5d785441b9cf55b98169f20fd9056a7
Dispatcher signing keyLightChain Foundation0xd92d9989d6a7A5aEcB4Da59D414Fb0673aDC2519

The v1 archive (AIVMInferenceV2, LCAIValidatorRegistry, ChallengePayAivmPoiVerifier, ChallengeTaskRegistry) remains on testnet v2 chain but is unused. It will not be deployed to mainnet.


What we need from LightChain (mainnet launch)

#ItemWhy
1Mainnet chain IDRPC config, Hardhat network config, wallet config
2Mainnet RPC endpointAll on-chain interactions
3Mainnet gateway URL (SIWE auth)judgeChallengeViaLightchain round-trip
4Mainnet relay WSS URLEncrypted response channel
5Mainnet JobRegistry addressJob anchor + JobCompleted audit trail
6Mainnet dispatcher signing keyprepareSession signature
7Mainnet fee per job (current testnet: 0.02 LCAI)Worker fee on submitJob
8Active staked workers on mainnetWithout workers, jobs don’t get picked up
9Disputer key / arbitration policyFor dispute resolution if a worker lies

The contract surface is identical (same JobRegistry ABI on testnet and mainnet); only addresses + URLs change.


Code changes required

1. Environment variables

Update .env, webapp/.env.local, Vercel, and fly secrets:

# Chain
NEXT_PUBLIC_CHAIN_ID=<mainnet-chain-id>
NEXT_PUBLIC_RPC_URL=<mainnet-rpc-url>
LIGHTCHAIN_RPC=<mainnet-rpc-url>
NEXT_PUBLIC_EXPLORER_URL=<mainnet-explorer-url>
 
# Our redeployed contracts
NEXT_PUBLIC_CHALLENGEPAY_ADDR=<mainnet-challengepay>
CHALLENGEPAY_LIGHTCHAIN_ATTESTOR_ADDRESS=<mainnet-attestor>
NEXT_PUBLIC_TREASURY_ADDR=<mainnet-treasury>
NEXT_PUBLIC_MULTISIG_ADDR=<mainnet-multisig>
 
# LightChain v2 mainnet endpoints
LIGHTCHAIN_GATEWAY_URL=<mainnet-gateway>
LIGHTCHAIN_RELAY_WSS=<mainnet-relay-wss>
LIGHTCHAIN_JOB_REGISTRY=<mainnet-jobregistry>
 
# Worker wallet (must be funded on mainnet, registered as ChallengePay dispatcher + attestor)
LCAI_WORKER_PK=0x<funded-mainnet-key>

2. Redeploy our contracts on mainnet

Same deploy sequence as testnet (see /guides/deploy). All our contracts are independent of LightChain’s v2 contracts at deploy time — ChallengePayLightchainAttestor doesn’t need any LightChain address as a constructor argument.

npx hardhat deploy --network lightchain-mainnet
RUN_POST_DEPLOY_CONFIG=true npx hardhat deploy --network lightchain-mainnet
npx tsx scripts/syncAbis.ts

3. Multi-sig rotation

The 2-of-3 protocol multi-sig signers should be rotated for mainnet. The testnet signer keys (0x4480…, 0x74aD…, 0x3917…) are not appropriate for mainnet custody.

For each replacement, the existing signers submit a multi-sig tx calling replaceSigner(oldAddr, newAddr) on the wallet itself, then 2 of 3 confirm + execute. Repeat for each of the 3 slots.

4. Attestor key rotation

The LCAI_WORKER_PK testnet wallet should be rotated to a mainnet hot-wallet. The new wallet must be:

  • Registered as a ChallengePay dispatcher: setDispatcher(newAddr, true) (admin)
  • Authorised on the attestor: attestor.setAttestor(newAddr, true) (attestor admin)

After rotation, deauthorise the old key:

  • attestor.setAttestor(oldAddr, false)
  • setDispatcher(oldAddr, false)

5. Hardhat config

Add the mainnet network in hardhat.config.ts:

"lightchain-mainnet": {
  url: process.env.MAINNET_RPC_URL,
  chainId: <mainnet-chain-id>,
  accounts: [process.env.DEPLOYER_PRIVATE_KEY],
},

6. Database migrations

All migrations through 050_aivm_jobs_per_participant.sql apply equally to mainnet — no chain-specific data structures. Run npx tsx db/migrate.ts against the mainnet Postgres to apply.


Verification checklist

Before switching production traffic to mainnet:

Contracts

  • ChallengePay.admin() returns the expected mainnet admin
  • ChallengePay.dispatchers(<LCAI_WORKER_PK_addr>) returns true
  • ChallengePay’s default verifier (or per-challenge verifier) is ChallengePayLightchainAttestor
  • Treasury.hasRole(OPERATOR_ROLE, <ChallengePay>) returns true
  • ChallengePayLightchainAttestor.attestor(<LCAI_WORKER_PK_addr>) returns true
  • MultiSigWallet.signers() returns the rotated mainnet signer set

LightChain integration

  • judgeChallengeViaLightchain round-trip succeeds (run scripts/demoLightchainJudge.ts against mainnet)
  • JobCompleted event from a mainnet worker is observable for our jobs
  • Attestor attest(...) succeeds (run scripts/demoChallengeLeanE2E.ts against mainnet)
  • ChallengePay.submitProofFor accepts the proof and marks the participant as winner

End-to-end

  • scripts/demoChallengeFullLifecycle.ts against mainnet completes without errors
  • Winner receives funds via autoDistribute
  • No-winner case routes correctly to the protocol multi-sig

Off-chain

  • PM2 workers all online after restart with mainnet env
  • claimsIndexer picks up the test challenge’s WinnerClaimed event
  • Webapp /me/challenges shows the test challenge correctly (on-chain truth)

Files that reference chain-specific addresses

These files contain testnet addresses and must be updated during migration. Most are env-driven, but a few hold hard-coded references:

FileWhat to update
.env, webapp/.env.localAll NEXT_PUBLIC_*_ADDR, *_ADDRESS, RPC URLs
webapp/public/deployments/lightchain.jsonAll contract addresses (regenerated by syncAbis.ts)
Vercel environment variablesProduction env vars
Fly.io secrets (fly secrets list)Off-chain worker env
scripts/ops/reimburseChallenge13.tsHard-coded MULTISIG constant — bump to mainnet multi-sig if reusing the script

The chain redeploy runbook (docs/chain-redeploy-runbook.md) covers the 6 surfaces that hold chain identity (contracts, bundled JSON, local envs, Vercel envs, Fly secrets, DB rows). The helper script scripts/ops/syncChainAddresses.ts automates the env propagation.


Timeline estimate

PhaseWhatBlocked on
Phase 1LightChain Foundation provides mainnet endpoints + JobRegistry addressLightChain mainnet launch
Phase 2Deploy our contracts to mainnet, set up post-deploy configPhase 1
Phase 3Run smoke + lifecycle demos against mainnetPhase 2
Phase 4Rotate multi-sig signers + attestor key to mainnet hot walletsPhase 3
Phase 5Switch production traffic (DNS, Vercel env, Fly secrets)Phase 4

The actual code changes are minimal (env vars + redeploy). The critical dependency is LightChain having an active worker network on mainnet.