Step-by-step deploy + initialization for the gkd_chain program. Handles real SOL — do a devnet
dry-run and ideally a security audit first. I only ever need public keys; you run the signing
commands yourself.
0. What you provide
| Item | What | Notes |
|---|---|---|
| Founder/creator pubkey | wallet that receives the 49% creator split | public key only |
| Admin/deployer keypair | deploys + initializes + upgrade authority | you hold/sign this |
| Program keypair | the program's address | reuse target/deploy/gkd_chain-keypair.json or generate a new one |
| Pyth SOL/USD account | price feed for the weekly $0.10 re-peg | the feed account pubkey on the target cluster |
| (optional) 2/3 multisig | 3 member pubkeys | for fee override + pause |
| (optional) charity ops | charity wallet pubkeys | added later via whitelist_charity |
8qCpYyRjdG8Y1jW5fu77XZ3qkWErgABhscEuuY4mRjnr is the devnet-only creator wallet. Never use it
as the mainnet founder/creator pubkey; mainnet initialization requires a separately supplied wallet.
1. Program id
The repo currently uses a placeholder id. Pick the program keypair you will deploy, then sync the id:
solana address -k target/deploy/gkd_chain-keypair.json # your real program id
# put that id in BOTH:
# programs/gkd_chain/src/lib.rs -> declare_id!("<ID>")
# Anchor.toml -> [programs.mainnet]/[programs.devnet] gkd_chain = "<ID>"
# src/chain/chain_config.js -> programId
Rebuild after changing declare_id! (Windows toolchain workaround — see POOL_MECHANISM.md):
rustup toolchain link 1.84.1-sbpf-solana-v1.51 "$HOME/.cache/solana/v1.51/platform-tools/rust"
export PATH="$HOME/.cargo/bin:$PATH"
( cd programs/gkd_chain && cargo-build-sbf --skip-tools-install )
2. Devnet dry-run first (free, no real SOL) — strongly recommended
tools/dryrun_local.js runs the full core init sequence (config → pools → token → record →
leaderboard → season) and verifies every account/PDA on a live validator.
- Local validator (fastest): on Windows enable Developer Mode (or run elevated) so
solana-test-validatorcan create its symlinks, then:solana-test-validator --reset solana program deploy target/deploy/gkd_chain.so --program-id target/deploy/gkd_chain-keypair.json --url localhost node tools/dryrun_local.js - Devnet (no Developer Mode needed):
solana config set --url devnet, fund the deployer (solana airdropin small chunks until you have ~6 SOL),solana program deploy ... --url devnet, thenRPC_URL=https://api.devnet.solana.com node tools/dryrun_local.js.
Expected: DRY-RUN PASS ✅. NFT/governance/fee-refresh paths need Metaplex/Pyth accounts and are
covered by cargo test -p gkd_chain + the Ed25519 layout cross-check.
Then validate the exact canonical browser client through the persistent strict verifier and the real Devnet program:
npm run verifier:key:init
npm run verifier:set:devnet
node verifier/server.js
npm run test:devnet:browser
This pays a real Devnet entry fee, finalizes the event ledger, obtains the configured verifier's
canonical signature, submits submit_verified_run directly from the browser client, and confirms the
RunRecord and leaderboard PDAs. It does not prove authoritative gameplay physics; close that security
boundary before enabling score-derived mainnet rewards.
3. Mainnet deploy
solana config set --url mainnet-beta
solana balance # deployer must hold enough SOL (program ~0.76 MB)
solana program deploy target/deploy/gkd_chain.so --program-id target/deploy/gkd_chain-keypair.json
Switch Anchor.toml [provider].cluster = "mainnet-beta" and chain_config.js cluster too.
chain_config.js intentionally keeps the mainnet program id empty. Add the audited mainnet id to
the GKD_PROGRAM_IDS["mainnet-beta"] entry only after deployment and full PDA initialization. The
validated devnet id must never be copied into the mainnet entry.
4. Initialize (admin signs, in order)
initialize_config(creator_wallet, normal_fee_lamports=270000)initialize_pools()— Game Pool + Charity Pool PDAsinitialize_token()— $420POP mint (authority = PDA)initialize_record_state()initialize_leaderboard()initialize_fee_gov(members, threshold=2, target_usd_micros=100000, pyth_sol_usd)initialize_season(season_id, mint_cap, score_target, reward_per_run)— per epoch, caps fromHUMAN_SEASON_CAPS × 10^9(season 33 = Chaos cap × 10^9)set_verifier(<verifier pubkey>)— turns on the on-chain anti-cheat score signature gate- (optional)
whitelist_charity(operator, name)per charity
tools/gate_e_submit_run.js already performs steps 1, 4, 5, 7 automatically when submitting a run;
the rest are one-time admin calls.
5. Go-live wiring
- Run the verifier with
STRICT_GATE_D=true,REQUIRE_ENTRY_TX=true,ALLOW_BROWSER_COMPAT=false, andENABLE_MEMO_SUBMIT=false. - Keep
VERIFIER_SECRET_KEY_B64in HSM/KMS-backed production secret storage; its pubkey must equal step 8. Never commit or expose the secret. - Replace the local JSON ledger with a durable transactional database and run the verifier as a monitored, redundant service.
- Use a dedicated production RPC with rate limits/failover; do not depend on a public RPC endpoint.
chain_config.js:cluster,programId,usePoolProgram=true,showLeaderboard=true.- Confirm the browser sends
submit_verified_rundirectly with a self-contained Ed25519 instruction before it. The program tolerates wallet-added ComputeBudget prefixes but never accepts a signature instruction after the submit; keep the legacy memo relay disabled. - Confirm entry fee flows into the Pool PDA and the real top-3 leaderboard renders.
- Transfer program/admin authority to the approved multisig and complete the independent audit before adding the mainnet program id.
Cost note
A ~0.76 MB program is deployed as an upgradeable buffer (~2× rent-exempt). Fund the deployer generously and verify the exact lamports with a deploy dry-run; you can later reclaim/close the buffer if needed.