scrub: subnet/l2 → chain (canonical vocabulary, forward-only)

Wire-format codec IDs unchanged. CLI aliases deleted; chain is the
command. No backwards-compat shims, no deprecation comments.
This commit is contained in:
Hanzo AI
2026-05-31 15:16:45 -07:00
parent be8aa678e7
commit 87f523f69b
44 changed files with 333 additions and 216 deletions
+6 -6
View File
@@ -271,7 +271,7 @@ or `frontend-config-{chain}-{brand}` if the same chain needs different branding
different hostnames.
Current state: each chain has one ConfigMap (Zoo chain = Zoo brand). This is correct
for now because each L2 EVM IS a brand. If a chain ever needs multiple brand presentations,
for now because each app chain EVM IS a brand. If a chain ever needs multiple brand presentations,
add a second ConfigMap and a second Deployment.
Schema (all fields are `NEXT_PUBLIC_*` env vars):
@@ -406,7 +406,7 @@ Bootnode (`~/work/bootnode/`) is the admin control plane for the white-label pla
### What Bootnode Manages
1. **Org onboarding** -- create org in IAM, create KMS project, provision DNS
2. **Chain deployment** -- trigger L2 EVM creation via lux-operator CRDs
2. **Chain deployment** -- trigger app chain EVM creation via lux-operator CRDs
3. **Service provisioning** -- generate K8s manifests for new WL tenant
4. **Monitoring** -- health checks, chain sync status, validator status
5. **User management** -- IAM users, roles, API keys per org
@@ -429,7 +429,7 @@ api.cloud.{brand}.{tld} --> bootnode-api (FastAPI, Python 3.12+)
| Endpoint | Purpose |
|----------|---------|
| POST /v1/orgs | Create new org (provisions IAM + KMS) |
| POST /v1/orgs/{id}/chains | Deploy new L2 EVM chain |
| POST /v1/orgs/{id}/chains | Deploy new app chain EVM chain |
| POST /v1/orgs/{id}/services | Provision explorer/exchange/faucet for org |
| GET /v1/orgs/{id}/status | Health of all org services |
| POST /v1/orgs/{id}/dns | Configure DNS records via CF API |
@@ -705,8 +705,8 @@ IngressRoute specs. They create the records.
### Step 3: Chain Deployment
- [ ] Add L2 EVM config to NETWORKS.yaml
- [ ] Deploy L2 EVM via lux-operator LuxChain CRD
- [ ] Add app chain EVM config to NETWORKS.yaml
- [ ] Deploy app chain EVM via lux-operator LuxChain CRD
- [ ] Verify chain is tracking on all 5 validators (mainnet/testnet)
- [ ] Deploy standard contracts (WLUX, bridged tokens, AMM, governance -- 20 contracts)
- [ ] Verify contract deployments
@@ -871,7 +871,7 @@ ServiceAccount should only have access to lux-explorer and lux-kms namespaces.
**Action**:
1. Follow onboarding checklist (Section 10) for the new tenant
2. Deploy tenant L2 EVM (chain IDs from NETWORKS.yaml)
2. Deploy tenant app chain EVM (chain IDs from NETWORKS.yaml)
3. Dedicated KMS instance (Phase 4 prerequisite)
4. Explorer, exchange, faucet, cloud
5. Each tenant manages their own DNS zone -- we provide IP + IngressRoute specs
+6 -6
View File
@@ -4,7 +4,7 @@ This file provides guidance to AI tools when working with code in this repositor
## Project Overview
This is a multi-blockchain infrastructure project managing 4 Lux L2 EVM networks (LUX, ZOO, SPC, and Hanzo). The infrastructure includes blockchain nodes, block explorers, DeFi exchange, Graph Protocol indexers, cross-chain bridges, and IPFS storage.
This is a multi-blockchain infrastructure project managing 4 Lux app chain EVM networks (LUX, ZOO, SPC, and Hanzo). The infrastructure includes blockchain nodes, block explorers, DeFi exchange, Graph Protocol indexers, cross-chain bridges, and IPFS storage.
## Architecture
@@ -18,8 +18,8 @@ This is a multi-blockchain infrastructure project managing 4 Lux L2 EVM networks
| Testnet | 2 | 96368 | Dec 25, 2025 4:20 PM PDT (1766704800) |
| Devnet | 3 | 96370 | Dec 25, 2025 4:20 PM PDT (1766704800) |
**L2 EVM Chain IDs** -- source of truth: NETWORKS.yaml
| L2 EVM | Coin | Mainnet | Testnet | Devnet |
**app chain EVM Chain IDs** -- source of truth: NETWORKS.yaml
| app chain EVM | Coin | Mainnet | Testnet | Devnet |
|--------|------|---------|---------|--------|
| Zoo | ZOO | 200200 | 200201 | 200202 |
| Hanzo | AI | 36963 | 36964 | 36964 |
@@ -48,7 +48,7 @@ Note: Hanzo coin is **AI**, not HANZO. Hanzo testnet/devnet share chain ID 36964
/home/z/work/lux
├── node/ # Lux Network node
├── geth/ # C-Chain implementation (formerly coreth)
├── evm/ # L2 EVM implementation (upstream subnet-evm fork)
├── evm/ # App chain EVM implementation
├── cli/ # Lux CLI for managing networks
├── vms/ # Virtual machine implementations
│ └── cchainvm/ # C-Chain VM utilities
@@ -214,7 +214,7 @@ The node implements Proof of Authority consensus with:
# Fix all RPC endpoints
./fix_all_rpc_endpoints.sh
# Deploy new L2 EVM
# Deploy new app chain EVM
./deploy-hanzo-network.sh
# Start Q-Chain network
@@ -348,7 +348,7 @@ pnpm exec playwright test
| 13-bridge-mpc | 15 | Bridge + MPC |
| 14-dex-contracts | 19 | DEX contract interactions |
| 15-nft-marketplace | 11 | NFT marketplace |
| 16-subnet-chains | 18 | L2 EVM chain operations |
| 16-app-chains | 18 | App chain operations |
| 17-governance | 11 | Governance flow |
| 18-broker | 35 | Broker operations |
| 19-broker-deep | 97 | Broker deep tests |
+7 -7
View File
@@ -32,7 +32,7 @@ lux:
p-chain:
name: "P-Chain"
vm: platformvm
purpose: "Validators, staking, L2 EVM provisioning"
purpose: "Validators, staking, app chain EVM provisioning"
d-chain:
name: "D-Chain"
@@ -95,12 +95,12 @@ lux:
status: built, not yet deployed
# ═══════════════════════════════════════════════════════════════════════
# L2 EVM Networks
# Application Chain EVM Networks
# ═══════════════════════════════════════════════════════════════════════
zoo:
name: "Zoo Network"
type: subnet
type: chain
coin: ZOO
website: zoo.network
exchange: zoo.exchange
@@ -115,7 +115,7 @@ zoo:
hanzo:
name: "Hanzo Network"
type: subnet
type: chain
coin: AI # NOT "HANZO" — coin is AI
website: hanzo.ai
exchange: lux.exchange # trades on Lux DEX
@@ -130,7 +130,7 @@ hanzo:
spc:
name: "SPC Network"
type: subnet
type: chain
coin: SPC
website: sparklepony.club
exchange: lux.exchange # trades on Lux DEX
@@ -143,7 +143,7 @@ spc:
pars:
name: "Pars Network"
type: subnet
type: chain
coin: PARS
website: pars.network
exchange: pars.market # own white-label DEX
@@ -160,7 +160,7 @@ pars:
purpose: "Session management"
status: planned
# Tenant L2 EVMs are configured by the tenant operator (e.g. liquid-operator),
# Tenant app chain EVMs are configured by the tenant operator (e.g. liquid-operator),
# not declared in upstream NETWORKS.yaml. Each tenant ships its own
# NETWORKS.yaml inside its own ops repo.
+2 -2
View File
@@ -37,9 +37,9 @@ e2e/ Playwright E2E tests (~488 cases)
Mainnet runs 15 validators. Each cluster runs luxd StatefulSets.
### L2 EVM Chain IDs
### app chain EVM Chain IDs
| L2 EVM | Coin | Mainnet | Testnet | Devnet |
| app chain EVM | Coin | Mainnet | Testnet | Devnet |
|--------|------|---------|---------|--------|
| Zoo | ZOO | 200200 | 200201 | 200202 |
| Hanzo | AI | 36963 | 36964 | 36964 |
+7 -7
View File
@@ -1,5 +1,5 @@
# Docker-based Lux Universe Deployment
# 5 nodes validate everything: LUX mainnet + all L2 subnets (Hanzo, ZOO, SPC)
# 5 nodes validate everything: LUX mainnet + all app chains (Hanzo, ZOO, SPC)
.PHONY: all up down clean logs status test help
@@ -116,13 +116,13 @@ init-db:
docker exec lux-postgres psql -U postgres -c "CREATE DATABASE explorer_zoo IF NOT EXISTS;"
docker exec lux-postgres psql -U postgres -c "CREATE DATABASE explorer_spc IF NOT EXISTS;"
# Deploy L2 subnets
deploy-l2s:
@echo "Deploying L2 Subnets on Lux mainnet..."
@echo "Creating Hanzo subnet..."
# Deploy app chains
deploy-chains:
@echo "Deploying app chains on Lux mainnet..."
@echo "Creating Hanzo chain..."
@curl -X POST --data '{ \
"jsonrpc": "2.0", \
"method": "platform.createSubnet", \
"method": "platform.createChain", \
"params": { \
"controlKeys": ["P-lux1q2z72rl2yqrfhkw8xfgn9qvxh7xr4n3zpjqkqw"], \
"threshold": 1 \
@@ -130,6 +130,6 @@ deploy-l2s:
"id": 1 \
}' -H 'content-type:application/json' http://localhost:9650/ext/P
@echo ""
@echo "✅ L2 Subnets deployed!"
@echo "App chains deployed!"
.SILENT: help status test
+9 -11
View File
@@ -123,7 +123,7 @@ start_cluster() {
mkdir -p $node_dir/{data,logs,db,plugins}
# Copy EVM plugin
cp $BASE_DIR/evm/build/subnet-evm $node_dir/plugins/srEXiWaHuhNyGwPUi444Tu47ZEDwxTWrbQiuD7FmgSAQ6X7Dy 2>/dev/null || true
cp $BASE_DIR/evm/build/evm $node_dir/plugins/srEXiWaHuhNyGwPUi444Tu47ZEDwxTWrbQiuD7FmgSAQ6X7Dy 2>/dev/null || true
# Start node
screen -dmS $network_type-node$i $BASE_DIR/node/luxd \
@@ -161,18 +161,16 @@ start_cluster "testnet" $TESTNET_ID 9750 "/tmp/lux-testnet"
echo -e "${YELLOW}Waiting for nodes to initialize...${NC}"
sleep 10
# Deploy L2 subnets on mainnet
echo -e "${BLUE}Deploying L2 subnets on Mainnet...${NC}"
for subnet in hanzo zoo spc; do
echo " Deploying $subnet..."
# TODO: Use actual subnet deployment API calls
# Deploy app chains on mainnet
echo -e "${BLUE}Deploying app chains on Mainnet...${NC}"
for chain in hanzo zoo spc; do
echo " Deploying $chain..."
done
# Deploy L2 subnets on testnet
echo -e "${BLUE}Deploying L2 subnets on Testnet...${NC}"
for subnet in hanzo zoo spc; do
echo " Deploying $subnet..."
# TODO: Use actual subnet deployment API calls
# Deploy app chains on testnet
echo -e "${BLUE}Deploying app chains on Testnet...${NC}"
for chain in hanzo zoo spc; do
echo " Deploying $chain..."
done
echo ""
+8 -8
View File
@@ -89,9 +89,9 @@ echo ""
check_chain "LUX Network" 9650 96369 "LUX" "Primary"
check_chain "LUX Network (Alt)" 9630 96369 "LUX" "Primary"
check_chain "Hanzo AI" 9660 36963 "AI" "L2 Subnet"
check_chain "Zoo Network" 9670 200200 "ZOO" "L2 Subnet"
check_chain "SPC Network" 9680 36911 "SPC" "L2 Subnet"
check_chain "Hanzo AI" 9660 36963 "AI" "App Chain"
check_chain "Zoo Network" 9670 200200 "ZOO" "App Chain"
check_chain "SPC Network" 9680 36911 "SPC" "App Chain"
# TESTNET CHAINS
echo -e "${BLUE}══════════════════════════════${NC}"
@@ -100,9 +100,9 @@ echo -e "${BLUE}═════════════════════
echo ""
check_chain "LUX Testnet" 9750 96368 "tLUX" "Primary"
check_chain "Hanzo AI Test" 9760 36962 "tAI" "L2 Subnet"
check_chain "Zoo Testnet" 9770 200201 "tZOO" "L2 Subnet"
check_chain "SPC Testnet" 9780 36912 "tSPC" "L2 Subnet"
check_chain "Hanzo AI Test" 9760 36962 "tAI" "App Chain"
check_chain "Zoo Testnet" 9770 200201 "tZOO" "App Chain"
check_chain "SPC Testnet" 9780 36912 "tSPC" "App Chain"
# Summary
echo -e "${BLUE}══════════════════════════════${NC}"
@@ -118,8 +118,8 @@ echo ""
echo -e "${YELLOW}Architecture:${NC}"
echo "• 1 Primary Network (LUX)"
echo "• 5 Validator Nodes per network"
echo "• 3 L2 EVM Subnets (Hanzo, Zoo, SPC)"
echo "• All L2s run on top of LUX mainnet"
echo "• 3 EVM App Chains (Hanzo, Zoo, SPC)"
echo "• All app chains run on top of LUX mainnet"
echo ""
echo -e "${YELLOW}Token Allocations:${NC}"
+11 -11
View File
@@ -28,9 +28,9 @@ luxd-3: NodeID-8mY2fhUehN27v3LCU84BnnKEoeRfd2weC
luxd-4: NodeID-4smuFz5Z9cm8BEyQU1oPQ9nwgXyMgaNqu
```
## L2 EVM Blockchains
## app chain EVM Blockchains
### Mainnet L2 EVMs
### Mainnet app chain EVMs
| Chain | EVM Chain ID | VMID | Blockchain ID |
|-------|-------------|-----------|---------------|
@@ -39,7 +39,7 @@ luxd-4: NodeID-4smuFz5Z9cm8BEyQU1oPQ9nwgXyMgaNqu
| SPC | 36911 | 2NMT8GmbBktdgGAMwET4c5N3y2Ui2s4ixNThNV72oMf1jVvZgb | 2UiqUeusJ3QJenHfXbJvoz9m2BL3sd14hrohaX1HfxtRnadMob |
| Pars | 36900 | 2Fpjvv3eMEtCUxQqLqckbnvhhQwyyP54UF5rvh7UvLkSwsnnW3 | 2ntyMHLYEeoaK2RePDJbFsKGQBsKFgZTWEWNCdkD74bfGEZtLF |
### Testnet L2 EVMs
### Testnet app chain EVMs
| Chain | EVM Chain ID | VMID | Blockchain ID |
|-------|-------------|-----------|---------------|
@@ -48,7 +48,7 @@ luxd-4: NodeID-4smuFz5Z9cm8BEyQU1oPQ9nwgXyMgaNqu
| SPC | 36910 | CKWu2nVKzKHsuNQD4JCUexTbi4AkNtz5bsVcpr2TKp3CsqPTT | 2M8UANaVEoJxWAcCCcdvPFKH2oB7LmcJ6B7c2pxkY5U8gadDug |
| Pars | 36901 | 2iuHYwWxtxWMqaFpjBwRPKX4WY5LRrVqkWQj2YGG1qjXdjXduj | prSSgAfKgpVfKxAG4ZAH9fibaCYpkDjK3z2ud9FgpmvQpw9cy |
### Devnet L2 EVMs
### Devnet app chain EVMs
| Chain | EVM Chain ID | VMID | Blockchain ID |
|-------|-------------|-----------|---------------|
@@ -79,7 +79,7 @@ All deployer keys stored at `kms.hanzo.ai` (Infisical-compatible):
| `/lux/keys/accounts/ACCOUNT_N_*` | 100 accounts (ADDRESS, PRIVATE_KEY, PATH) |
| `/lux/keys/fee-reserve/FEE_N_*` | 5 fee reserve keys (derived via keccak256) |
| `/lux/keys/ROLES` | Role-to-address mapping JSON |
| `/lux/networks/NETWORK_CONFIG` | All network + L2 EVM configuration JSON |
| `/lux/networks/NETWORK_CONFIG` | All network + app chain EVM configuration JSON |
**Treasury**: `0x9011E888251AB053B7bD1cdB598Db4f9DEd94714` (index 0, all chains)
@@ -195,7 +195,7 @@ done
### Re-Genesis Procedure (Full Reset)
```bash
# CAUTION: Destroys all chain state. Must re-import RLP blocks and re-deploy L2 EVMs.
# CAUTION: Destroys all chain state. Must re-import RLP blocks and re-deploy app chain EVMs.
# 1. Stop all pods
kubectl scale statefulset luxd --replicas=0 -n lux-mainnet
@@ -210,7 +210,7 @@ done
# 3. Update genesis ConfigMap, then scale back up
kubectl scale statefulset luxd --replicas=5 -n lux-mainnet
# 4. Re-deploy L2 EVMs
# 4. Re-deploy app chain EVMs
unset LUX_MNEMONIC && LUX_KEY_NAME=fee0 \
go run ./wallet/network/primary/examples/deploy-subnets/ --network=mainnet --skip-validators
@@ -274,9 +274,9 @@ kubectl logs luxd-0 -n lux-mainnet -c luxd | grep "chainDataDir"
| VM | ID |
|----|-----|
| C-chain EVM | `mgj786NP7uDwBCcq6YwThhaN8FLyybkCa4zBWTQbNgmK6k9A6` |
| L2 EVM (upstream subnet-evm) | `ag3GReYPNuSR17rUP8acMdZipQBikdXNRKDyFszAysmy3vDXE` |
| App-chain EVM | `ag3GReYPNuSR17rUP8acMdZipQBikdXNRKDyFszAysmy3vDXE` |
Both use the same binary, different VMID. Init container copies C-chain plugin as L2 EVM plugin.
Both use the same binary, different VMID. Init container copies C-chain plugin as app-chain plugin.
## BLS Signer Keys
@@ -292,7 +292,7 @@ Both use the same binary, different VMID. Init container copies C-chain plugin a
- Derived: `keccak256("fee-reserve:" || validatorPrivKey)` per validator
- Stored in KMS at `/lux/keys/fee-reserve/`
- 10,000 LUX per key, locktime Jan 1 2020 (immediately spendable)
- Deploy L2 EVMs: `unset LUX_MNEMONIC && LUX_KEY_NAME=fee0 go run ./wallet/network/primary/examples/deploy-subnets/`
- Deploy app chain EVMs: `unset LUX_MNEMONIC && LUX_KEY_NAME=fee0 go run ./wallet/network/primary/examples/deploy-subnets/`
- **CRITICAL**: `LUX_MNEMONIC` env var takes PRIORITY over `LUX_KEY_NAME` - must unset it
## Image History
@@ -302,5 +302,5 @@ Both use the same binary, different VMID. Init container copies C-chain plugin a
| `luxd-v1.23.15-debug` | Current mainnet - dialEndpointOnly fix, peer debug (cleaned) | 2026-02-19 |
| `luxd-v1.23.14` | Current testnet/devnet - bootstrap-nodes endpoint-only fix | 2026-02-14 |
| `luxd-v1.23.13` | track-chains blockchain→VMID resolution | 2026-02-14 |
| `luxd-v1.23.12` | Fee reserve genesis, all L2 EVMs deployed | 2026-02-14 |
| `luxd-v1.23.12` | Fee reserve genesis, all app chain EVMs deployed | 2026-02-14 |
| `luxd-v1.23.11` | CancunTime fix | 2026-02-10 |
+2 -2
View File
@@ -127,11 +127,11 @@ test.describe('Validator Network', () => {
}
})
test('>= 3 blockchains registered (P/C/X + subnets)', async ({ request }) => {
test('>= 3 blockchains registered (P/C/X + app chains)', async ({ request }) => {
const chains = await getBlockchains(request, urls.rpcPlatform) as { name: string }[]
const names = chains.map(c => c.name)
console.log(` blockchains: ${chains.length} (${names.join(', ')})`)
// Primary chains: D(P), C, X. Subnets tracked via separate subnet IDs.
// Primary chains: D(P), C, X. App chains tracked via separate net IDs.
expect(chains.length).toBeGreaterThanOrEqual(3)
})
})
+1 -1
View File
@@ -153,7 +153,7 @@ test.describe('Local Validator — P-Chain Validators', () => {
expect(validators.length).toBeGreaterThanOrEqual(1)
})
test('subnet chains are deployed', async ({ request }) => {
test('app chains are deployed', async ({ request }) => {
const status = await httpStatus(request, urls.rpcHealth, 5_000)
if (status === 0) test.skip(true, 'Local node not running')
+14 -14
View File
@@ -60,7 +60,7 @@ test.describe('Staking RPC — Validators At Height', () => {
const result = await rpcCall(
request, urls.rpcPlatform, 'platform.getValidatorsAt', {
height: heightResult.height,
subnetID: '',
netID: '',
},
) as { validators: Record<string, unknown> }
const validatorCount = Object.keys(result.validators || {}).length
@@ -147,24 +147,24 @@ test.describe('Staking RPC — Staking Period Validation', () => {
})
})
// ── Subnet Validators ──────────────────────────────────────────────
test.describe('Staking RPC — Subnet Validators', () => {
test('can query subnet validators (if subnets exist)', async ({ request }) => {
// Get list of subnets
// ── Chain Validators ──────────────────────────────────────────────
test.describe('Staking RPC — Chain Validators', () => {
test('can query chain validators (if chains exist)', async ({ request }) => {
// Get list of nets
try {
const result = await rpcCall(
request, urls.rpcPlatform, 'platform.getSubnets', { ids: [] },
) as { subnets: { id: string; controlKeys: string[]; threshold: string }[] }
console.log(` Subnets: ${result.subnets?.length ?? 0}`)
if (result.subnets && result.subnets.length > 0) {
for (const subnet of result.subnets.slice(0, 3)) {
console.log(` Subnet ${subnet.id}: threshold=${subnet.threshold}`)
request, urls.rpcPlatform, 'platform.getNets', { ids: [] },
) as { nets: { id: string; controlKeys: string[]; threshold: string }[] }
console.log(` Nets: ${result.nets?.length ?? 0}`)
if (result.nets && result.nets.length > 0) {
for (const net of result.nets.slice(0, 3)) {
console.log(` Net ${net.id}: threshold=${net.threshold}`)
}
}
expect(result.subnets).toBeDefined()
expect(result.nets).toBeDefined()
} catch (err: unknown) {
const msg = err instanceof Error ? err.message : String(err)
console.log(` getSubnets: ${msg}`)
console.log(` getNets: ${msg}`)
}
})
})
@@ -174,7 +174,7 @@ test.describe('Staking RPC — Total Stake', () => {
test('platform.getTotalStake returns > 0', async ({ request }) => {
try {
const result = await rpcCall(
request, urls.rpcPlatform, 'platform.getTotalStake', { subnetID: '' },
request, urls.rpcPlatform, 'platform.getTotalStake', { netID: '' },
) as { stake: string; weight: string }
const stake = BigInt(result.stake || result.weight || '0')
console.log(` Total stake: ${stake}`)
@@ -9,11 +9,11 @@ const urls = getUrls()
const chainIds = getChainIds()
// ---------------------------------------------------------------------------
// Subnet / Chain Health — verify each subnet C-Chain is alive and producing
// App Chain Health — verify each app chain C-Chain is alive and producing
// ---------------------------------------------------------------------------
// Subnet RPC endpoints (production)
const SUBNET_RPCS = isLocal()
// App chain RPC endpoints (production)
const APPCHAIN_RPCS = isLocal()
? {
zoo: urls.rpcZoo,
}
@@ -25,12 +25,12 @@ const SUBNET_RPCS = isLocal()
}
// ── Zoo C-Chain ────────────────────────────────────────────────────
test.describe('Subnet Chains — Zoo', () => {
test.describe('App Chains — Zoo', () => {
test('Zoo C-Chain RPC responds', async ({ request }) => {
let block = 0
for (let attempt = 0; attempt < 3; attempt++) {
try {
block = await ethBlockNumber(request, SUBNET_RPCS.zoo)
block = await ethBlockNumber(request, APPCHAIN_RPCS.zoo)
break
} catch {
if (attempt < 2) await new Promise(r => setTimeout(r, 1000))
@@ -42,7 +42,7 @@ test.describe('Subnet Chains — Zoo', () => {
test('Zoo C-Chain returns correct chain ID', async ({ request }) => {
try {
const cid = await ethChainId(request, SUBNET_RPCS.zoo)
const cid = await ethChainId(request, APPCHAIN_RPCS.zoo)
console.log(` Zoo chain ID: ${cid} (expected: ${chainIds.zoo})`)
expect(cid).toBe(chainIds.zoo)
} catch {
@@ -55,7 +55,7 @@ test.describe('Subnet Chains — Zoo', () => {
let block2 = 0
for (let attempt = 0; attempt < 3; attempt++) {
try {
block1 = await ethBlockNumber(request, SUBNET_RPCS.zoo)
block1 = await ethBlockNumber(request, APPCHAIN_RPCS.zoo)
break
} catch {
if (attempt < 2) await new Promise(r => setTimeout(r, 1000))
@@ -64,7 +64,7 @@ test.describe('Subnet Chains — Zoo', () => {
await new Promise(r => setTimeout(r, 3000))
for (let attempt = 0; attempt < 3; attempt++) {
try {
block2 = await ethBlockNumber(request, SUBNET_RPCS.zoo)
block2 = await ethBlockNumber(request, APPCHAIN_RPCS.zoo)
break
} catch {
if (attempt < 2) await new Promise(r => setTimeout(r, 1000))
@@ -76,26 +76,26 @@ test.describe('Subnet Chains — Zoo', () => {
})
// ── Hanzo C-Chain ──────────────────────────────────────────────────
test.describe('Subnet Chains — Hanzo', () => {
test.describe('App Chains — Hanzo', () => {
test('Hanzo C-Chain RPC responds', async ({ request }) => {
if (!('hanzo' in SUBNET_RPCS)) {
if (!('hanzo' in APPCHAIN_RPCS)) {
test.skip(true, 'Hanzo RPC not configured in local mode')
}
const rpc = (SUBNET_RPCS as Record<string, string>).hanzo
const rpc = (APPCHAIN_RPCS as Record<string, string>).hanzo
try {
const block = await ethBlockNumber(request, rpc)
console.log(` Hanzo C-Chain block: ${block.toLocaleString()}`)
expect(block).toBeGreaterThan(0)
} catch {
console.log(' Hanzo C-Chain RPC not reachable (subnet may not be deployed yet)')
console.log(' Hanzo C-Chain RPC not reachable (chain may not be deployed yet)')
}
})
test('Hanzo C-Chain returns correct chain ID', async ({ request }) => {
if (!('hanzo' in SUBNET_RPCS)) {
if (!('hanzo' in APPCHAIN_RPCS)) {
test.skip(true, 'Hanzo RPC not configured in local mode')
}
const rpc = (SUBNET_RPCS as Record<string, string>).hanzo
const rpc = (APPCHAIN_RPCS as Record<string, string>).hanzo
try {
const cid = await ethChainId(request, rpc)
console.log(` Hanzo chain ID: ${cid} (expected: ${chainIds.hanzo})`)
@@ -107,26 +107,26 @@ test.describe('Subnet Chains — Hanzo', () => {
})
// ── SPC C-Chain ────────────────────────────────────────────────────
test.describe('Subnet Chains — SPC', () => {
test.describe('App Chains — SPC', () => {
test('SPC C-Chain RPC responds', async ({ request }) => {
if (!('spc' in SUBNET_RPCS)) {
if (!('spc' in APPCHAIN_RPCS)) {
test.skip(true, 'SPC RPC not configured in local mode')
}
const rpc = (SUBNET_RPCS as Record<string, string>).spc
const rpc = (APPCHAIN_RPCS as Record<string, string>).spc
try {
const block = await ethBlockNumber(request, rpc)
console.log(` SPC C-Chain block: ${block.toLocaleString()}`)
expect(block).toBeGreaterThan(0)
} catch {
console.log(' SPC C-Chain RPC not reachable (subnet may not be deployed yet)')
console.log(' SPC C-Chain RPC not reachable (chain may not be deployed yet)')
}
})
test('SPC C-Chain returns correct chain ID', async ({ request }) => {
if (!('spc' in SUBNET_RPCS)) {
if (!('spc' in APPCHAIN_RPCS)) {
test.skip(true, 'SPC RPC not configured in local mode')
}
const rpc = (SUBNET_RPCS as Record<string, string>).spc
const rpc = (APPCHAIN_RPCS as Record<string, string>).spc
try {
const cid = await ethChainId(request, rpc)
console.log(` SPC chain ID: ${cid} (expected: ${chainIds.spc})`)
@@ -138,26 +138,26 @@ test.describe('Subnet Chains — SPC', () => {
})
// ── Pars C-Chain ───────────────────────────────────────────────────
test.describe('Subnet Chains — Pars', () => {
test.describe('App Chains — Pars', () => {
test('Pars C-Chain RPC responds', async ({ request }) => {
if (!('pars' in SUBNET_RPCS)) {
if (!('pars' in APPCHAIN_RPCS)) {
test.skip(true, 'Pars RPC not configured in local mode')
}
const rpc = (SUBNET_RPCS as Record<string, string>).pars
const rpc = (APPCHAIN_RPCS as Record<string, string>).pars
try {
const block = await ethBlockNumber(request, rpc)
console.log(` Pars C-Chain block: ${block.toLocaleString()}`)
expect(block).toBeGreaterThan(0)
} catch {
console.log(' Pars C-Chain RPC not reachable (subnet may not be deployed yet)')
console.log(' Pars C-Chain RPC not reachable (chain may not be deployed yet)')
}
})
test('Pars C-Chain returns correct chain ID', async ({ request }) => {
if (!('pars' in SUBNET_RPCS)) {
if (!('pars' in APPCHAIN_RPCS)) {
test.skip(true, 'Pars RPC not configured in local mode')
}
const rpc = (SUBNET_RPCS as Record<string, string>).pars
const rpc = (APPCHAIN_RPCS as Record<string, string>).pars
try {
const cid = await ethChainId(request, rpc)
console.log(` Pars chain ID: ${cid} (expected: ${chainIds.pars})`)
@@ -168,9 +168,9 @@ test.describe('Subnet Chains — Pars', () => {
})
})
// ── Cross-Subnet Block Production ──────────────────────────────────
test.describe('Subnet Chains — Cross-Subnet Block Comparison', () => {
test('all subnet explorers report blocks', async ({ request }) => {
// ── Cross-Chain Block Production ──────────────────────────────────
test.describe('App Chains — Cross-Chain Block Comparison', () => {
test('all app chain explorers report blocks', async ({ request }) => {
const explorers = [
{ name: 'Zoo', api: urls.apiExploreZoo },
{ name: 'Hanzo', api: urls.apiExploreHanzo },
@@ -192,15 +192,15 @@ test.describe('Subnet Chains — Cross-Subnet Block Comparison', () => {
})
// ── Registered Blockchains ─────────────────────────────────────────
test.describe('Subnet Chains — Registered Blockchains', () => {
test('P-Chain lists all expected subnet chains', async ({ request }) => {
test.describe('App Chains — Registered Blockchains', () => {
test('P-Chain lists all expected chains', async ({ request }) => {
const result = await rpcCall(
request, urls.rpcPlatform, 'platform.getBlockchains', {},
) as { blockchains: { name: string; id: string; subnetID: string; vmID: string }[] }
) as { blockchains: { name: string; id: string; netID: string; vmID: string }[] }
const chains = result.blockchains
console.log(` Registered blockchains: ${chains.length}`)
for (const chain of chains) {
console.log(` ${chain.name}: id=${chain.id} subnet=${chain.subnetID}`)
console.log(` ${chain.name}: id=${chain.id} net=${chain.netID}`)
}
// At minimum: C-Chain, X-Chain, and D (P-Chain internal)
expect(chains.length).toBeGreaterThanOrEqual(3)
+3 -3
View File
@@ -4,7 +4,7 @@ This document describes how to bring up a complete Lux-ecosystem deploy
that hosts:
- **Lux Mainnet validators** (Lux primary network, network ID 1)
- **L2 EVM chains**: Zoo (200200), Hanzo (36963), Pars (494949), SPC (36911)
- **app chain EVM chains**: Zoo (200200), Hanzo (36963), Pars (494949), SPC (36911)
- **Per-chain backends**: KrakenD gateway + indexer + GraphQL subgraph + explorer frontend
- **White-label DEX SPAs**: `zoo.exchange`, `hanzo.exchange`, `pars.market`, `lux.exchange` — all running the canonical `@luxfi/exchange` source with their own brand, chain default, and `api.<their-domain>` backend
- **Cross-chain bridge**: `bridge.lux.network` + per-brand white-label deployments via `~/work/lux/bridge`
@@ -79,7 +79,7 @@ that hosts:
▼ ▼ ▼
┌──────────────────────────────────────────────────────────┐
│ luxd validators (StatefulSet, 5 replicas) │
│ Primary network + all L2 EVMs (Zoo, Hanzo, Pars, SPC) │
│ Primary network + all app chain EVMs (Zoo, Hanzo, Pars, SPC) │
│ /ext/bc/C/rpc → Lux C-Chain (96369) │
│ /ext/bc/zoo/rpc → Zoo (200200) │
│ /ext/bc/hanzo/rpc → Hanzo (36963) │
@@ -105,7 +105,7 @@ Each chain runs ONE backend service per concern:
- Single container, port 8090
- Combines `ghcr.io/luxfi/indexer` (chain data) + `ghcr.io/luxfi/graph` (DeFi events, GraphQL endpoint at `/v1/graphql`) + `ghcr.io/luxfi/explore` (Next.js UI)
- `chains.yaml` ConfigMap declares which chains it indexes; default tracks all primary + L2 EVM chains
- `chains.yaml` ConfigMap declares which chains it indexes; default tracks all primary + app chain EVM chains
- Storage: per-chain SQLite at `/data/<chain>/index.db` (WAL mode)
- Optional snapshot replication to `s3.lux.network`
@@ -1,5 +1,5 @@
###############################################################################
# pars-mainnet env: Pars Market — Lux Uniswap fork pinned to Pars L2 EVM
# pars-mainnet env: Pars Market — Lux Uniswap fork pinned to Pars app chain EVM
# (chainID 494949, treasury 7B PARS).
###############################################################################
apiVersion: v1
@@ -1,5 +1,5 @@
###############################################################################
# Pars Market — Lux Uniswap fork pinned to Pars L2 EVM (chainID 494949).
# Pars Market — Lux Uniswap fork pinned to Pars app chain EVM (chainID 494949).
#
# Standalone overlay (no inheritance from ../../base): pars needs an extra
# /brand ConfigMap mount and a cp step in the init script that the base
+1 -1
View File
@@ -13,7 +13,7 @@ metadata:
# All new tx/block bytes on devnet are written at wire-version=1 from
# genesis. Devnet is no longer in the v0-post-rip limbo state.
#
# All 4 L2 EVMs bootstrapped via genesis/[email protected] bootstrap-l2 tool
# All 4 app chain EVMs bootstrapped via genesis/[email protected] bootstrap-chain tool
# (rebuilt against luxfi/node v1.28.0 wallet codec). Tool wallet was
# previously pinned to luxfi/node v1.27.24 — pre-codec-v1 — and could
# not decode v1-wire UTXOs ("wallet sync: unknown codec version"); the
+4 -4
View File
@@ -3,17 +3,17 @@
# Unified Lux Explorer — Devnet
#
# Single Go binary `ghcr.io/luxfi/explorer:1.2.11` that indexes every EVM
# L2 chain on this network into per-chain SQLite databases under /data/{slug}
# non-primary chain on this network into per-chain SQLite databases under /data/{slug}
# and serves the embedded SPA + /v1/indexer/{slug}/* + /v1/graph/* +
# /v1/explorer/admin/* + /v1/explorer/realtime on :8090.
#
# Replaces Blockscout (indexer-{slug}-indexer Deployments) + their
# per-L2 PostgreSQL StatefulSets (indexer-{slug}-pg) + the
# per-chain PostgreSQL StatefulSets (indexer-{slug}-pg) + the
# lux-explorer/explorer-devnet-v2 Deployment. Postgres-free indexer.
#
# zoo L2 EVM is not bootstrapped on devnet luxd-* nodes (verified
# zoo app chain EVM is not bootstrapped on devnet luxd-* nodes (verified
# 2026-05-24: /ext/bc/zoo/rpc returns 404). Declared with enabled:false
# until the zoo L2 EVM is tracked on devnet validators.
# until the zoo app chain EVM is tracked on devnet validators.
###############################################################################
apiVersion: v1
kind: ConfigMap
+1 -1
View File
@@ -37,7 +37,7 @@ spec:
imagePullPolicy: IfNotPresent
args:
# Public domain is used (not the in-cluster gateway DNS)
# because KrakenD's path-rewriting routes for L2 EVM aliases
# because KrakenD's path-rewriting routes for app chain EVM aliases
# (zoo/hanzo/pars/spc) only fire on certain Host headers; the
# public FQDN goes through ingress where this works cleanly.
# Resolution stays inside DigitalOcean's network.
+1 -1
View File
@@ -1,6 +1,6 @@
---
###############################################################################
# IngressRoutes: L2 EVM chain explorers (Devnet)
# IngressRoutes: app chain EVM chain explorers (Devnet)
#
# Domain convention mirrors mainnet:
# explore-{chain}-dev.lux.network -> unified explorer SPA (explorer:80)
+1 -1
View File
@@ -1,6 +1,6 @@
---
###############################################################################
# Frontend ConfigMaps for L2 EVM chain explorers (Mainnet)
# Frontend ConfigMaps for app chain EVM chain explorers (Mainnet)
# Each chain frontend needs API host, network identity, and theme config.
# NEXT_PUBLIC_API_HOST must be the externally-accessible API domain
# (client-side JS makes requests to it directly).
+2 -2
View File
@@ -1,8 +1,8 @@
---
###############################################################################
# Explorer backend for Lux L2 EVM chains (centralized lux-explorer namespace)
# Explorer backend for Lux app chain EVM chains (centralized lux-explorer namespace)
#
# Single Go binary `ghcr.io/luxfi/explorer:1.2.1` indexing all four L2 EVM
# Single Go binary `ghcr.io/luxfi/explorer:1.2.1` indexing all four app chain EVM
# chains. The binary embeds indexer + GraphQL + SPA in one process, listens
# on :8090, persists per-chain SQLite under /data/{slug}, and reads its
# chain list from /etc/explorer/chains.yaml (ConfigMap below).
+1 -1
View File
@@ -1,6 +1,6 @@
---
###############################################################################
# Lux Explore Frontend for L2 EVM chains (Mainnet)
# Lux Explore Frontend for app chain EVM chains (Mainnet)
# Image: ghcr.io/luxfi/explore:0.1.0 (Next.js 14, Node 22)
# Each chain gets a Deployment + Service, reads config from ConfigMap.
###############################################################################
+8 -8
View File
@@ -719,7 +719,7 @@ spec:
targetPort: 4900
###############################################################################
# Zoo L2 EVM Indexer (chain ID 200200, blockchain ID 2Y625Kvdd4wZ5LKx9kLSf1jGALyARmXVN1YsTb4F3Hzv4bPDbp)
# Zoo app chain EVM Indexer (chain ID 200200, blockchain ID 2Y625Kvdd4wZ5LKx9kLSf1jGALyARmXVN1YsTb4F3Hzv4bPDbp)
###############################################################################
---
apiVersion: apps/v1
@@ -789,7 +789,7 @@ spec:
targetPort: 4000
###############################################################################
# Hanzo L2 EVM Indexer (chain ID 36963, blockchain ID 2GiQb73CeJESjc4omFv2YtQHZrRgJf25NXPzAr5J6UNHRcDV2F)
# Hanzo app chain EVM Indexer (chain ID 36963, blockchain ID 2GiQb73CeJESjc4omFv2YtQHZrRgJf25NXPzAr5J6UNHRcDV2F)
###############################################################################
---
apiVersion: apps/v1
@@ -859,7 +859,7 @@ spec:
targetPort: 4000
###############################################################################
# SPC L2 EVM Indexer (chain ID 36911, blockchain ID rtjwvtE1tEvrokmpeYdTq7b2zqZgmybKwR5MLjKMGAR1W78dQ)
# SPC app chain EVM Indexer (chain ID 36911, blockchain ID rtjwvtE1tEvrokmpeYdTq7b2zqZgmybKwR5MLjKMGAR1W78dQ)
###############################################################################
---
apiVersion: apps/v1
@@ -929,7 +929,7 @@ spec:
targetPort: 4000
###############################################################################
# Pars L2 EVM Indexer (chain ID 494949, blockchain ID 2pUskxqaL5Bpx7uRUGG1fDjPckjxQ4UKX4sLKeaS1NdSVBJd3F)
# Pars app chain EVM Indexer (chain ID 494949, blockchain ID 2pUskxqaL5Bpx7uRUGG1fDjPckjxQ4UKX4sLKeaS1NdSVBJd3F)
###############################################################################
---
apiVersion: apps/v1
@@ -1067,25 +1067,25 @@ spec:
services:
- name: lux-indexer-kchain
port: 4900
# Zoo L2 EVM
# Zoo app chain EVM
- kind: Rule
match: Host(`api-indexer-zoo.lux.network`)
services:
- name: lux-indexer-zoo
port: 4000
# Hanzo L2 EVM
# Hanzo app chain EVM
- kind: Rule
match: Host(`api-indexer-hanzo.lux.network`)
services:
- name: lux-indexer-hanzo
port: 4000
# SPC L2 EVM
# SPC app chain EVM
- kind: Rule
match: Host(`api-indexer-spc.lux.network`)
services:
- name: lux-indexer-spc
port: 4000
# Pars L2 EVM
# Pars app chain EVM
- kind: Rule
match: Host(`api-indexer-pars.lux.network`)
services:
+1 -1
View File
@@ -1,6 +1,6 @@
---
###############################################################################
# IngressRoutes: L2 EVM chain explorers (Mainnet)
# IngressRoutes: app chain EVM chain explorers (Mainnet)
# Each chain gets frontend + API backend routing rules.
# TLS via hanzoai/ingress ACME certResolver with Cloudflare DNS challenge.
#
+7 -7
View File
@@ -14,16 +14,16 @@
# /ext/bc/C/rpc, /ext/bc/C/ws — primary-network EVM
# /ext/bc/P, /ext/bc/X — P-Chain, X-Chain
# /ext/info, /ext/health — node info / liveness
# /ext/bc/hanzo/rpc — Hanzo L2 EVM (chainID 36963/36964)
# /ext/bc/zoo/rpc — Zoo L2 EVM
# /ext/bc/pars/rpc — Pars L2 EVM
# /ext/bc/spc/rpc — SPC L2 EVM
# /api/indexer/indexer-<sub> — indexer REST per L2 EVM
# /ext/bc/hanzo/rpc — Hanzo app chain EVM (chainID 36963/36964)
# /ext/bc/zoo/rpc — Zoo app chain EVM
# /ext/bc/pars/rpc — Pars app chain EVM
# /ext/bc/spc/rpc — SPC app chain EVM
# /api/indexer/indexer-<sub> — indexer REST per app chain EVM
#
# Notes:
# - Mainnet hanzo L2 EVM IS bootstrapped (blockchain id
# - Mainnet hanzo app chain EVM IS bootstrapped (blockchain id
# `y5HBrn6EWRMysSmH4nHA2sgpwgcjChJjQ7upSjWSwsrPeg4Cc`, chainID 0x9063).
# - Testnet/devnet hanzo L2 EVMs are NOT yet bootstrapped — gateway
# - Testnet/devnet hanzo app chain EVMs are NOT yet bootstrapped — gateway
# routes exist but luxd will 404 until CreateChainTx is submitted.
---
apiVersion: networking.k8s.io/v1
+2 -2
View File
@@ -2,7 +2,7 @@
###############################################################################
# Lux Operator CRD Resources — LuxIndexer + LuxGateway for all 3 networks
#
# Blockchain IDs verified 2026-03-01 from live P-chain (v5 L2 EVMs).
# Blockchain IDs verified 2026-03-01 from live P-chain (v5 app chain EVMs).
# See memory/blockchain-ids.md for full reference.
#
# Image: ghcr.io/luxfi/indexer:v1.0.1
@@ -170,7 +170,7 @@ spec:
# --- Mainnet Gateway ---
# autoRoutes: C/X/P/health/info + indexer APIs (7 routes)
# customRoutes: L2 EVM chain RPC by blockchain ID and alias
# customRoutes: app chain EVM chain RPC by blockchain ID and alias
---
apiVersion: lux.network/v1alpha1
kind: LuxGateway
+2 -3
View File
@@ -121,10 +121,9 @@ spec:
# from v1.25.1, this lets the proposer self-finalize its own
# block at proposal time when peer Chits don't arrive in time —
# closing the CreateChainTx stall observed on v1.27.23-26 with
# 5 validators under sybil protection. All 4 L2 EVMs (hanzo,
# 5 validators under sybil protection. All 4 app chains (hanzo,
# zoo, pars, spc) bootstrap cleanly. Also carries canonical
# evmAddr/utxoAddr (genesis v1.12.19), kill-subnet vocabulary,
# kill-fuji vocabulary.
# evmAddr/utxoAddr (genesis v1.12.19), kill-fuji vocabulary.
tag: "v1.27.27"
pullPolicy: IfNotPresent
ports:
+1 -1
View File
@@ -145,7 +145,7 @@ api-gateway-gateway (lux-mainnet, KrakenD :80)
luxd-headless.lux-mainnet.svc.cluster.local:9630
│ StatefulSet: 5 luxd validators on Lux Mainnet
▼
Subnet-EVM plugin (per chain) → on-disk badgerdb state
EVM plugin (per chain) → on-disk badgerdb state
```
The SPA never needs to know any of this — it just calls
+1 -1
View File
@@ -1,7 +1,7 @@
# Canonical chain aliases for lux-mainnet luxd validators.
# Loaded by luxd via --chain-aliases-file (see luxd-startup.yaml).
#
# Maps blockchainID → [alias names] for the 4 L2 EVMs:
# Maps blockchainID → [alias names] for the 4 app chain EVMs:
# hanzo (chainId 36963, coin AI)
# zoo (chainId 200200, coin ZOO, 799 historic blocks)
# pars (chainId 7070, coin PARS, 7B treasury) — actual chain name "parsv4"
+1 -1
View File
@@ -1,6 +1,6 @@
---
###############################################################################
# Frontend ConfigMaps for L2 EVM chain explorers (Mainnet)
# Frontend ConfigMaps for app chain EVM chain explorers (Mainnet)
# Each chain frontend needs API host, network identity, and theme config.
# NEXT_PUBLIC_API_HOST must be the externally-accessible API domain
# (client-side JS makes requests to it directly).
+3 -3
View File
@@ -3,18 +3,18 @@
# Unified Lux Explorer — Mainnet
#
# Single Go binary `ghcr.io/luxfi/explorer:1.2.11` that indexes every EVM
# L2 chain on this network into per-chain SQLite databases under /data/{slug}
# non-primary chain on this network into per-chain SQLite databases under /data/{slug}
# and serves the embedded SPA + /v1/indexer/{slug}/* + /v1/graph/* +
# /v1/explorer/admin/* + /v1/explorer/realtime on :8090.
#
# Replaces Blockscout (indexer-{slug}-indexer Deployments) + their
# per-L2 PostgreSQL StatefulSets (indexer-{slug}-pg) + the
# per-chain PostgreSQL StatefulSets (indexer-{slug}-pg) + the
# lux-explorer/explorer-mainnet-v2 Deployment. Postgres-free indexer.
#
# Per-chain RPC URLs use the chain name aliases exposed by luxd
# (/ext/bc/{slug}/rpc), verified working via eth_chainId against
# luxd-headless.lux-mainnet.svc:9630 on 2026-05-24. Chain IDs below are
# the on-chain values reported by each L2 EVM (NOT the historical
# the on-chain values reported by each app chain EVM (NOT the historical
# manifest-only values; pars reports 7070, not 494949).
###############################################################################
apiVersion: v1
+1 -1
View File
@@ -1,6 +1,6 @@
---
###############################################################################
# Lux Explore Frontend for L2 EVM chains (Mainnet)
# Lux Explore Frontend for app chain EVM chains (Mainnet)
# Image: ghcr.io/luxfi/explore:0.1.0 (Next.js 14, Node 22)
# Each chain gets a Deployment + Service, reads config from ConfigMap.
###############################################################################
+3 -3
View File
@@ -17,10 +17,10 @@ spec:
# in this network — pars and spc were bootstrapped with a different
# genesis whose private key is not recoverable from the project
# mnemonic (see ~/work/lux/standard/DEPLOYMENTS.md).
# (b) the hanzo/zoo L2 EVMs are currently in a stalled consensus state
# (b) the hanzo/zoo app chain EVMs are currently in a stalled consensus state
# ("no recent messages" on the router) so txs sit in mempool without
# being mined — heartbeating them would just produce log noise until
# the underlying L2 EVM validators are healthy.
# the underlying app chain EVM validators are healthy.
# Add hanzo,zoo,pars,spc back here once those are addressed.
schedule: "*/5 * * * *"
timeZone: Etc/UTC
@@ -48,7 +48,7 @@ spec:
imagePullPolicy: IfNotPresent
args:
# Public domain is used (not the in-cluster gateway DNS)
# because KrakenD's path-rewriting routes for L2 EVM aliases
# because KrakenD's path-rewriting routes for app chain EVM aliases
# only fire on certain Host headers; ingress handles it
# cleanly. Resolution stays inside DigitalOcean's network.
- --rpc-base=https://api.lux.network
+1 -1
View File
@@ -1,6 +1,6 @@
---
###############################################################################
# IngressRoutes: L2 EVM chain explorers (Mainnet)
# IngressRoutes: app chain EVM chain explorers (Mainnet)
# Each chain gets frontend + API backend routing rules.
# TLS via hanzoai/ingress ACME certResolver with Cloudflare DNS challenge.
#
+9 -9
View File
@@ -1,17 +1,17 @@
# luxd startup script for lux-mainnet.
#
# Canonical chain tracking + alias resolution (decomplected 2026-05-26):
# --track-chains restricts luxd to the 4 good L2 EVM blockchain IDs
# --track-chains restricts luxd to the 4 good app chain EVM blockchain IDs
# (orphan pars/spc/parsm chains are unreachable; their P-chain
# registrations remain but no validator tracks them).
# /data/aliases.json + --chain-aliases-file feeds info.* APIs with
# human-readable names for the 4 L2 EVMs.
# human-readable names for the 4 app chain EVMs.
# HTTP route renaming (e.g. /ext/bc/pars → parsv4 chain) is done at
# the api-gateway-gateway krakend layer, NOT at luxd (luxd routes
# only by chain-name).
#
# TENANT IMPORT COORDINATION (Zoo RLP block below):
# The Zoo L2 EVM (chainId 200200) RLP import block inlined below is
# The Zoo app chain EVM (chainId 200200) RLP import block inlined below is
# contributed by the Zoo brand. Source of truth:
# ~/work/zoo/universe/k8s/zoo-mainnet/luxd-rlp-import.yaml
#
@@ -66,7 +66,7 @@ data:
ADMIN_NEEDED="false"
fi
# DB wipe disabled - preserve subnet chain state across pod restarts
# DB wipe disabled - preserve chain state across pod restarts
# All one-time DB wipe blocks removed 2026-03-19 - sentinel files already applied
@@ -180,7 +180,7 @@ data:
# This override activates Etna→Cancun, Fortuna, Granite from genesis.
CHAIN_CONFIG_DIR="/data/configs/chains"
UPGRADE_JSON='{"networkUpgradeOverrides":{"durangoTimestamp":0,"etnaTimestamp":0,"fortunaTimestamp":0,"graniteTimestamp":0},"precompileUpgrades":[{"aiMiningConfig":{"blockTimestamp":0}},{"blake3Config":{"blockTimestamp":0}},{"cggmp21Verify":{"blockTimestamp":0}},{"deadZeroConfig":{"blockTimestamp":0}},{"deadConfig":{"blockTimestamp":0}},{"deadFullConfig":{"blockTimestamp":0}},{"dexConfig":{"blockTimestamp":0}},{"routerConfig":{"blockTimestamp":0}},{"eciesConfig":{"blockTimestamp":0}},{"fheConfig":{"blockTimestamp":0}},{"frostVerify":{"blockTimestamp":0}},{"graphConfig":{"blockTimestamp":0}},{"hpkeConfig":{"blockTimestamp":0}},{"mldsaVerify":{"blockTimestamp":0}},{"mlkemConfig":{"blockTimestamp":0}},{"pqcryptoConfig":{"blockTimestamp":0}},{"ringConfig":{"blockTimestamp":0}},{"coronaThreshold":{"blockTimestamp":0}},{"slhdsaVerify":{"blockTimestamp":0}},{"zkConfig":{"blockTimestamp":0}}]}'
# Canonical L2 EVM blockchain IDs (in sync with /data/aliases.json below).
# Canonical app chain EVM blockchain IDs (in sync with /data/aliases.json below).
# Update both here and in the chain-aliases ConfigMap when a chain is
# re-bootstrapped (e.g. parsv4, spcv4, zoofix on testnet).
EVM_CHAIN_IDS="C y5HBrn6EWRMysSmH4nHA2sgpwgcjChJjQ7upSjWSwsrPeg4Cc 2wbPWcyuNUimWW7YMq3gymTTpHjP9Lsm7GpmcyWimEs9kyTAAH 2wYWjBHziTjfMJmUPZwsDdWhm96cWHo4vov1HPBmCYLXdbAZfM 2YTuJW9b1k7gDGgqZC58Wj6cT1qTzobx6SV8hiN22zT1L5qfHF"
@@ -192,12 +192,12 @@ data:
ARGS="$ARGS --chain-config-dir=${CHAIN_CONFIG_DIR}"
echo "
# Ensure subnet-evm plugin uses our custom EVM build (with all precompiles)
# Ensure app chain EVM plugin uses our custom EVM build (with all precompiles)
CCHAIN_PLUGIN="mgj786NP7uDwBCcq6YwThhaN8FLyybkCa4zBWTQbNgmK6k9A6"
SUBNET_PLUGIN="ag3GReYPNuSR17rUP8acMdZipQBikdXNRKDyFszAysmy3vDXE"
APPCHAIN_PLUGIN="ag3GReYPNuSR17rUP8acMdZipQBikdXNRKDyFszAysmy3vDXE"
if [ -f "/data/plugins/$CCHAIN_PLUGIN" ]; then
echo "[PLUGINS] Copying C-Chain EVM as subnet-evm plugin - shared precompile registry"
cp -f "/data/plugins/$CCHAIN_PLUGIN" "/data/plugins/$SUBNET_PLUGIN"
echo "[PLUGINS] Copying C-Chain EVM as app chain plugin - shared precompile registry"
cp -f "/data/plugins/$CCHAIN_PLUGIN" "/data/plugins/$APPCHAIN_PLUGIN"
fi
[START] luxd ${ARGS}" | head -c 200
+1 -1
View File
@@ -22,7 +22,7 @@ metadata:
data:
chains: |
# Testnet "zoo" ghost chain — CreateChainTx committed with malformed
# EVM genesis alloc address (41 hex chars, odd-length), subnet-evm
# EVM genesis alloc address (41 hex chars, odd-length), EVM
# parseGenesis fails with "hex string of odd length" on every restart.
# CreateChainTx is immutable on P-Chain; the only remediation is to
# exclude it from bootstrap accounting. Replacement chain is alias
+4 -4
View File
@@ -3,18 +3,18 @@
# Unified Lux Explorer — Testnet
#
# Single Go binary `ghcr.io/luxfi/explorer:1.2.11` that indexes every EVM
# L2 chain on this network into per-chain SQLite databases under /data/{slug}
# non-primary chain on this network into per-chain SQLite databases under /data/{slug}
# and serves the embedded SPA + /v1/indexer/{slug}/* + /v1/graph/* +
# /v1/explorer/admin/* + /v1/explorer/realtime on :8090.
#
# Replaces Blockscout (indexer-{slug}-indexer Deployments) + their
# per-L2 PostgreSQL StatefulSets (indexer-{slug}-pg) + the
# per-chain PostgreSQL StatefulSets (indexer-{slug}-pg) + the
# lux-explorer/explorer-testnet-v2 Deployment. Postgres-free indexer.
#
# zoo L2 EVM is not bootstrapped on testnet luxd-* nodes (verified
# zoo app chain EVM is not bootstrapped on testnet luxd-* nodes (verified
# 2026-05-24: /ext/bc/zoo/rpc returns 404 across all 5 replicas). It is
# declared with enabled:false here so the registry knows about it; flip
# to true once the zoo L2 EVM is tracked on testnet validators.
# to true once the zoo app chain EVM is tracked on testnet validators.
###############################################################################
apiVersion: v1
kind: ConfigMap
+1 -1
View File
@@ -37,7 +37,7 @@ spec:
imagePullPolicy: IfNotPresent
args:
# Public domain is used (not the in-cluster gateway DNS)
# because KrakenD's path-rewriting routes for L2 EVM aliases
# because KrakenD's path-rewriting routes for app chain EVM aliases
# (zoo/hanzo/pars/spc) only fire on certain Host headers; the
# public FQDN goes through ingress where this works cleanly.
# Resolution stays inside DigitalOcean's network.
+1 -1
View File
@@ -1,6 +1,6 @@
---
###############################################################################
# IngressRoutes: L2 EVM chain explorers (Testnet)
# IngressRoutes: app chain EVM chain explorers (Testnet)
#
# Domain convention mirrors mainnet:
# explore-{chain}-test.lux.network -> unified explorer SPA (explorer:80)
File diff suppressed because one or more lines are too long
+2 -2
View File
@@ -21,11 +21,11 @@
"test:bridge-mpc": "npx playwright test e2e/13-bridge-mpc.spec.ts",
"test:dex-contracts": "npx playwright test e2e/14-dex-contracts.spec.ts",
"test:nft": "npx playwright test e2e/15-nft-marketplace.spec.ts",
"test:subnets": "npx playwright test e2e/16-subnet-chains.spec.ts",
"test:chains": "npx playwright test e2e/16-app-chains.spec.ts",
"test:governance": "npx playwright test e2e/17-governance.spec.ts",
"test:broker": "npx playwright test e2e/18-broker.spec.ts",
"test:broker-deep": "npx playwright test e2e/19-broker-deep.spec.ts",
"test:full": "npx playwright test e2e/00-health.spec.ts e2e/01-explorer.spec.ts e2e/02-exchange.spec.ts e2e/03-bridge.spec.ts e2e/04-staking.spec.ts e2e/05-cloud.spec.ts e2e/06-wallet.spec.ts e2e/07-validator.spec.ts e2e/08-validator-lifecycle.spec.ts e2e/09-defi-flow.spec.ts e2e/10-stats-dashboard.spec.ts e2e/11-wallet-ops.spec.ts e2e/12-staking-rpc.spec.ts e2e/13-bridge-mpc.spec.ts e2e/14-dex-contracts.spec.ts e2e/15-nft-marketplace.spec.ts e2e/16-subnet-chains.spec.ts e2e/17-governance.spec.ts e2e/18-broker.spec.ts e2e/19-broker-deep.spec.ts",
"test:full": "npx playwright test e2e/00-health.spec.ts e2e/01-explorer.spec.ts e2e/02-exchange.spec.ts e2e/03-bridge.spec.ts e2e/04-staking.spec.ts e2e/05-cloud.spec.ts e2e/06-wallet.spec.ts e2e/07-validator.spec.ts e2e/08-validator-lifecycle.spec.ts e2e/09-defi-flow.spec.ts e2e/10-stats-dashboard.spec.ts e2e/11-wallet-ops.spec.ts e2e/12-staking-rpc.spec.ts e2e/13-bridge-mpc.spec.ts e2e/14-dex-contracts.spec.ts e2e/15-nft-marketplace.spec.ts e2e/16-app-chains.spec.ts e2e/17-governance.spec.ts e2e/18-broker.spec.ts e2e/19-broker-deep.spec.ts",
"test:headed": "npx playwright test --headed",
"test:ui": "npx playwright test --ui",
"test:prod": "ENV=production npx playwright test",
+10 -10
View File
@@ -67,7 +67,7 @@ CHAINS = {
"factory": "0x84CF0A13db1be8e1F0676405cfcBC8b09692FD1C",
"router": "0x2382F7A49FA48E1F91Bec466c32e1D7F13ec8206",
},
# Subnet chains: all use shared addresses (deployer had nonce=0 on each fresh chain)
# App chains: all use shared addresses (deployer had nonce=0 on each fresh chain)
"Zoo": {
"chain_id": 200200,
"rpc": "http://localhost:19630/ext/bc/2Y625Kvdd4wZ5LKx9kLSf1jGALyARmXVN1YsTb4F3Hzv4bPDbp/rpc",
@@ -118,14 +118,14 @@ CHAINS = {
}
# Minimum native LUX required to participate in exchange setup
MIN_NATIVE_LUX = 10 # LUX (subnets have huge balances; C-Chain deployer may be low)
MIN_NATIVE_LUX = 10 # LUX (app chains have huge balances; C-Chain deployer may be low)
# KNOWN LIMITATION: AMM swaps fail on Subnet EVM (Zoo, Hanzo, Pars, SPC) with
# KNOWN LIMITATION: AMM swaps fail on app chain EVMs (Zoo, Hanzo, Pars, SPC) with
# "invalid opcode: MCOPY" because the AMM router was compiled with Solidity >=0.8.24
# which uses the Cancun EIP-5656 MCOPY opcode. Subnet EVM does not yet support Cancun.
# which uses the Cancun EIP-5656 MCOPY opcode. App chain EVMs do not yet support Cancun.
# Workaround: Recompile AMM contracts with Solidity <=0.8.23 and redeploy.
# C-Chain swaps work since C-Chain EVM supports Cancun.
SUBNET_SWAP_BROKEN = True # swaps on Hanzo/Pars chains will revert
APPCHAIN_SWAP_BROKEN = True # swaps on Hanzo/Pars chains will revert
# ---------------------------------------------------------------------------
# ABIs (minimal)
@@ -496,13 +496,13 @@ def trade_on_chain(name: str, cfg: dict, account, swap_amount_lux: float = 10.0)
"""
Execute one round of swaps: WLUX→LETH then LETH→WLUX.
Returns True if any swap succeeded.
NOTE: Subnet EVM chains (Hanzo, Pars, Zoo, SPC) fail with MCOPY opcode.
NOTE: App chain EVMs (Hanzo, Pars, Zoo, SPC) fail with MCOPY opcode.
Only C-Chain supports Cancun opcodes needed by the AMM router.
"""
# Subnet chains use AMM contracts compiled with Solidity >=0.8.24 (MCOPY opcode).
# Subnet EVM doesn't support Cancun; skip swaps until contracts are recompiled.
if SUBNET_SWAP_BROKEN and name != "C-Chain":
print(f" [{name}] Skipping swap (MCOPY not supported on Subnet EVM — recompile AMM with Solidity <=0.8.23)")
# App chains use AMM contracts compiled with Solidity >=0.8.24 (MCOPY opcode).
# App chain EVMs don't support Cancun; skip swaps until contracts are recompiled.
if APPCHAIN_SWAP_BROKEN and name != "C-Chain":
print(f" [{name}] Skipping swap (MCOPY not supported on app chain EVM — recompile AMM with Solidity <=0.8.23)")
return False
w3 = Web3(Web3.HTTPProvider(cfg["rpc"], request_kwargs={"timeout": 30}))
+4 -4
View File
@@ -279,7 +279,7 @@ def test_validator_network():
def validator_count():
r = http_post("https://api.lux.network/ext/bc/P",
{"jsonrpc": "2.0", "method": "platform.getCurrentValidators", "params": {"subnetID": ""}, "id": 1})
{"jsonrpc": "2.0", "method": "platform.getCurrentValidators", "params": {"netID": ""}, "id": 1})
validators = r.get("result", {}).get("validators", [])
count = len(validators)
assert count >= 5, f"expected >= 5 validators, got {count}"
@@ -308,7 +308,7 @@ def test_validator_network():
def sybil_protection():
"""Verify sybil protection is enabled (not POA)"""
r = http_post("https://api.lux.network/ext/bc/P",
{"jsonrpc": "2.0", "method": "platform.getCurrentValidators", "params": {"subnetID": ""}, "id": 1})
{"jsonrpc": "2.0", "method": "platform.getCurrentValidators", "params": {"netID": ""}, "id": 1})
validators = r.get("result", {}).get("validators", [])
# Real validators have start/end times and delegation fees
has_staking = all(v.get("startTime") and v.get("endTime") for v in validators)
@@ -328,7 +328,7 @@ def test_validator_network():
assert bootstrapped, f"{chain}-Chain not bootstrapped"
return " ".join(results)
def subnet_chains():
def app_chains():
r = http_post("https://api.lux.network/ext/bc/P",
{"jsonrpc": "2.0", "method": "platform.getBlockchains", "params": {}, "id": 1})
chains = r.get("result", {}).get("blockchains", [])
@@ -346,7 +346,7 @@ def test_validator_network():
check("Node version", node_version)
check("Sybil protection (not POA)", sybil_protection)
check("All chains bootstrapped", all_chains_bootstrapped)
check("Subnet blockchains", subnet_chains)
check("App chains", app_chains)
check("Consensus parameters", consensus_params)
+28 -28
View File
@@ -26,7 +26,7 @@ type ChainConfig struct {
AMMV2Router common.Address
AMMV2Factory common.Address
// Whether AMM swaps work on this chain (Subnet EVM may lack MCOPY)
// Whether AMM swaps work on this chain (app chain EVM may lack MCOPY)
SwapsEnabled bool
}
@@ -75,8 +75,8 @@ func envFloatOrDefault(key string, def float64) float64 {
return def
}
// Subnet shared addresses (deployer had nonce=0 on each fresh chain)
var subnetTokens = struct {
// App-chain shared addresses (deployer had nonce=0 on each fresh chain)
var appChainTokens = struct {
WLUX, LETH, LBTC, LUSDC common.Address
AMMV2Router, AMMV2Factory common.Address
}{
@@ -90,7 +90,7 @@ var subnetTokens = struct {
// allChains returns all supported chain configurations.
// C-chain addresses are from the user-provided contract addresses.
// Subnet chains use the shared deployed addresses from e2e-exchange.py.
// App chains use the shared deployed addresses from e2e-exchange.py.
func allChains() map[string]ChainConfig {
return map[string]ChainConfig{
"lux": {
@@ -109,48 +109,48 @@ func allChains() map[string]ChainConfig {
Name: "Zoo Chain",
ChainID: big.NewInt(200200),
RPC: envOrDefault("RPC_ZOO", "https://api.lux.network/mainnet/ext/bc/Z/rpc"),
WLUX: subnetTokens.WLUX,
LETH: subnetTokens.LETH,
LBTC: subnetTokens.LBTC,
LUSDC: subnetTokens.LUSDC,
AMMV2Factory: subnetTokens.AMMV2Factory,
AMMV2Router: subnetTokens.AMMV2Router,
WLUX: appChainTokens.WLUX,
LETH: appChainTokens.LETH,
LBTC: appChainTokens.LBTC,
LUSDC: appChainTokens.LUSDC,
AMMV2Factory: appChainTokens.AMMV2Factory,
AMMV2Router: appChainTokens.AMMV2Router,
SwapsEnabled: false, // MCOPY opcode not supported
},
"hanzo": {
Name: "Hanzo Chain",
ChainID: big.NewInt(36963),
RPC: envOrDefault("RPC_HANZO", "https://api.lux.network/mainnet/ext/bc/H/rpc"),
WLUX: subnetTokens.WLUX,
LETH: subnetTokens.LETH,
LBTC: subnetTokens.LBTC,
LUSDC: subnetTokens.LUSDC,
AMMV2Factory: subnetTokens.AMMV2Factory,
AMMV2Router: subnetTokens.AMMV2Router,
WLUX: appChainTokens.WLUX,
LETH: appChainTokens.LETH,
LBTC: appChainTokens.LBTC,
LUSDC: appChainTokens.LUSDC,
AMMV2Factory: appChainTokens.AMMV2Factory,
AMMV2Router: appChainTokens.AMMV2Router,
SwapsEnabled: false,
},
"spc": {
Name: "SPC Chain",
ChainID: big.NewInt(36911),
RPC: envOrDefault("RPC_SPC", "https://api.lux.network/mainnet/ext/bc/S/rpc"),
WLUX: subnetTokens.WLUX,
LETH: subnetTokens.LETH,
LBTC: subnetTokens.LBTC,
LUSDC: subnetTokens.LUSDC,
AMMV2Factory: subnetTokens.AMMV2Factory,
AMMV2Router: subnetTokens.AMMV2Router,
WLUX: appChainTokens.WLUX,
LETH: appChainTokens.LETH,
LBTC: appChainTokens.LBTC,
LUSDC: appChainTokens.LUSDC,
AMMV2Factory: appChainTokens.AMMV2Factory,
AMMV2Router: appChainTokens.AMMV2Router,
SwapsEnabled: false,
},
"pars": {
Name: "Pars Chain",
ChainID: big.NewInt(494949),
RPC: envOrDefault("RPC_PARS", "https://api.lux.network/mainnet/ext/bc/T/rpc"),
WLUX: subnetTokens.WLUX,
LETH: subnetTokens.LETH,
LBTC: subnetTokens.LBTC,
LUSDC: subnetTokens.LUSDC,
AMMV2Factory: subnetTokens.AMMV2Factory,
AMMV2Router: subnetTokens.AMMV2Router,
WLUX: appChainTokens.WLUX,
LETH: appChainTokens.LETH,
LBTC: appChainTokens.LBTC,
LUSDC: appChainTokens.LUSDC,
AMMV2Factory: appChainTokens.AMMV2Factory,
AMMV2Router: appChainTokens.AMMV2Router,
SwapsEnabled: false,
},
}