- Institutional / JVMTM / reserve-provenance / GRU transport + standards JSON - Validation and verify scripts (Blockscout labels, x402, GRU preflight, P1 local path) - Wormhole wiring in AGENTS, MCP_SETUP, MASTER_INDEX, 04-configuration README - Meta docs, integration gaps, live verification log, architecture updates - CI validate-config workflow updates Operator/LAN items, submodule working trees, and public token-aggregation edge routes remain follow-up (see TODOS_CONSOLIDATED P1). Made-with: Cursor
622 lines
26 KiB
JavaScript
622 lines
26 KiB
JavaScript
/**
|
|
* Load relay mappings, GRU transport overlay config, routing registry, and deployment JSON.
|
|
* Used by relay service, token-aggregation, bridge/LP tooling, and docs. Safe to publish.
|
|
*
|
|
* @version 2026-03-30
|
|
*/
|
|
|
|
const path = require('path');
|
|
const fs = require('fs');
|
|
|
|
const ZERO_ADDRESS = '0x0000000000000000000000000000000000000000';
|
|
|
|
const DEFAULT_JSON_PATH = path.resolve(__dirname, 'token-mapping.json');
|
|
const DEFAULT_MULTICHAIN_JSON_PATH = path.resolve(__dirname, 'token-mapping-multichain.json');
|
|
const DEFAULT_GRU_ACTIVE_JSON_PATH = path.resolve(__dirname, 'gru-transport-active.json');
|
|
const DEFAULT_ROUTING_REGISTRY_JSON_PATH = path.resolve(__dirname, 'routing-registry.json');
|
|
const DEFAULT_DEPLOYMENT_STATUS_JSON_PATH = path.resolve(
|
|
__dirname,
|
|
'..',
|
|
'cross-chain-pmm-lps',
|
|
'config',
|
|
'deployment-status.json'
|
|
);
|
|
const DEFAULT_POOL_MATRIX_JSON_PATH = path.resolve(
|
|
__dirname,
|
|
'..',
|
|
'cross-chain-pmm-lps',
|
|
'config',
|
|
'pool-matrix.json'
|
|
);
|
|
|
|
const JSON_CACHES = {
|
|
token: null,
|
|
multichain: null,
|
|
gruTransport: null,
|
|
routingRegistry: null,
|
|
deploymentStatus: null,
|
|
poolMatrix: null,
|
|
};
|
|
|
|
function loadCachedJson(cacheKey, jsonPath) {
|
|
const current = JSON_CACHES[cacheKey];
|
|
if (current && current.path === jsonPath) return current.data;
|
|
try {
|
|
const raw = fs.readFileSync(jsonPath, 'utf8');
|
|
const data = JSON.parse(raw);
|
|
JSON_CACHES[cacheKey] = { path: jsonPath, data };
|
|
return data;
|
|
} catch (e) {
|
|
return null;
|
|
}
|
|
}
|
|
|
|
function normalizeAddress(address) {
|
|
return typeof address === 'string' ? address.trim().toLowerCase() : '';
|
|
}
|
|
|
|
function normalizeSymbol(symbol) {
|
|
return typeof symbol === 'string' ? symbol.trim().toLowerCase() : '';
|
|
}
|
|
|
|
function normalizeTransportSymbol(symbol) {
|
|
const normalized = normalizeSymbol(symbol).replace(/[\s_-]/g, '');
|
|
if (normalized.startsWith('cw')) {
|
|
return `c${normalized.slice(2)}`;
|
|
}
|
|
return normalized;
|
|
}
|
|
|
|
function isNonZeroAddress(address) {
|
|
const normalized = normalizeAddress(address);
|
|
return /^0x[a-f0-9]{40}$/.test(normalized) && normalized !== ZERO_ADDRESS;
|
|
}
|
|
|
|
function resolveConfigRef(ref) {
|
|
if (!ref || typeof ref !== 'object') return '';
|
|
if (isNonZeroAddress(ref.address)) return ref.address;
|
|
if (typeof ref.env === 'string' && isNonZeroAddress(process.env[ref.env])) {
|
|
return process.env[ref.env];
|
|
}
|
|
return '';
|
|
}
|
|
|
|
function hasConfigRef(ref) {
|
|
if (!ref || typeof ref !== 'object') return false;
|
|
return isNonZeroAddress(ref.address) || (typeof ref.env === 'string' && ref.env.trim() !== '');
|
|
}
|
|
|
|
function resolvePolicyRefValue(ref) {
|
|
if (!ref || typeof ref !== 'object') return '';
|
|
if (typeof ref.amount === 'string' && ref.amount.trim() !== '') return ref.amount.trim();
|
|
if (typeof ref.env === 'string') {
|
|
const value = process.env[ref.env];
|
|
if (typeof value === 'string' && value.trim() !== '') return value.trim();
|
|
}
|
|
return '';
|
|
}
|
|
|
|
function loadTokenMappingJson(jsonPath = DEFAULT_JSON_PATH) {
|
|
return loadCachedJson('token', jsonPath);
|
|
}
|
|
|
|
function loadTokenMappingMultichainJson(jsonPath = DEFAULT_MULTICHAIN_JSON_PATH) {
|
|
return loadCachedJson('multichain', jsonPath);
|
|
}
|
|
|
|
function loadGruTransportActiveJson(jsonPath = DEFAULT_GRU_ACTIVE_JSON_PATH) {
|
|
return loadCachedJson('gruTransport', jsonPath);
|
|
}
|
|
|
|
function loadRoutingRegistryJson(jsonPath = DEFAULT_ROUTING_REGISTRY_JSON_PATH) {
|
|
return loadCachedJson('routingRegistry', jsonPath);
|
|
}
|
|
|
|
function loadDeploymentStatusJson(jsonPath = DEFAULT_DEPLOYMENT_STATUS_JSON_PATH) {
|
|
return loadCachedJson('deploymentStatus', jsonPath);
|
|
}
|
|
|
|
function loadPoolMatrixJson(jsonPath = DEFAULT_POOL_MATRIX_JSON_PATH) {
|
|
return loadCachedJson('poolMatrix', jsonPath);
|
|
}
|
|
|
|
function getRelayTokenMapping(jsonPath) {
|
|
const data = loadTokenMappingJson(jsonPath);
|
|
if (!data || !Array.isArray(data.tokens)) return {};
|
|
const out = {};
|
|
for (const t of data.tokens) {
|
|
if (t.chain138Address && t.mainnetAddress) {
|
|
out[t.chain138Address] = t.mainnetAddress;
|
|
}
|
|
}
|
|
return out;
|
|
}
|
|
|
|
function getTokenList(jsonPath) {
|
|
const data = loadTokenMappingJson(jsonPath);
|
|
if (!data || !Array.isArray(data.tokens)) return [];
|
|
return data.tokens;
|
|
}
|
|
|
|
function getTokenMappingForPair(fromChainId, toChainId, jsonPath) {
|
|
const data = loadTokenMappingMultichainJson(jsonPath);
|
|
if (!data || !Array.isArray(data.pairs)) return null;
|
|
const from = Number(fromChainId);
|
|
const to = Number(toChainId);
|
|
let pair = data.pairs.find((p) => p.fromChainId === from && p.toChainId === to);
|
|
let reverse = false;
|
|
if (!pair) {
|
|
pair = data.pairs.find((p) => p.fromChainId === to && p.toChainId === from);
|
|
reverse = true;
|
|
}
|
|
if (!pair || !Array.isArray(pair.tokens)) return null;
|
|
const tokens = reverse
|
|
? pair.tokens.map((t) => ({
|
|
key: t.key,
|
|
name: t.name,
|
|
addressFrom: t.addressTo,
|
|
addressTo: t.addressFrom,
|
|
notes: t.notes,
|
|
}))
|
|
: pair.tokens;
|
|
const addressMapFromTo = {};
|
|
const addressMapToFrom = {};
|
|
for (const t of tokens) {
|
|
if (t.addressFrom && t.addressTo) {
|
|
addressMapFromTo[t.addressFrom.toLowerCase()] = t.addressTo;
|
|
addressMapToFrom[t.addressTo.toLowerCase()] = t.addressFrom;
|
|
}
|
|
}
|
|
return { tokens, addressMapFromTo, addressMapToFrom };
|
|
}
|
|
|
|
function getAllMultichainPairs(jsonPath) {
|
|
const data = loadTokenMappingMultichainJson(jsonPath);
|
|
if (!data || !Array.isArray(data.pairs)) return [];
|
|
return data.pairs.map((p) => ({ fromChainId: p.fromChainId, toChainId: p.toChainId, notes: p.notes }));
|
|
}
|
|
|
|
function getMappedAddress(fromChainId, toChainId, tokenAddressOnSource, jsonPath) {
|
|
const activeTransportPair = getActiveTransportPair(
|
|
fromChainId,
|
|
toChainId,
|
|
{ sourceTokenAddress: tokenAddressOnSource },
|
|
{ multichainJsonPath: jsonPath }
|
|
);
|
|
if (activeTransportPair) {
|
|
const sameDirection =
|
|
Number(activeTransportPair.canonicalChainId) === Number(fromChainId) &&
|
|
Number(activeTransportPair.destinationChainId) === Number(toChainId);
|
|
const targetAddress = sameDirection ? activeTransportPair.mirroredAddress : activeTransportPair.canonicalAddress;
|
|
if (isNonZeroAddress(targetAddress)) {
|
|
return targetAddress;
|
|
}
|
|
}
|
|
const result = getTokenMappingForPair(fromChainId, toChainId, jsonPath);
|
|
if (!result) return undefined;
|
|
return result.addressMapFromTo[String(tokenAddressOnSource).toLowerCase()];
|
|
}
|
|
|
|
function getRoutingRegistryRoutes(jsonPath = DEFAULT_ROUTING_REGISTRY_JSON_PATH) {
|
|
const data = loadRoutingRegistryJson(jsonPath);
|
|
if (!data || !Array.isArray(data.routes)) return [];
|
|
return data.routes;
|
|
}
|
|
|
|
function getGruTransportMetadata(jsonPath = DEFAULT_GRU_ACTIVE_JSON_PATH) {
|
|
const data = loadGruTransportActiveJson(jsonPath);
|
|
if (!data || typeof data !== 'object') return null;
|
|
const activeTransportPairs = getActiveTransportPairs({ activeJsonPath: jsonPath });
|
|
const activePublicPools = getActivePublicPools(jsonPath);
|
|
return {
|
|
system: data.system || null,
|
|
terminology: data.terminology || {},
|
|
enabledCanonicalTokens: Array.isArray(data.enabledCanonicalTokens) ? data.enabledCanonicalTokens : [],
|
|
enabledDestinationChains: Array.isArray(data.enabledDestinationChains) ? data.enabledDestinationChains : [],
|
|
counts: {
|
|
enabledCanonicalTokens: Array.isArray(data.enabledCanonicalTokens) ? data.enabledCanonicalTokens.length : 0,
|
|
enabledDestinationChains: Array.isArray(data.enabledDestinationChains) ? data.enabledDestinationChains.length : 0,
|
|
approvedBridgePeers: Array.isArray(data.approvedBridgePeers) ? data.approvedBridgePeers.length : 0,
|
|
transportPairs: Array.isArray(data.transportPairs) ? data.transportPairs.length : 0,
|
|
eligibleTransportPairs: activeTransportPairs.filter((pair) => pair.eligible).length,
|
|
runtimeReadyTransportPairs: activeTransportPairs.filter((pair) => pair.runtimeReady).length,
|
|
publicPools: Array.isArray(data.publicPools) ? data.publicPools.length : 0,
|
|
activePublicPools: activePublicPools.filter((pool) => pool.active === true).length,
|
|
routablePublicPools: activePublicPools.filter(
|
|
(pool) => pool.active === true && pool.routingEnabled === true
|
|
).length,
|
|
mcpVisiblePublicPools: activePublicPools.filter(
|
|
(pool) => pool.active === true && pool.mcpVisible === true
|
|
).length,
|
|
},
|
|
};
|
|
}
|
|
|
|
function getEnabledCanonicalTokens(jsonPath = DEFAULT_GRU_ACTIVE_JSON_PATH) {
|
|
const data = loadGruTransportActiveJson(jsonPath);
|
|
if (!data || !Array.isArray(data.enabledCanonicalTokens)) return [];
|
|
return data.enabledCanonicalTokens;
|
|
}
|
|
|
|
function getEnabledCanonicalToken(identifier, jsonPath = DEFAULT_GRU_ACTIVE_JSON_PATH) {
|
|
const normalizedSymbol = normalizeSymbol(identifier || '');
|
|
const normalizedAddress = normalizeAddress(identifier || '');
|
|
|
|
return (
|
|
getEnabledCanonicalTokens(jsonPath).find((token) => {
|
|
if (normalizedSymbol) {
|
|
if (normalizeSymbol(token.symbol) === normalizedSymbol) return true;
|
|
if (normalizeSymbol(token.mirroredSymbol) === normalizedSymbol) return true;
|
|
}
|
|
|
|
if (!normalizedAddress) return false;
|
|
if (normalizeAddress(token.activeAddress) === normalizedAddress) return true;
|
|
if (normalizeAddress(token.x402PreferredAddress) === normalizedAddress) return true;
|
|
|
|
if (Array.isArray(token.deployments)) {
|
|
return token.deployments.some((deployment) => normalizeAddress(deployment.address) === normalizedAddress);
|
|
}
|
|
|
|
return false;
|
|
}) || null
|
|
);
|
|
}
|
|
|
|
function getEnabledDestinationChains(jsonPath = DEFAULT_GRU_ACTIVE_JSON_PATH) {
|
|
const data = loadGruTransportActiveJson(jsonPath);
|
|
if (!data || !Array.isArray(data.enabledDestinationChains)) return [];
|
|
return data.enabledDestinationChains;
|
|
}
|
|
|
|
function isCanonicalTokenActive(symbol, jsonPath = DEFAULT_GRU_ACTIVE_JSON_PATH) {
|
|
const normalized = normalizeSymbol(symbol);
|
|
return getEnabledCanonicalTokens(jsonPath).some((token) => normalizeSymbol(token.symbol) === normalized);
|
|
}
|
|
|
|
function isDestinationChainActive(chainId, jsonPath = DEFAULT_GRU_ACTIVE_JSON_PATH) {
|
|
const numericChainId = Number(chainId);
|
|
return getEnabledDestinationChains(jsonPath).some((chain) => Number(chain.chainId) === numericChainId);
|
|
}
|
|
|
|
function getApprovedBridgePeer(chainId, jsonPath = DEFAULT_GRU_ACTIVE_JSON_PATH) {
|
|
const data = loadGruTransportActiveJson(jsonPath);
|
|
if (!data || !Array.isArray(data.approvedBridgePeers)) return null;
|
|
const numericChainId = Number(chainId);
|
|
return data.approvedBridgePeers.find((peer) => Number(peer.chainId) === numericChainId) || null;
|
|
}
|
|
|
|
function getRawMappingTokenEntry(fromChainId, toChainId, mappingKey, jsonPath = DEFAULT_MULTICHAIN_JSON_PATH) {
|
|
const data = loadTokenMappingMultichainJson(jsonPath);
|
|
if (!data || !Array.isArray(data.pairs)) return null;
|
|
const pair = data.pairs.find(
|
|
(entry) => Number(entry.fromChainId) === Number(fromChainId) && Number(entry.toChainId) === Number(toChainId)
|
|
);
|
|
if (!pair || !Array.isArray(pair.tokens)) return null;
|
|
return pair.tokens.find((token) => token.key === mappingKey) || null;
|
|
}
|
|
|
|
function getActiveTransportPairs(options = {}) {
|
|
const activeJsonPath = options.activeJsonPath || DEFAULT_GRU_ACTIVE_JSON_PATH;
|
|
const multichainJsonPath = options.multichainJsonPath || DEFAULT_MULTICHAIN_JSON_PATH;
|
|
const deploymentJsonPath = options.deploymentJsonPath || DEFAULT_DEPLOYMENT_STATUS_JSON_PATH;
|
|
const active = loadGruTransportActiveJson(activeJsonPath);
|
|
const deployment = loadDeploymentStatusJson(deploymentJsonPath);
|
|
if (!active || !Array.isArray(active.transportPairs)) return [];
|
|
|
|
const enabledCanonicalTokens = new Set(
|
|
(Array.isArray(active.enabledCanonicalTokens) ? active.enabledCanonicalTokens : []).map((token) => normalizeSymbol(token.symbol))
|
|
);
|
|
const enabledDestinationChains = new Set(
|
|
(Array.isArray(active.enabledDestinationChains) ? active.enabledDestinationChains : []).map((chain) => Number(chain.chainId))
|
|
);
|
|
const peersByKey = new Map(
|
|
(Array.isArray(active.approvedBridgePeers) ? active.approvedBridgePeers : []).map((peer) => [String(peer.key), peer])
|
|
);
|
|
const reserveVerifiers = active.reserveVerifiers && typeof active.reserveVerifiers === 'object' ? active.reserveVerifiers : {};
|
|
|
|
return active.transportPairs.map((pair) => {
|
|
const canonicalChainId = Number(pair.canonicalChainId ?? active.system?.canonicalChainId ?? 138);
|
|
const destinationChainId = Number(pair.destinationChainId);
|
|
const canonicalSymbol = String(pair.canonicalSymbol || '').trim();
|
|
const mirroredSymbol = String(pair.mirroredSymbol || '').trim();
|
|
const mappingEntry = getRawMappingTokenEntry(canonicalChainId, destinationChainId, pair.mappingKey, multichainJsonPath);
|
|
const deploymentChain =
|
|
deployment && deployment.chains && typeof deployment.chains === 'object'
|
|
? deployment.chains[String(destinationChainId)] || null
|
|
: null;
|
|
const mirrorDeploymentAddress =
|
|
deploymentChain && deploymentChain.cwTokens && typeof deploymentChain.cwTokens === 'object'
|
|
? deploymentChain.cwTokens[mirroredSymbol] || null
|
|
: null;
|
|
const peer = peersByKey.get(String(pair.peerKey || '')) || null;
|
|
const maxOutstanding = pair.maxOutstanding && typeof pair.maxOutstanding === 'object' ? pair.maxOutstanding : {};
|
|
const reserveVerifier = pair.reserveVerifierKey ? reserveVerifiers[pair.reserveVerifierKey] : null;
|
|
const routeDiscoveryEnabled = pair.routeDiscoveryEnabled !== false;
|
|
const canonicalAddress = mappingEntry?.addressFrom || null;
|
|
const mirroredAddress = mappingEntry?.addressTo || null;
|
|
const runtimeL1BridgeAddress = peer ? resolveConfigRef(peer.l1Bridge) : '';
|
|
const runtimeL2BridgeAddress = peer ? resolveConfigRef(peer.l2Bridge) : '';
|
|
const runtimeMaxOutstandingValue = resolvePolicyRefValue(maxOutstanding);
|
|
const runtimeReserveVerifier = reserveVerifier && typeof reserveVerifier === 'object' ? reserveVerifier : null;
|
|
const runtimeReserveVerifierBridgeAddress = runtimeReserveVerifier ? resolveConfigRef(runtimeReserveVerifier.bridgeRef) : '';
|
|
const runtimeReserveVerifierAddress = runtimeReserveVerifier ? resolveConfigRef(runtimeReserveVerifier.verifierRef) : '';
|
|
const runtimeReserveVaultAddress = runtimeReserveVerifier ? resolveConfigRef(runtimeReserveVerifier.vaultRef) : '';
|
|
const runtimeReserveSystemAddress = runtimeReserveVerifier ? resolveConfigRef(runtimeReserveVerifier.reserveSystemRef) : '';
|
|
const mirrorDeployed =
|
|
isNonZeroAddress(mirrorDeploymentAddress) &&
|
|
isNonZeroAddress(mirroredAddress) &&
|
|
normalizeAddress(mirrorDeploymentAddress) === normalizeAddress(mirroredAddress);
|
|
|
|
const bridgePeerConfigured =
|
|
!!peer &&
|
|
hasConfigRef(peer.l1Bridge) &&
|
|
hasConfigRef(peer.l2Bridge);
|
|
|
|
const maxOutstandingConfigured = !maxOutstanding.required || !!maxOutstanding.amount || !!maxOutstanding.env;
|
|
const reserveVerifierConfigured =
|
|
!pair.reserveVerifierKey ||
|
|
(!!runtimeReserveVerifier &&
|
|
hasConfigRef(runtimeReserveVerifier.bridgeRef) &&
|
|
hasConfigRef(runtimeReserveVerifier.verifierRef) &&
|
|
(!runtimeReserveVerifier.requireVaultBacking || hasConfigRef(runtimeReserveVerifier.vaultRef)) &&
|
|
(!runtimeReserveVerifier.requireReserveSystemBalance || hasConfigRef(runtimeReserveVerifier.reserveSystemRef)) &&
|
|
(!runtimeReserveVerifier.requireTokenOwnerMatchVault || hasConfigRef(runtimeReserveVerifier.vaultRef)));
|
|
const runtimeBridgeReady = !!runtimeL1BridgeAddress && !!runtimeL2BridgeAddress;
|
|
const runtimeMaxOutstandingReady = !maxOutstanding.required || !!runtimeMaxOutstandingValue;
|
|
const runtimeReserveVerifierReady =
|
|
!pair.reserveVerifierKey ||
|
|
(!!runtimeReserveVerifierBridgeAddress &&
|
|
!!runtimeReserveVerifierAddress &&
|
|
(!runtimeReserveVerifier.requireVaultBacking || !!runtimeReserveVaultAddress) &&
|
|
(!runtimeReserveVerifier.requireReserveSystemBalance || !!runtimeReserveSystemAddress) &&
|
|
(!runtimeReserveVerifier.requireTokenOwnerMatchVault || !!runtimeReserveVaultAddress));
|
|
const eligibilityBlockers = [];
|
|
if (!routeDiscoveryEnabled) eligibilityBlockers.push('policy:routeDiscoveryDisabled');
|
|
if (!enabledCanonicalTokens.has(normalizeSymbol(canonicalSymbol))) {
|
|
eligibilityBlockers.push('overlay:canonicalTokenDisabled');
|
|
}
|
|
if (!enabledDestinationChains.has(destinationChainId)) {
|
|
eligibilityBlockers.push('overlay:destinationChainDisabled');
|
|
}
|
|
if (!mappingEntry) eligibilityBlockers.push('mapping:pairMissing');
|
|
if (!isNonZeroAddress(canonicalAddress)) eligibilityBlockers.push('mapping:canonicalAddressMissing');
|
|
if (!isNonZeroAddress(mirroredAddress)) eligibilityBlockers.push('mapping:mirroredAddressMissing');
|
|
if (!mirrorDeployed) eligibilityBlockers.push('deployment:mirroredTokenNotDeployed');
|
|
if (!bridgePeerConfigured) eligibilityBlockers.push('config:bridgePeerRefMissing');
|
|
if (!maxOutstandingConfigured) eligibilityBlockers.push('config:maxOutstandingRefMissing');
|
|
if (!reserveVerifierConfigured) eligibilityBlockers.push('config:reserveVerifierRefMissing');
|
|
|
|
const eligible = eligibilityBlockers.length === 0;
|
|
const runtimeMissingRequirements = [];
|
|
if (!runtimeL1BridgeAddress) runtimeMissingRequirements.push('bridge:l1Bridge');
|
|
if (!runtimeL2BridgeAddress) runtimeMissingRequirements.push('bridge:l2Bridge');
|
|
if (maxOutstanding.required && !runtimeMaxOutstandingValue) {
|
|
runtimeMissingRequirements.push('policy:maxOutstanding');
|
|
}
|
|
if (pair.reserveVerifierKey) {
|
|
if (!runtimeReserveVerifierBridgeAddress) runtimeMissingRequirements.push('reserveVerifier:bridgeRef');
|
|
if (!runtimeReserveVerifierAddress) runtimeMissingRequirements.push('reserveVerifier:verifierRef');
|
|
if (runtimeReserveVerifier?.requireVaultBacking && !runtimeReserveVaultAddress) {
|
|
runtimeMissingRequirements.push('reserveVerifier:vaultRef');
|
|
}
|
|
if (runtimeReserveVerifier?.requireReserveSystemBalance && !runtimeReserveSystemAddress) {
|
|
runtimeMissingRequirements.push('reserveVerifier:reserveSystemRef');
|
|
}
|
|
}
|
|
if (deploymentChain?.bridgeAvailable === false) {
|
|
runtimeMissingRequirements.push('deployment:bridgeUnavailable');
|
|
}
|
|
const runtimeReady = eligible && runtimeMissingRequirements.length === 0;
|
|
|
|
return {
|
|
...pair,
|
|
canonicalChainId,
|
|
destinationChainId,
|
|
canonicalSymbol,
|
|
mirroredSymbol,
|
|
canonicalAddress,
|
|
mirroredAddress,
|
|
mirrorDeploymentAddress,
|
|
peer,
|
|
mappingFound: !!mappingEntry,
|
|
mirrorDeployed,
|
|
canonicalEnabled: enabledCanonicalTokens.has(normalizeSymbol(canonicalSymbol)),
|
|
destinationEnabled: enabledDestinationChains.has(destinationChainId),
|
|
bridgeAvailable: deploymentChain?.bridgeAvailable ?? null,
|
|
bridgePeerConfigured,
|
|
maxOutstandingConfigured,
|
|
reserveVerifierConfigured,
|
|
runtimeL1BridgeAddress: runtimeL1BridgeAddress || null,
|
|
runtimeL2BridgeAddress: runtimeL2BridgeAddress || null,
|
|
runtimeBridgeReady,
|
|
runtimeMaxOutstandingValue: runtimeMaxOutstandingValue || null,
|
|
runtimeMaxOutstandingReady,
|
|
runtimeReserveVerifierBridgeAddress: runtimeReserveVerifierBridgeAddress || null,
|
|
runtimeReserveVerifierAddress: runtimeReserveVerifierAddress || null,
|
|
runtimeReserveVaultAddress: runtimeReserveVaultAddress || null,
|
|
runtimeReserveSystemAddress: runtimeReserveSystemAddress || null,
|
|
runtimeReserveVerifierReady,
|
|
runtimeMissingRequirements,
|
|
eligibilityBlockers,
|
|
runtimeReady,
|
|
eligible,
|
|
};
|
|
});
|
|
}
|
|
|
|
function getActiveTransportPair(fromChainId, toChainId, criteria = {}, options = {}) {
|
|
const from = Number(fromChainId);
|
|
const to = Number(toChainId);
|
|
const normalizedSymbol = normalizeTransportSymbol(
|
|
criteria.symbol || criteria.canonicalSymbol || criteria.mirroredSymbol || ''
|
|
);
|
|
const normalizedSourceAddress = normalizeAddress(
|
|
criteria.address || criteria.sourceTokenAddress || criteria.tokenAddress || ''
|
|
);
|
|
const normalizedTargetAddress = normalizeAddress(criteria.targetTokenAddress || '');
|
|
|
|
return (
|
|
getActiveTransportPairs(options).find((pair) => {
|
|
const sameDirection = pair.canonicalChainId === from && pair.destinationChainId === to;
|
|
const reverseDirection = pair.canonicalChainId === to && pair.destinationChainId === from;
|
|
if (!sameDirection && !reverseDirection) return false;
|
|
|
|
if (normalizedSymbol) {
|
|
const pairSymbols = new Set([
|
|
normalizeTransportSymbol(pair.canonicalSymbol),
|
|
normalizeTransportSymbol(pair.mirroredSymbol),
|
|
normalizeSymbol(pair.canonicalSymbol),
|
|
normalizeSymbol(pair.mirroredSymbol),
|
|
]);
|
|
if (!pairSymbols.has(normalizedSymbol)) return false;
|
|
}
|
|
|
|
if (normalizedSourceAddress) {
|
|
const allowedSourceAddresses = sameDirection
|
|
? [pair.canonicalAddress, pair.mirroredAddress]
|
|
: [pair.mirroredAddress, pair.canonicalAddress];
|
|
if (!allowedSourceAddresses.some((address) => normalizeAddress(address) === normalizedSourceAddress)) {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
if (normalizedTargetAddress) {
|
|
const targetAddress = sameDirection ? pair.mirroredAddress : pair.canonicalAddress;
|
|
if (normalizeAddress(targetAddress) !== normalizedTargetAddress) return false;
|
|
}
|
|
|
|
return true;
|
|
}) || null
|
|
);
|
|
}
|
|
|
|
function getKnownMirroredTokenAddresses(chainId, options = {}) {
|
|
const multichainJsonPath = options.multichainJsonPath || DEFAULT_MULTICHAIN_JSON_PATH;
|
|
const deploymentJsonPath = options.deploymentJsonPath || DEFAULT_DEPLOYMENT_STATUS_JSON_PATH;
|
|
const data = loadTokenMappingMultichainJson(multichainJsonPath);
|
|
const deployment = loadDeploymentStatusJson(deploymentJsonPath);
|
|
const chainKey = String(Number(chainId));
|
|
const out = new Set();
|
|
|
|
if (deployment && deployment.chains && deployment.chains[chainKey]?.cwTokens) {
|
|
for (const address of Object.values(deployment.chains[chainKey].cwTokens)) {
|
|
if (isNonZeroAddress(address)) out.add(normalizeAddress(address));
|
|
}
|
|
}
|
|
|
|
if (data && Array.isArray(data.pairs)) {
|
|
const pair = data.pairs.find((entry) => Number(entry.fromChainId) === 138 && Number(entry.toChainId) === Number(chainId));
|
|
if (pair && Array.isArray(pair.tokens)) {
|
|
for (const token of pair.tokens) {
|
|
if (String(token.key || '').endsWith('_cW') && isNonZeroAddress(token.addressTo)) {
|
|
out.add(normalizeAddress(token.addressTo));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return Array.from(out);
|
|
}
|
|
|
|
function getActivePublicPools(jsonPath = DEFAULT_GRU_ACTIVE_JSON_PATH) {
|
|
const data = loadGruTransportActiveJson(jsonPath);
|
|
if (!data || !Array.isArray(data.publicPools)) return [];
|
|
return data.publicPools;
|
|
}
|
|
|
|
function getPublicPoolRecord(chainId, poolAddress, jsonPath = DEFAULT_GRU_ACTIVE_JSON_PATH) {
|
|
if (!isNonZeroAddress(poolAddress)) return null;
|
|
const normalizedPoolAddress = normalizeAddress(poolAddress);
|
|
return (
|
|
getActivePublicPools(jsonPath).find(
|
|
(pool) => Number(pool.chainId) === Number(chainId) && normalizeAddress(pool.poolAddress) === normalizedPoolAddress
|
|
) || null
|
|
);
|
|
}
|
|
|
|
function isPublicPoolActive(chainId, poolAddress, jsonPath = DEFAULT_GRU_ACTIVE_JSON_PATH) {
|
|
const record = getPublicPoolRecord(chainId, poolAddress, jsonPath);
|
|
return !!record && record.active === true;
|
|
}
|
|
|
|
function isPublicPoolRoutable(chainId, poolAddress, jsonPath = DEFAULT_GRU_ACTIVE_JSON_PATH) {
|
|
const record = getPublicPoolRecord(chainId, poolAddress, jsonPath);
|
|
return !!record && record.active === true && record.routingEnabled === true;
|
|
}
|
|
|
|
function isPublicPoolMcpVisible(chainId, poolAddress, jsonPath = DEFAULT_GRU_ACTIVE_JSON_PATH) {
|
|
const record = getPublicPoolRecord(chainId, poolAddress, jsonPath);
|
|
return !!record && record.active === true && record.mcpVisible === true;
|
|
}
|
|
|
|
function shouldExposePublicPool(
|
|
chainId,
|
|
poolAddress,
|
|
token0Address,
|
|
token1Address,
|
|
activeJsonPath = DEFAULT_GRU_ACTIVE_JSON_PATH,
|
|
multichainJsonPath = DEFAULT_MULTICHAIN_JSON_PATH,
|
|
deploymentJsonPath = DEFAULT_DEPLOYMENT_STATUS_JSON_PATH
|
|
) {
|
|
const mirroredAddresses = new Set(getKnownMirroredTokenAddresses(chainId, { multichainJsonPath, deploymentJsonPath }));
|
|
const touchesMirroredToken =
|
|
mirroredAddresses.has(normalizeAddress(token0Address)) || mirroredAddresses.has(normalizeAddress(token1Address));
|
|
if (!touchesMirroredToken) return true;
|
|
return isPublicPoolActive(chainId, poolAddress, activeJsonPath);
|
|
}
|
|
|
|
function shouldUsePublicPoolForRouting(
|
|
chainId,
|
|
poolAddress,
|
|
token0Address,
|
|
token1Address,
|
|
activeJsonPath = DEFAULT_GRU_ACTIVE_JSON_PATH,
|
|
multichainJsonPath = DEFAULT_MULTICHAIN_JSON_PATH,
|
|
deploymentJsonPath = DEFAULT_DEPLOYMENT_STATUS_JSON_PATH
|
|
) {
|
|
const mirroredAddresses = new Set(getKnownMirroredTokenAddresses(chainId, { multichainJsonPath, deploymentJsonPath }));
|
|
const touchesMirroredToken =
|
|
mirroredAddresses.has(normalizeAddress(token0Address)) || mirroredAddresses.has(normalizeAddress(token1Address));
|
|
if (!touchesMirroredToken) return true;
|
|
return isPublicPoolRoutable(chainId, poolAddress, activeJsonPath);
|
|
}
|
|
|
|
module.exports = {
|
|
loadTokenMappingJson,
|
|
loadTokenMappingMultichainJson,
|
|
loadGruTransportActiveJson,
|
|
loadRoutingRegistryJson,
|
|
loadDeploymentStatusJson,
|
|
loadPoolMatrixJson,
|
|
getRelayTokenMapping,
|
|
getTokenList,
|
|
getTokenMappingForPair,
|
|
getAllMultichainPairs,
|
|
getMappedAddress,
|
|
getRoutingRegistryRoutes,
|
|
getGruTransportMetadata,
|
|
getEnabledCanonicalTokens,
|
|
getEnabledCanonicalToken,
|
|
getEnabledDestinationChains,
|
|
isCanonicalTokenActive,
|
|
isDestinationChainActive,
|
|
getApprovedBridgePeer,
|
|
getActiveTransportPairs,
|
|
getActiveTransportPair,
|
|
getKnownMirroredTokenAddresses,
|
|
getActivePublicPools,
|
|
isPublicPoolActive,
|
|
isPublicPoolRoutable,
|
|
isPublicPoolMcpVisible,
|
|
shouldExposePublicPool,
|
|
shouldUsePublicPoolForRouting,
|
|
resolveConfigRef,
|
|
isNonZeroAddress,
|
|
DEFAULT_JSON_PATH,
|
|
DEFAULT_MULTICHAIN_JSON_PATH,
|
|
DEFAULT_GRU_ACTIVE_JSON_PATH,
|
|
DEFAULT_ROUTING_REGISTRY_JSON_PATH,
|
|
DEFAULT_DEPLOYMENT_STATUS_JSON_PATH,
|
|
DEFAULT_POOL_MATRIX_JSON_PATH,
|
|
};
|