#!/usr/bin/env bash # Compare the GRU global-priority rollout plan against the current repo state. # Usage: # bash scripts/verify/check-gru-global-priority-rollout.sh # bash scripts/verify/check-gru-global-priority-rollout.sh --json # bash scripts/verify/check-gru-global-priority-rollout.sh --wave=wave1 set -euo pipefail SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" PROJECT_ROOT="$(cd "$SCRIPT_DIR/../.." && pwd)" export PROJECT_ROOT OUTPUT_JSON=0 WAVE_FILTER="" for arg in "$@"; do case "$arg" in --json) OUTPUT_JSON=1 ;; --wave=*) WAVE_FILTER="${arg#--wave=}" ;; *) echo "Unknown argument: $arg" >&2 exit 2 ;; esac done need_cmd() { command -v "$1" >/dev/null 2>&1 || { echo "[FAIL] Missing required command: $1" >&2 exit 1 } } need_cmd node OUTPUT_JSON="$OUTPUT_JSON" WAVE_FILTER="$WAVE_FILTER" node <<'NODE' const fs = require('fs'); const path = require('path'); const root = process.env.PROJECT_ROOT; const outputJson = process.env.OUTPUT_JSON === '1'; const waveFilter = process.env.WAVE_FILTER || ''; function readJson(relPath) { return JSON.parse(fs.readFileSync(path.join(root, relPath), 'utf8')); } const rollout = readJson('config/gru-global-priority-currency-rollout.json'); const manifest = readJson('config/gru-iso4217-currency-manifest.json'); const mapping = readJson('config/token-mapping-multichain.json'); const transport = readJson('config/gru-transport-active.json'); const manifestByCode = new Map((manifest.currencies || []).map((item) => [item.code, item])); const transportBySymbol = new Map((transport.enabledCanonicalTokens || []).map((item) => [item.symbol, item])); const cToCw = mapping.cToCwSymbolMapping || {}; const wavesById = new Map((rollout.waves || []).map((item) => [item.id, item])); const desiredDestinationNetworks = rollout.desiredDestinationNetworks || {}; const destinationSummary = { evmPublicCwMesh: (desiredDestinationNetworks.evmPublicCwMeshChainIds || []).length, altEvmPrograms: (desiredDestinationNetworks.altEvmPrograms || []).length, nonEvmRelayPrograms: (desiredDestinationNetworks.nonEvmRelayPrograms || []).length }; function manifestSymbolsFor(entry) { if (!entry || !entry.canonicalAssets) return []; const out = []; for (const [form, data] of Object.entries(entry.canonicalAssets)) { if (data && typeof data.symbol === 'string') { out.push({ form, symbol: data.symbol }); } } return out; } function deriveState(bits) { if (bits.transportActive) return 'live_transport'; if (bits.canonical138Deployed) return 'canonical_only'; if (bits.manifestPresent) return 'manifest_only'; if (bits.cToCwMapped) return 'mapping_only'; return 'backlog'; } function nextStep(bits) { if (!bits.manifestPresent) return 'add_to_manifest_and_standards'; if (!bits.canonical138Deployed) return 'deploy_canonical_on_chain138'; if (!bits.cToCwMapped) return 'add_c_to_cw_symbol_mapping'; if (!bits.transportActive) return 'deploy_cw_and_enable_transport'; if (!bits.x402Ready) return 'promote_x402_surface'; return 'monitor_and_scale'; } const filteredAssets = (rollout.assets || []).filter((asset) => !waveFilter || asset.wave === waveFilter); const results = filteredAssets.map((asset) => { const manifestEntry = manifestByCode.get(asset.code); const rolloutSymbols = (asset.tokenForms || []).map((item) => ({ form: item.form, canonicalSymbol: item.canonicalSymbol, wrappedSymbol: item.wrappedSymbol })); const manifestSymbols = manifestSymbolsFor(manifestEntry); const manifestPresent = Boolean(manifestEntry); const canonical138Deployed = Boolean(manifestEntry?.status?.deployed); const transportActive = rolloutSymbols.some((item) => { const transportEntry = transportBySymbol.get(item.canonicalSymbol); return Boolean(transportEntry); }); const x402Ready = Boolean(manifestEntry?.status?.x402Ready); const cToCwMapped = rolloutSymbols.length > 0 && rolloutSymbols.every((item) => cToCw[item.canonicalSymbol] === item.wrappedSymbol); const manifestMatchesRollout = manifestSymbols.length > 0 && rolloutSymbols.every((item) => manifestSymbols.some((m) => m.symbol === item.canonicalSymbol)); const bits = { manifestPresent, canonical138Deployed, cToCwMapped, transportActive, x402Ready }; return { code: asset.code, name: asset.name, tier: asset.tier, rank: asset.rank, wave: asset.wave, waveName: wavesById.get(asset.wave)?.name || asset.wave, repoTargetState: asset.repoTargetState, currentRepoState: deriveState(bits), nextStep: nextStep(bits), manifestPresent, canonical138Deployed, cToCwMapped, transportActive, x402Ready, manifestMatchesRollout, canonicalSymbols: rolloutSymbols.map((item) => item.canonicalSymbol), wrappedSymbols: rolloutSymbols.map((item) => item.wrappedSymbol) }; }); const summary = { totalAssets: results.length, liveTransport: results.filter((item) => item.currentRepoState === 'live_transport').length, canonicalOnly: results.filter((item) => item.currentRepoState === 'canonical_only').length, manifestOnly: results.filter((item) => item.currentRepoState === 'manifest_only').length, backlog: results.filter((item) => item.currentRepoState === 'backlog').length, mappingOnly: results.filter((item) => item.currentRepoState === 'mapping_only').length }; if (outputJson) { console.log(JSON.stringify({ summary, destinationSummary, desiredDestinationNetworks, results }, null, 2)); process.exit(0); } console.log('=== GRU Global Priority Cross-Chain Rollout ==='); console.log(`Assets checked: ${summary.totalAssets}`); console.log(`Desired destination networks: EVM cW mesh=${destinationSummary.evmPublicCwMesh}, ALT/gated EVM=${destinationSummary.altEvmPrograms}, non-EVM relay=${destinationSummary.nonEvmRelayPrograms}`); console.log(`live_transport: ${summary.liveTransport}`); console.log(`canonical_only: ${summary.canonicalOnly}`); console.log(`manifest_only: ${summary.manifestOnly}`); console.log(`mapping_only: ${summary.mappingOnly}`); console.log(`backlog: ${summary.backlog}`); if (waveFilter) { console.log(`Wave filter: ${waveFilter}`); } if ((desiredDestinationNetworks.nonEvmRelayPrograms || []).length > 0) { const nonEvmNames = desiredDestinationNetworks.nonEvmRelayPrograms .map((item) => item.identifier) .join(', '); console.log(`Non-EVM desired targets: ${nonEvmNames}`); } for (const item of results) { const flags = [ item.manifestPresent ? 'manifest' : 'no-manifest', item.canonical138Deployed ? 'canonical138' : 'no-canonical138', item.cToCwMapped ? 'mapped' : 'no-mapping', item.transportActive ? 'transport' : 'no-transport', item.x402Ready ? 'x402' : 'no-x402' ].join(', '); console.log(`- ${item.code} (${item.name}) [${item.tier} / ${item.wave}] -> ${item.currentRepoState}; next: ${item.nextStep}; ${flags}`); } NODE