#!/usr/bin/env node /** * Validates config/deployment-status.json for "minimum viable deployed graph". * Use in CI so deployment-realistic sim cannot run with half-filled state. * * Rules: * - If bridgeAvailable === true on a chain, cwTokens must include at least cWUSDT and cWUSDC (phase 1). * - For each pmmPool: role in {defense, public_routing}, feeBps and k present, * base/quote (or tokenIn/tokenOut) exist in cwTokens or anchorAddresses. * * Exit code: 0 if valid, 1 if invalid (and prints errors to stderr). */ const fs = require('fs'); const path = require('path'); const CONFIG_DIR = path.join(__dirname, '..', 'config'); const DEPLOYMENT_STATUS_PATH = path.join(CONFIG_DIR, 'deployment-status.json'); const PHASE1_CW = ['cWUSDT', 'cWUSDC']; const VALID_ROLES = ['defense', 'public_routing']; function loadJson(p) { return JSON.parse(fs.readFileSync(p, 'utf8')); } function main() { const status = loadJson(DEPLOYMENT_STATUS_PATH); const chains = status.chains || {}; const errors = []; for (const [chainId, chain] of Object.entries(chains)) { const cwTokens = chain.cwTokens || {}; const anchorAddresses = chain.anchorAddresses || {}; const pmmPools = chain.pmmPools || []; const bridgeAvailable = chain.bridgeAvailable; if (bridgeAvailable === true) { for (const sym of PHASE1_CW) { if (!cwTokens[sym] || typeof cwTokens[sym] !== 'string' || !cwTokens[sym].trim()) { errors.push(`Chain ${chainId} (${chain.name}): bridgeAvailable=true but cwTokens.${sym} missing or empty`); } } } const knownTokens = new Set([...Object.keys(cwTokens), ...Object.keys(anchorAddresses)]); for (let i = 0; i < pmmPools.length; i++) { const pool = pmmPools[i]; const base = pool.base ?? pool.tokenIn; const quote = pool.quote ?? pool.tokenOut; if (!VALID_ROLES.includes(pool.role)) { errors.push(`Chain ${chainId} pmmPools[${i}]: role must be one of ${VALID_ROLES.join(', ')}`); } if (pool.feeBps == null || pool.k == null) { errors.push(`Chain ${chainId} pmmPools[${i}]: feeBps and k required`); } if (base && !knownTokens.has(base)) { errors.push(`Chain ${chainId} pmmPools[${i}]: base/tokenIn "${base}" not in cwTokens or anchorAddresses`); } if (quote && !knownTokens.has(quote)) { errors.push(`Chain ${chainId} pmmPools[${i}]: quote/tokenOut "${quote}" not in cwTokens or anchorAddresses`); } } } if (errors.length > 0) { errors.forEach((e) => process.stderr.write(e + '\n')); process.exit(1); } process.exit(0); } main();