- Update dbis_core, cross-chain-pmm-lps, explorer-monorepo, metamask-integration, pr-workspace/chains - Omit embedded publish git dirs and empty placeholders from index Made-with: Cursor
322 lines
14 KiB
Bash
322 lines
14 KiB
Bash
#!/usr/bin/env bash
|
|
# Summarize the GRU v2 public-network rollout posture across the public EVM cW*
|
|
# mesh, Wave 1 transport activation, and public protocol liquidity.
|
|
#
|
|
# Usage:
|
|
# bash scripts/verify/check-gru-v2-public-protocols.sh
|
|
# bash scripts/verify/check-gru-v2-public-protocols.sh --json
|
|
# bash scripts/verify/check-gru-v2-public-protocols.sh --write-explorer-config
|
|
|
|
set -euo pipefail
|
|
|
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
PROJECT_ROOT="$(cd "$SCRIPT_DIR/../.." && pwd)"
|
|
export PROJECT_ROOT
|
|
|
|
OUTPUT_JSON=0
|
|
WRITE_EXPLORER_CONFIG=0
|
|
|
|
for arg in "$@"; do
|
|
case "$arg" in
|
|
--json) OUTPUT_JSON=1 ;;
|
|
--write-explorer-config) WRITE_EXPLORER_CONFIG=1 ;;
|
|
*)
|
|
echo "Unknown argument: $arg" >&2
|
|
exit 2
|
|
;;
|
|
esac
|
|
done
|
|
|
|
command -v node >/dev/null 2>&1 || {
|
|
echo "[FAIL] Missing required command: node" >&2
|
|
exit 1
|
|
}
|
|
|
|
OUTPUT_JSON="$OUTPUT_JSON" WRITE_EXPLORER_CONFIG="$WRITE_EXPLORER_CONFIG" node <<'NODE'
|
|
const fs = require('fs');
|
|
const path = require('path');
|
|
|
|
const root = process.env.PROJECT_ROOT;
|
|
const outputJson = process.env.OUTPUT_JSON === '1';
|
|
const writeExplorerConfig = process.env.WRITE_EXPLORER_CONFIG === '1';
|
|
const explorerConfigPath = path.join(
|
|
root,
|
|
'explorer-monorepo/backend/api/rest/config/metamask/GRU_V2_PUBLIC_DEPLOYMENT_STATUS.json'
|
|
);
|
|
|
|
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 transport = readJson('config/gru-transport-active.json');
|
|
const deployment = readJson('cross-chain-pmm-lps/config/deployment-status.json');
|
|
const mapping = readJson('config/token-mapping-multichain.json');
|
|
const poolMatrix = readJson('cross-chain-pmm-lps/config/pool-matrix.json');
|
|
const routingRegistry = readJson('config/routing-registry.json');
|
|
|
|
const desiredChainIds = rollout.desiredDestinationNetworks?.evmPublicCwMeshChainIds || [];
|
|
const chainNames = mapping.chainNames || {};
|
|
const manifestByCode = new Map((manifest.currencies || []).map((item) => [item.code, item]));
|
|
const transportBySymbol = new Map((transport.enabledCanonicalTokens || []).map((item) => [item.symbol, item]));
|
|
|
|
const coreCwSymbols = [
|
|
'cWUSDT',
|
|
'cWUSDC',
|
|
'cWEURC',
|
|
'cWEURT',
|
|
'cWGBPC',
|
|
'cWGBPT',
|
|
'cWAUDC',
|
|
'cWJPYC',
|
|
'cWCHFC',
|
|
'cWCADC',
|
|
'cWXAUC',
|
|
'cWXAUT'
|
|
];
|
|
|
|
const publicProtocols = [
|
|
{ key: 'uniswap_v3', name: 'Uniswap v3' },
|
|
{ key: 'balancer', name: 'Balancer' },
|
|
{ key: 'curve_3', name: 'Curve 3' },
|
|
{ key: 'dodo_pmm', name: 'DODO PMM' },
|
|
{ key: 'one_inch', name: '1inch' }
|
|
];
|
|
|
|
const desiredChainRows = desiredChainIds.map((chainId) => {
|
|
const chain = deployment.chains?.[String(chainId)] || {};
|
|
const cwTokens = Object.keys(chain.cwTokens || {});
|
|
const pmmPools = Array.isArray(chain.pmmPools) ? chain.pmmPools : [];
|
|
return {
|
|
chainId,
|
|
name: chain.name || chainNames[String(chainId)] || `Chain ${chainId}`,
|
|
cwTokenCount: cwTokens.length,
|
|
hasFullCoreSuite: coreCwSymbols.every((symbol) => cwTokens.includes(symbol)),
|
|
bridgeAvailable: chain.bridgeAvailable === true,
|
|
pmmPoolCount: pmmPools.length
|
|
};
|
|
});
|
|
|
|
const desiredButNotLoaded = desiredChainRows.filter((row) => row.cwTokenCount === 0);
|
|
const loadedChains = desiredChainRows.filter((row) => row.cwTokenCount > 0);
|
|
const fullCoreSuiteChains = desiredChainRows.filter((row) => row.hasFullCoreSuite);
|
|
const chainsWithAnyPools = desiredChainRows.filter((row) => row.pmmPoolCount > 0);
|
|
const totalRecordedPublicPools = desiredChainRows.reduce((sum, row) => sum + row.pmmPoolCount, 0);
|
|
const arbitrumRoute = (routingRegistry.routes || []).find((route) => route.fromChain === 138 && route.toChain === 42161 && route.asset === 'WETH');
|
|
const arbitrumHubBlocker = {
|
|
active: true,
|
|
fromChain: 138,
|
|
viaChain: 1,
|
|
toChain: 42161,
|
|
currentPath: '138 -> Mainnet -> Arbitrum',
|
|
sourceBridge: '0xc9901ce2Ddb6490FAA183645147a87496d8b20B6',
|
|
failedTxHash: '0x97df657f0e31341ca852666766e553650531bbcc86621246d041985d7261bb07',
|
|
note: (arbitrumRoute && arbitrumRoute.note) || 'Use Mainnet hub; the current Mainnet -> Arbitrum WETH9 leg is blocked.'
|
|
};
|
|
|
|
function currencyState(code) {
|
|
const item = manifestByCode.get(code);
|
|
return {
|
|
manifestPresent: Boolean(item),
|
|
deployed: Boolean(item?.status?.deployed),
|
|
transportActive: Boolean(item?.status?.transportActive),
|
|
x402Ready: Boolean(item?.status?.x402Ready)
|
|
};
|
|
}
|
|
|
|
const allAssetResults = (rollout.assets || []).map((asset) => {
|
|
const state = currencyState(asset.code);
|
|
const transportSymbols = (asset.tokenForms || []).map((item) => item.canonicalSymbol);
|
|
const enabledByOverlay = transportSymbols.some((symbol) => transportBySymbol.has(symbol));
|
|
return {
|
|
code: asset.code,
|
|
name: asset.name,
|
|
wave: asset.wave,
|
|
manifestPresent: state.manifestPresent,
|
|
deployed: state.deployed,
|
|
transportActive: state.transportActive && enabledByOverlay,
|
|
x402Ready: state.x402Ready,
|
|
canonicalSymbols: (asset.tokenForms || []).map((item) => item.canonicalSymbol),
|
|
wrappedSymbols: (asset.tokenForms || []).map((item) => item.wrappedSymbol)
|
|
};
|
|
});
|
|
|
|
const wave1Results = allAssetResults
|
|
.filter((asset) => asset.wave === 'wave1')
|
|
.map((asset) => ({
|
|
...asset,
|
|
currentState: asset.transportActive
|
|
? 'live_transport'
|
|
: asset.deployed
|
|
? 'canonical_only'
|
|
: asset.manifestPresent
|
|
? 'manifest_only'
|
|
: 'backlog',
|
|
nextStep: asset.transportActive
|
|
? 'monitor_and_scale'
|
|
: asset.deployed
|
|
? 'activate_transport_and_attach_public_liquidity'
|
|
: asset.manifestPresent
|
|
? 'finish_canonical_deployment'
|
|
: 'add_to_manifest'
|
|
}));
|
|
|
|
const wave1WrappedSymbols = [...new Set(wave1Results.flatMap((asset) => asset.wrappedSymbols))];
|
|
const poolMatrixCwTokens = new Set(poolMatrix.cwTokens || []);
|
|
const wave1WrappedSymbolsMissingFromPoolMatrix = wave1WrappedSymbols.filter((symbol) => !poolMatrixCwTokens.has(symbol));
|
|
|
|
const rolloutSummary = {
|
|
liveTransportAssets: allAssetResults.filter((asset) => asset.transportActive).length,
|
|
canonicalOnlyAssets: allAssetResults.filter((asset) => asset.deployed && !asset.transportActive).length,
|
|
backlogAssets: allAssetResults.filter((asset) => !asset.manifestPresent).length,
|
|
wave1LiveTransport: wave1Results.filter((asset) => asset.currentState === 'live_transport').length,
|
|
wave1CanonicalOnly: wave1Results.filter((asset) => asset.currentState === 'canonical_only').length,
|
|
wave1WrappedSymbols: wave1WrappedSymbols.length,
|
|
wave1WrappedSymbolsCoveredByPoolMatrix: wave1WrappedSymbols.length - wave1WrappedSymbolsMissingFromPoolMatrix.length
|
|
};
|
|
|
|
const protocolResults = publicProtocols.map((protocol) => {
|
|
if (protocol.key === 'dodo_pmm') {
|
|
return {
|
|
key: protocol.key,
|
|
name: protocol.name,
|
|
activePublicCwPools: totalRecordedPublicPools,
|
|
destinationChainsWithPools: chainsWithAnyPools.length,
|
|
status: totalRecordedPublicPools > 0 ? 'partial_live_on_public_cw_mesh' : 'not_deployed_on_public_cw_mesh',
|
|
notes: totalRecordedPublicPools > 0
|
|
? 'deployment-status.json now records live public-chain cW* DODO PMM pools on Mainnet, including recorded non-USD Wave 1 rows, and the recorded Mainnet pools now have bidirectional live execution proof. The broader public cW mesh is still partial.'
|
|
: 'cross-chain-pmm-lps/config/deployment-status.json still records no public-chain cW* pools, so no live DODO PMM cW venue can be asserted.'
|
|
};
|
|
}
|
|
|
|
return {
|
|
key: protocol.key,
|
|
name: protocol.name,
|
|
activePublicCwPools: 0,
|
|
destinationChainsWithPools: 0,
|
|
status: 'not_deployed_on_public_cw_mesh',
|
|
notes: 'No live public-chain cW* venue is recorded for this protocol in deployment-status.json yet.'
|
|
};
|
|
});
|
|
|
|
const blockers = [];
|
|
if (desiredButNotLoaded.length > 0) {
|
|
blockers.push(`Desired public EVM targets still lack cW token suites: ${desiredButNotLoaded.map((row) => row.name).join(', ')}.`);
|
|
}
|
|
if (rolloutSummary.wave1CanonicalOnly > 0) {
|
|
blockers.push(`Wave 1 GRU assets are still canonical-only on Chain 138: ${wave1Results.filter((asset) => asset.currentState === 'canonical_only').map((asset) => asset.code).join(', ')}.`);
|
|
}
|
|
if (wave1WrappedSymbolsMissingFromPoolMatrix.length > 0) {
|
|
blockers.push(`Wave 1 wrapped symbols are still missing from the public pool matrix: ${wave1WrappedSymbolsMissingFromPoolMatrix.join(', ')}.`);
|
|
}
|
|
if (chainsWithAnyPools.length === 0) {
|
|
blockers.push('Public cW* liquidity is still undeployed across Uniswap v3, Balancer, Curve 3, DODO PMM, and 1inch on the tracked public-network mesh.');
|
|
}
|
|
if (chainsWithAnyPools.length > 0 && protocolResults.some((item) => item.activePublicCwPools > 0) && protocolResults.some((item) => item.activePublicCwPools === 0)) {
|
|
blockers.push('Public cW* protocol rollout is now partial: DODO PMM has recorded pools, while Uniswap v3, Balancer, Curve 3, and 1inch remain not live on the public cW mesh.');
|
|
}
|
|
if (rolloutSummary.backlogAssets > 0) {
|
|
blockers.push(`The ranked GRU global rollout still has ${rolloutSummary.backlogAssets} backlog assets outside the live manifest.`);
|
|
}
|
|
if ((rollout.desiredDestinationNetworks?.nonEvmRelayPrograms || []).length > 0) {
|
|
blockers.push(`Desired non-EVM GRU targets remain planned / relay-dependent: ${(rollout.desiredDestinationNetworks.nonEvmRelayPrograms || []).map((item) => item.identifier).join(', ')}.`);
|
|
}
|
|
if (arbitrumHubBlocker.active) {
|
|
blockers.push(`Arbitrum public-network bootstrap remains blocked on the current Mainnet hub leg: tx ${arbitrumHubBlocker.failedTxHash} reverted from ${arbitrumHubBlocker.sourceBridge} before any bridge event was emitted.`);
|
|
}
|
|
|
|
const report = {
|
|
generatedAt: new Date().toISOString(),
|
|
canonicalChainId: 138,
|
|
summary: {
|
|
desiredPublicEvmTargets: desiredChainRows.length,
|
|
loadedPublicEvmChains: loadedChains.length,
|
|
loadedPublicEvmFullCoreSuite: fullCoreSuiteChains.length,
|
|
desiredButNotLoaded: desiredButNotLoaded.length,
|
|
publicProtocolsTracked: protocolResults.length,
|
|
publicProtocolsWithActiveCwPools: protocolResults.filter((item) => item.activePublicCwPools > 0).length,
|
|
chainsWithAnyRecordedPublicCwPools: chainsWithAnyPools.length,
|
|
liveTransportAssets: rolloutSummary.liveTransportAssets,
|
|
wave1CanonicalOnly: rolloutSummary.wave1CanonicalOnly,
|
|
backlogAssets: rolloutSummary.backlogAssets
|
|
},
|
|
publicEvmMesh: {
|
|
coreCwSuite: coreCwSymbols,
|
|
desiredChains: desiredChainRows,
|
|
desiredButNotLoaded: desiredButNotLoaded.map((row) => ({ chainId: row.chainId, name: row.name })),
|
|
wave1PoolMatrixCoverage: {
|
|
totalWrappedSymbols: wave1WrappedSymbols.length,
|
|
coveredSymbols: rolloutSummary.wave1WrappedSymbolsCoveredByPoolMatrix,
|
|
missingSymbols: wave1WrappedSymbolsMissingFromPoolMatrix
|
|
},
|
|
note: desiredButNotLoaded.length > 0
|
|
? `The public EVM cW token mesh is complete on the currently loaded ${loadedChains.length}-chain set, but ${desiredButNotLoaded.map((row) => row.name).join(', ')} ${desiredButNotLoaded.length === 1 ? 'remains a desired target without a cW suite' : 'remain desired targets without cW suites'} in deployment-status.json.`
|
|
: `The public EVM cW token mesh is complete across all ${loadedChains.length} desired public EVM targets recorded in deployment-status.json.`
|
|
},
|
|
transport: {
|
|
liveTransportAssets: allAssetResults.filter((asset) => asset.transportActive).map((asset) => ({ code: asset.code, name: asset.name })),
|
|
wave1: wave1Results,
|
|
note: 'USD is the only live transport asset today. Wave 1 non-USD assets are deployed canonically on Chain 138 but are not yet promoted into the active transport overlay.'
|
|
},
|
|
protocols: {
|
|
publicCwMesh: protocolResults,
|
|
chain138CanonicalVenues: {
|
|
note: 'Chain 138 canonical routing is a separate surface: DODO PMM plus upstream-native Uniswap v3 and the funded pilot-compatible Balancer, Curve 3, and 1inch venues are live there.',
|
|
liveProtocols: ['DODO PMM', 'Uniswap v3', 'Balancer', 'Curve 3', '1inch']
|
|
}
|
|
},
|
|
bridgeRouteHealth: {
|
|
arbitrumHubBlocker
|
|
},
|
|
explorer: {
|
|
tokenListApi: 'https://explorer.d-bis.org/api/config/token-list',
|
|
staticStatusPath: 'https://explorer.d-bis.org/config/GRU_V2_PUBLIC_DEPLOYMENT_STATUS.json'
|
|
},
|
|
blockers
|
|
};
|
|
|
|
if (writeExplorerConfig) {
|
|
fs.writeFileSync(explorerConfigPath, `${JSON.stringify(report, null, 2)}\n`);
|
|
}
|
|
|
|
if (outputJson) {
|
|
console.log(JSON.stringify(report, null, 2));
|
|
process.exit(0);
|
|
}
|
|
|
|
console.log('=== GRU V2 Public-Protocol Rollout Status ===');
|
|
console.log(`Desired public EVM targets: ${report.summary.desiredPublicEvmTargets}`);
|
|
console.log(`Loaded public EVM chains: ${report.summary.loadedPublicEvmChains}`);
|
|
console.log(`Loaded chains with full core cW suite: ${report.summary.loadedPublicEvmFullCoreSuite}`);
|
|
console.log(`Desired targets still unloaded: ${report.summary.desiredButNotLoaded}`);
|
|
console.log(`Live transport assets: ${report.summary.liveTransportAssets}`);
|
|
console.log(`Wave 1 canonical-only assets: ${report.summary.wave1CanonicalOnly}`);
|
|
console.log(`Wave 1 wrapped symbols covered by pool-matrix: ${report.publicEvmMesh.wave1PoolMatrixCoverage.coveredSymbols}/${report.publicEvmMesh.wave1PoolMatrixCoverage.totalWrappedSymbols}`);
|
|
console.log(`Backlog assets: ${report.summary.backlogAssets}`);
|
|
console.log(`Tracked public protocols: ${report.summary.publicProtocolsTracked}`);
|
|
console.log(`Protocols with active public cW pools: ${report.summary.publicProtocolsWithActiveCwPools}`);
|
|
console.log(`Chains with any recorded public cW pools: ${report.summary.chainsWithAnyRecordedPublicCwPools}`);
|
|
console.log('');
|
|
console.log('Wave 1:');
|
|
for (const asset of wave1Results) {
|
|
console.log(`- ${asset.code} (${asset.name}) -> ${asset.currentState}; next: ${asset.nextStep}`);
|
|
}
|
|
console.log('');
|
|
console.log('Public protocol surface:');
|
|
for (const protocol of protocolResults) {
|
|
console.log(`- ${protocol.name}: ${protocol.status}`);
|
|
}
|
|
if (blockers.length > 0) {
|
|
console.log('');
|
|
console.log('Active blockers:');
|
|
for (const blocker of blockers) {
|
|
console.log(`- ${blocker}`);
|
|
}
|
|
}
|
|
if (writeExplorerConfig) {
|
|
console.log('');
|
|
console.log(`Wrote: ${explorerConfigPath}`);
|
|
}
|
|
NODE
|