Files
smom-dbis-138/services/token-aggregation/src/api/routes/report.ts
defiQUG 21578e1a13 fix(token-aggregation): normalize inflated liquidityUsd; token-price report
- Apply decimal-aware liquidity normalization on /tokens and reports
- Add GET /report/token-price/:symbol compact evidence snapshot
- Extend route tests for normalization and token-price endpoint

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-05-11 12:55:10 -07:00

1784 lines
69 KiB
TypeScript

/**
* CMC and CoinGecko reporting API: all tokens, liquidity, volume, and reportable data.
* Use for listing submissions and external aggregator sync.
*/
import { Router, Request, Response } from 'express';
import { existsSync, readFileSync } from 'fs';
import path from 'path';
import { TokenRepository } from '../../database/repositories/token-repo';
import { MarketDataRepository } from '../../database/repositories/market-data-repo';
import { PoolRepository } from '../../database/repositories/pool-repo';
import {
CANONICAL_TOKENS,
getCanonicalTokensByChain,
getLogoUriForSpec,
getTokenRegistryFamily,
} from '../../config/canonical-tokens';
import { resolvePoolTokenDisplays } from '../../services/token-display';
import { getSupportedChainIds } from '../../config/chains';
import { cacheMiddleware } from '../middleware/cache';
import { fetchRemoteJson } from '../utils/fetch-remote-json';
import { buildCrossChainReport } from '../../indexer/cross-chain-indexer';
import { logger } from '../../utils/logger';
import {
filterPoolsForExposure,
getActiveTransportPairs,
getGruTransportMetadata,
type GruTransportGasAssetFamily,
type GruTransportPair,
} from '../../config/gru-transport';
import {
buildCwRegistryChains,
buildGasRegistryChains,
loadDeploymentStatusFile,
type DeploymentStatusFile,
type CwRegistryChain,
} from '../../config/deployment-status';
import { getGruV2DeploymentPoolRows } from '../../config/gru-v2-deployment-pools';
import { getCanonicalPriceSnapshotGeneratedAt, getCanonicalPriceUsd } from '../../services/canonical-price-oracle';
import { pmmVaultReserveFromChain, resolvePmmQuoteRpcUrl } from '../../services/pmm-onchain-quote';
const router: Router = Router();
const tokenRepo = new TokenRepository();
const marketDataRepo = new MarketDataRepository();
const poolRepo = new PoolRepository();
const MAINNET_CWUSDC_ADDRESS = '0x2de5f116bfce3d0f922d9c8351e0c5fc24b9284a';
const DEFAULT_SUPPLY_CATALOG_RELATIVE_PATH = 'config/supply-proof-catalog.json';
const DEFAULT_LIVE_UNISWAP_V2_POOL_CATALOG_RELATIVE_PATH = 'config/live-uniswap-v2-pool-catalog.json';
const DEFAULT_PUBLIC_REPORT_BASE_URL = 'https://explorer.d-bis.org';
const DBIS_CHAIN_138_LOGO_PATH = '/api/v1/report/logo/chain-138';
const CWUSDC_SUPPLY_PROOF_FALLBACK = {
schema: 'mainnet-cwusdc-supply-proof/v1',
generatedAt: '2026-05-08T03:16:54Z',
network: {
chainId: 1,
name: 'Ethereum Mainnet',
referenceBlock: 25047586,
},
token: {
address: MAINNET_CWUSDC_ADDRESS,
name: 'Wrapped cUSDC',
symbol: 'cWUSDC',
decimals: 6,
totalSupplyRaw: '10451316981309788',
totalSupplyUnits: '10451316981.309788',
},
circulatingSupplyMethodology: {
status: 'ready_for_tracker_review',
recommendedFormula: 'circulatingSupply = totalSupply - protocolControlledNonCirculatingBalances',
currentConservativeReportableCirculatingSupplyUnits: '10451316981.309788',
notes: [
'No public tracker has accepted a circulating-supply value for this contract yet.',
'If a tracker requires exclusion of operator, treasury, bridge, or protocol-controlled balances, use the knownBalances section plus any additional signed treasury inventory supplied at submission time.',
'The value above is an on-chain supply proof, not a third-party listing approval.',
],
},
};
type SupplyProofSnapshot = typeof CWUSDC_SUPPLY_PROOF_FALLBACK & {
_sourcePath?: string;
_source?: string;
};
type SupplyProofEnrichment = {
totalSupply?: string;
totalSupplyRaw?: string;
circulatingSupply?: string;
circulatingSupplyFormula?: string;
marketCapUsd?: number;
supplyProofProvenance: {
source: string;
path?: string;
schema?: string;
generatedAt?: string;
referenceBlock?: number;
status?: string;
};
trackerCaveats: string[];
};
type ReportPoolEntry = {
poolAddress: string;
dex: string;
token0: string;
token1: string;
token0Symbol?: string;
token1Symbol?: string;
tvl: number;
volume24h: number;
source?: string;
status?: string;
statusReason?: string;
role?: string;
section?: string;
publicRoutingEnabled?: boolean;
reserveSource?: string;
reserveUpdatedAt?: string;
reserve0Raw?: string;
reserve1Raw?: string;
};
type LiveUniswapV2PoolCatalogRow = {
chainId: number;
poolAddress: string;
dex?: string;
baseSymbol: string;
quoteSymbol: string;
baseAddress: string;
quoteAddress: string;
totalLiquidityUsd: number;
reserve0Usd?: number;
reserve1Usd?: number;
depthOk?: boolean;
parityOk?: boolean;
healthy?: boolean;
deviationBps?: string;
};
type LiveUniswapV2PoolCatalog = {
schema?: string;
generatedAt?: string;
pools?: LiveUniswapV2PoolCatalogRow[];
};
function resolveRepoRoot(): string {
const candidates = [
process.env.PROXMOX_REPO_ROOT,
process.env.PROJECT_ROOT,
path.resolve(process.cwd(), '..', '..', '..'),
path.resolve(process.cwd(), '..'),
process.cwd(),
].filter(Boolean) as string[];
for (const candidate of candidates) {
if (
existsSync(path.join(candidate, DEFAULT_SUPPLY_CATALOG_RELATIVE_PATH)) ||
existsSync(path.join(candidate, DEFAULT_LIVE_UNISWAP_V2_POOL_CATALOG_RELATIVE_PATH)) ||
existsSync(path.join(candidate, 'reports/status/mainnet-cwusdc-supply-proof-20260508.json'))
) {
return candidate;
}
}
return process.cwd();
}
function loadLiveUniswapV2PoolCatalog(): LiveUniswapV2PoolCatalogRow[] {
const configuredCatalog = process.env.TOKEN_AGGREGATION_LIVE_UNISWAP_V2_POOL_CATALOG_JSON?.trim();
const repoRoot = resolveRepoRoot();
const candidates = [
configuredCatalog,
path.join(repoRoot, DEFAULT_LIVE_UNISWAP_V2_POOL_CATALOG_RELATIVE_PATH),
].filter(Boolean) as string[];
for (const candidate of candidates) {
if (!existsSync(candidate)) continue;
try {
const parsed = JSON.parse(readFileSync(candidate, 'utf8')) as LiveUniswapV2PoolCatalog;
return (parsed.pools ?? []).filter(
(pool) =>
Number.isFinite(pool.chainId) &&
pool.poolAddress?.startsWith('0x') &&
pool.baseAddress?.startsWith('0x') &&
pool.quoteAddress?.startsWith('0x')
);
} catch (error) {
logger.warn('Unable to parse live Uniswap V2 pool catalog; skipping file', { candidate, error });
}
}
return [];
}
function normalizeSupplyProofKey(chainId: number, address: string): string {
return `${chainId}:${address.toLowerCase()}`;
}
function loadSupplyProofFile(proofPath: string, source: string): SupplyProofSnapshot[] {
if (!proofPath || !existsSync(proofPath)) return [];
try {
const parsed = JSON.parse(readFileSync(proofPath, 'utf8')) as { proofs?: SupplyProofSnapshot[] } | SupplyProofSnapshot;
const proofs: SupplyProofSnapshot[] = Array.isArray((parsed as { proofs?: SupplyProofSnapshot[] })?.proofs)
? ((parsed as { proofs: SupplyProofSnapshot[] }).proofs)
: [parsed as SupplyProofSnapshot];
return proofs
.filter((proof) => proof?.network?.chainId && proof?.token?.address)
.map((proof) => ({
...proof,
_source: source,
_sourcePath: proofPath,
}));
} catch (error) {
logger.warn('Unable to parse supply proof file; skipping file', { proofPath, error });
return [];
}
}
function loadSupplyProofCatalog(): Map<string, SupplyProofSnapshot> {
const configuredCatalog = process.env.TOKEN_AGGREGATION_SUPPLY_PROOF_CATALOG_JSON?.trim();
const configuredSingleProof = process.env.CWUSDC_SUPPLY_PROOF_JSON?.trim();
const repoRoot = resolveRepoRoot();
const candidates = [
configuredCatalog ? { path: configuredCatalog, source: 'configured-supply-proof-catalog' } : null,
{ path: path.join(repoRoot, DEFAULT_SUPPLY_CATALOG_RELATIVE_PATH), source: 'repo-supply-proof-catalog' },
configuredSingleProof ? { path: configuredSingleProof, source: 'configured-supply-proof-file' } : null,
{ path: path.join(repoRoot, 'reports/status/mainnet-cwusdc-supply-proof-20260508.json'), source: 'repo-supply-proof-file' },
].filter(Boolean) as Array<{ path: string; source: string }>;
const byToken = new Map<string, SupplyProofSnapshot>();
for (const candidate of candidates) {
for (const proof of loadSupplyProofFile(candidate.path, candidate.source)) {
const key = normalizeSupplyProofKey(Number(proof.network.chainId), String(proof.token.address));
if (!byToken.has(key)) {
byToken.set(key, proof);
}
}
}
if (!byToken.has(normalizeSupplyProofKey(1, MAINNET_CWUSDC_ADDRESS))) {
byToken.set(normalizeSupplyProofKey(1, MAINNET_CWUSDC_ADDRESS), {
...CWUSDC_SUPPLY_PROOF_FALLBACK,
_source: 'embedded-fallback-snapshot',
});
}
return byToken;
}
function isGruSupplyTrackedCandidate(symbol: string, type?: string, registryFamily?: string): boolean {
return (
/^c[A-Z0-9]/.test(symbol) &&
(type === 'base' ||
type === 'c' ||
type === 'w' ||
['iso4217', 'monetary_unit', 'gas_native', 'commodity'].includes(String(registryFamily || '')))
);
}
const EXTERNAL_OFFICIAL_QUOTE_ASSETS = new Set([
'1:cUSDC:0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48',
'1:cUSDT:0xdac17f958d2ee523a2206206994597c13d831ec7',
'10:cUSDC:0x0b2c639c533813f4aa9d7837caf62653d097ff85',
'10:cUSDT:0x94b008aa00579c1307b0ef2c499ad98a8ce58e58',
'25:cUSDC:0xc21223249ca28397b4b6541dffaecc539bff0c59',
'25:cUSDT:0x66e428c3f67a68878562e79a0234c1f83c208770',
'56:cUSDC:0x8ac76a51cc950d9822d68b83fe1ad97b32cd580d',
'56:cUSDT:0x55d398326f99059ff775485246999027b3197955',
'100:cUSDC:0xddafbb505ad214d7b80b1f830fccc89b60fb7a83',
'100:cUSDT:0x4ecaba5870353805a9f068101a40e0f32ed605c6',
'137:cUSDC:0x3c499c542cef5e3811e1192ce70d8cc03d5c3359',
'137:cUSDT:0xc2132d05d31c914a87c6611c10748aeb04b58e8f',
'8453:cUSDC:0x833589fcd6edb6e08f4c7c32d4f71b54bda02913',
'8453:cUSDT:0xfde4c96c8593536e31f229ea8f37b2ada2699bb2',
'42161:cUSDC:0xaf88d065e77c8cc2239327c5edb3a432268e5831',
'42161:cUSDT:0xfd086bc7cd5c481dcc9c85ebe478a1c0b69fcbb9',
'42220:cUSDC:0xceba9300f2b948710d2653dd7b07f33a8b32118c',
'42220:cUSDT:0x48065fbbe25f71c9282ddf5e1cd6d6a887483d5e',
'43114:cUSDC:0xb97ef9ef8734c71904d8002f8b6bc66dd9c48a6e',
'43114:cUSDT:0x9702230a8ea53601f5cd2dc00fdbc13d4df4a8c7',
'1111:cUSDC:0xe3f5a90f9cb311505cd691a46596599aa1a0ad7d',
'1111:cUSDT:0xa649325aa7c5093d12d6f98eb4378deae68ce23f',
'651940:cUSDC:0xa95eed79f84e6a0151eaeb9d441f9ffd50e8e881',
'651940:cUSDT:0x015b1897ed5279930bc2be46f661894d219292a6',
]);
function hasExternalOfficialQuoteLiquidity(token: { chainId: number; symbol: string; address: string }): boolean {
return EXTERNAL_OFFICIAL_QUOTE_ASSETS.has(`${token.chainId}:${token.symbol}:${token.address.toLowerCase()}`);
}
function isDeterministicPlaceholderAddress(address: string): boolean {
const normalized = address.toLowerCase();
return /^0x[a-f0-9]{4}0{24,}[a-f0-9]{1,8}$/.test(normalized);
}
function buildSupplyProofEnrichment(
chainId: number,
address: string,
symbol: string,
type: string | undefined,
registryFamily: string | undefined,
priceUsd?: number
): SupplyProofEnrichment | undefined {
const proof = loadSupplyProofCatalog().get(normalizeSupplyProofKey(chainId, address));
if (!proof) {
if (!isGruSupplyTrackedCandidate(symbol, type, registryFamily)) return undefined;
if (isDeterministicPlaceholderAddress(address)) {
return {
supplyProofProvenance: {
source: 'deterministic-placeholder-address',
status: 'non_reportable_until_erc20_deployed',
},
trackerCaveats: [
'This token binding is a deterministic placeholder address and does not currently behave as an ERC-20 contract.',
'Do not submit totalSupply, circulatingSupply, marketCapUsd, or liquidity claims for this asset until a deployed ERC-20 binding replaces the placeholder.',
'The placeholder is kept visible for registry and routing roadmap traceability only.',
],
};
}
return {
supplyProofProvenance: {
source: 'missing-supply-proof',
status: 'proof_required',
},
trackerCaveats: [
'No token-specific on-chain supply proof artifact is currently attached to this report response.',
'Do not submit totalSupply, circulatingSupply, or marketCapUsd for this asset until a current proof artifact is generated and linked.',
'Registry and pool visibility are not the same as tracker-grade supply proof.',
],
};
}
const totalSupply = String(proof.token?.totalSupplyUnits ?? '');
const totalSupplyRaw = String(proof.token?.totalSupplyRaw ?? '');
const circulatingSupply = String(
proof.circulatingSupplyMethodology?.currentConservativeReportableCirculatingSupplyUnits ?? totalSupply
);
const parsedCirculatingSupply = Number(circulatingSupply);
const marketCapUsd =
priceUsd !== undefined && Number.isFinite(parsedCirculatingSupply) ? parsedCirculatingSupply * priceUsd : undefined;
return {
totalSupply,
totalSupplyRaw,
circulatingSupply,
circulatingSupplyFormula: String(
proof.circulatingSupplyMethodology?.recommendedFormula ??
'circulatingSupply = totalSupply - protocolControlledNonCirculatingBalances'
),
marketCapUsd,
supplyProofProvenance: {
source: proof._source ?? 'repo-supply-proof-file',
path: proof._sourcePath,
schema: proof.schema,
generatedAt: proof.generatedAt,
referenceBlock: proof.network?.referenceBlock,
status: proof.circulatingSupplyMethodology?.status,
},
trackerCaveats: proof.circulatingSupplyMethodology?.notes ?? [
'Tracker acceptance is external and not implied by this API response.',
],
};
}
function normalizePossiblyRawLiquidityUsd(
liquidityUsd: number | undefined,
decimals: number,
priceUsd: number | undefined,
totalSupplyUnits: string | undefined
): number | undefined {
if (liquidityUsd === undefined || !Number.isFinite(liquidityUsd) || liquidityUsd <= 0) return liquidityUsd;
if (priceUsd === undefined || !Number.isFinite(priceUsd) || priceUsd <= 0) return liquidityUsd;
if (!totalSupplyUnits) {
if (decimals === 6 && liquidityUsd >= 1_000_000_000_000 && priceUsd <= 10) {
return liquidityUsd / 10 ** decimals;
}
return liquidityUsd;
}
const supplyUnits = Number(totalSupplyUnits);
if (!Number.isFinite(supplyUnits) || supplyUnits <= 0) return liquidityUsd;
const normalizedSupplyValue = supplyUnits * priceUsd;
const divisor = 10 ** decimals;
const decimalAdjustedLiquidity = liquidityUsd / divisor;
if (
liquidityUsd > normalizedSupplyValue &&
decimalAdjustedLiquidity > 0 &&
decimalAdjustedLiquidity <= normalizedSupplyValue * 1.25
) {
return decimalAdjustedLiquidity;
}
return liquidityUsd;
}
function resolveGruV2ReserveRpcUrl(chainId: number): string {
const configured = resolvePmmQuoteRpcUrl();
if (chainId === 138 && process.env.NODE_ENV !== 'test') return configured || 'http://192.168.11.211:8545';
if (chainId === 56 && process.env.NODE_ENV !== 'test') {
return process.env.BSC_RPC_URL || process.env.BSC_MAINNET_RPC || process.env.RPC_URL_56 || 'https://bsc-rpc.publicnode.com';
}
if (chainId === 43114 && process.env.NODE_ENV !== 'test') {
return (
process.env.AVALANCHE_RPC_URL ||
process.env.AVALANCHE_MAINNET_RPC ||
process.env.RPC_URL_43114 ||
'https://avalanche-c-chain-rpc.publicnode.com'
);
}
if (chainId === 1111 && process.env.NODE_ENV !== 'test') {
return process.env.WEMIX_MAINNET_RPC || process.env.WEMIX_RPC || process.env.RPC_URL_1111 || 'https://api.wemix.com';
}
return '';
}
function decimalFromRawForReport(raw: bigint, decimals: number): number {
const scale = 10 ** decimals;
return Number(raw) / scale;
}
function fallbackPoolDecimals(symbol?: string): number {
const normalized = String(symbol || '').toUpperCase();
if (normalized === 'WETH' || normalized === 'ETH') return 18;
return 6;
}
function fallbackPoolUsdPrice(symbol?: string): number {
const normalized = String(symbol || '').toUpperCase();
if (normalized.includes('XAU')) return Number(process.env.TOKEN_AGGREGATION_XAU_USD || '0') || 0;
if (normalized === 'WETH' || normalized === 'ETH') return Number(process.env.TOKEN_AGGREGATION_ETH_USD || '0') || 0;
return 1;
}
async function enrichGruV2FallbackPoolWithReserves(chainId: number, pool: ReportPoolEntry): Promise<ReportPoolEntry> {
const rpcUrl = resolveGruV2ReserveRpcUrl(chainId);
if (!rpcUrl) return pool;
const reserves = await pmmVaultReserveFromChain({ rpcUrl, poolAddress: pool.poolAddress });
if (!reserves) return pool;
const baseUnits = decimalFromRawForReport(reserves.baseReserveRaw, fallbackPoolDecimals(pool.token0Symbol));
const quoteUnits = decimalFromRawForReport(reserves.quoteReserveRaw, fallbackPoolDecimals(pool.token1Symbol));
const reserveUsd =
baseUnits * fallbackPoolUsdPrice(pool.token0Symbol) + quoteUnits * fallbackPoolUsdPrice(pool.token1Symbol);
if (!Number.isFinite(reserveUsd) || reserveUsd <= 0) {
return {
...pool,
reserveSource: 'onchain-gru-v2-pmm-getVaultReserve',
reserveUpdatedAt: new Date().toISOString(),
reserve0Raw: reserves.baseReserveRaw.toString(),
reserve1Raw: reserves.quoteReserveRaw.toString(),
};
}
return {
...pool,
tvl: reserveUsd,
status: pool.status ?? 'reserve_visible',
statusReason:
pool.statusReason ??
'Pool is present in deployment-status and has positive on-chain reserves via getVaultReserve().',
reserveSource: 'onchain-gru-v2-pmm-getVaultReserve',
reserveUpdatedAt: new Date().toISOString(),
reserve0Raw: reserves.baseReserveRaw.toString(),
reserve1Raw: reserves.quoteReserveRaw.toString(),
};
}
async function buildGruV2FallbackPoolsForToken(
chainId: number,
tokenAddress: string,
existingPools: ReportPoolEntry[]
): Promise<ReportPoolEntry[]> {
const token = tokenAddress.toLowerCase();
const existing = new Set(existingPools.map((pool) => pool.poolAddress.toLowerCase()));
const pools = getGruV2DeploymentPoolRows()
.filter((pool) => pool.chainId === chainId)
.filter((pool) => pool.baseAddress.toLowerCase() === token || pool.quoteAddress.toLowerCase() === token)
.filter((pool) => !existing.has(pool.poolAddress.toLowerCase()))
.map((pool) => ({
poolAddress: pool.poolAddress,
dex: pool.venue ?? 'gru_v2_pmm',
token0: pool.baseAddress,
token1: pool.quoteAddress,
token0Symbol: pool.baseSymbol,
token1Symbol: pool.quoteSymbol,
tvl: 0,
volume24h: 0,
source: 'gru-v2-deployment-status',
status: pool.status,
statusReason: pool.statusReason,
role: pool.role,
section: pool.section,
publicRoutingEnabled: pool.publicRoutingEnabled,
}));
return Promise.all(pools.map((pool) => enrichGruV2FallbackPoolWithReserves(chainId, pool)));
}
function buildLiveUniswapV2FallbackPoolsForToken(
chainId: number,
tokenAddress: string,
existingPools: ReportPoolEntry[]
): ReportPoolEntry[] {
const token = tokenAddress.toLowerCase();
const existing = new Set(existingPools.map((pool) => pool.poolAddress.toLowerCase()));
return loadLiveUniswapV2PoolCatalog()
.filter((pool) => pool.chainId === chainId)
.filter((pool) => pool.baseAddress.toLowerCase() === token || pool.quoteAddress.toLowerCase() === token)
.filter((pool) => !existing.has(pool.poolAddress.toLowerCase()))
.map((pool) => ({
poolAddress: pool.poolAddress,
dex: pool.dex ?? 'uniswap_v2',
token0: pool.baseAddress,
token1: pool.quoteAddress,
token0Symbol: pool.baseSymbol,
token1Symbol: pool.quoteSymbol,
tvl: Number(pool.totalLiquidityUsd ?? 0),
volume24h: 0,
source: 'live-uniswap-v2-pair-discovery',
status: pool.healthy ? 'indexed_live_healthy' : 'indexed_live_needs_repair',
statusReason: pool.healthy
? 'Live Uniswap V2 pair discovered with on-chain reserves, depth, and parity checks passing.'
: `Live Uniswap V2 pair discovered; depthOk=${pool.depthOk === true}, parityOk=${pool.parityOk === true}, deviationBps=${pool.deviationBps ?? 'unknown'}.`,
role: 'public_indexable_liquidity',
publicRoutingEnabled: pool.healthy === true,
}));
}
function resolveLocalLogoUri(remoteLogoUri: string, symbol: string): string | undefined {
if (remoteLogoUri.startsWith('/api/v1/report/logo/')) {
return remoteLogoUri;
}
if (remoteLogoUri.includes('/token-lists/logos/gru/')) {
const fileName = remoteLogoUri.split('/').pop()?.replace(/\.svg$/i, '');
return fileName ? `/api/v1/report/logo/${fileName}` : undefined;
}
if (remoteLogoUri.includes('/blockchains/bitcoin/info/logo.png')) {
return '/api/v1/report/logo/cWBTC';
}
if (remoteLogoUri.includes('/ipfs/')) {
const cid = remoteLogoUri.split('/').pop();
return cid ? `/api/v1/report/logo/ipfs-${cid}` : undefined;
}
if (symbol === 'cWUSDC') {
return '/api/v1/report/logo/cUSDC';
}
return undefined;
}
function resolvePublicBaseUrl(req: Request): string {
const configured = (
process.env.TOKEN_AGGREGATION_PUBLIC_BASE_URL ??
process.env.PUBLIC_API_BASE_URL ??
process.env.PUBLIC_BASE_URL ??
''
).trim();
if (configured) return configured.replace(/\/+$/, '');
const host = String(req.get('x-forwarded-host') || req.get('host') || '').split(',')[0].trim();
if (host) {
let proto = String(req.get('x-forwarded-proto') || 'https').split(',')[0].trim() || 'https';
if (proto === 'http' && !/^(localhost|127\.0\.0\.1|\[::1\])(?::\d+)?$/i.test(host)) {
proto = 'https';
}
return `${proto}://${host}`.replace(/\/+$/, '');
}
return DEFAULT_PUBLIC_REPORT_BASE_URL;
}
function absolutePublicUrl(req: Request, value: string | undefined): string | undefined {
if (!value) return undefined;
if (/^https?:\/\//i.test(value)) return value;
if (!value.startsWith('/')) return value;
return `${resolvePublicBaseUrl(req)}${value}`;
}
function absoluteLogoUri(req: Request, remoteLogoUri: string, symbol: string): string {
const localLogoURI = resolveLocalLogoUri(remoteLogoUri, symbol);
return absolutePublicUrl(req, localLogoURI) ?? remoteLogoUri;
}
function absoluteReportLogoUri(remoteLogoUri: string, symbol: string): string {
const localLogoURI = resolveLocalLogoUri(remoteLogoUri, symbol);
if (localLogoURI?.startsWith('/')) return `${DEFAULT_PUBLIC_REPORT_BASE_URL}${localLogoURI}`;
return localLogoURI ?? remoteLogoUri;
}
/** Build token entries with DB market/pool data for a chain */
async function buildTokenReport(chainId: number) {
const canonical = getCanonicalTokensByChain(chainId);
const out: Array<{
chainId: number;
address: string;
symbol: string;
name: string;
type: string;
decimals: number;
currencyCode?: string;
registryFamily?: string;
familySymbol?: string;
deploymentVersion?: string;
deploymentStatus?: string;
preferredForX402?: boolean;
liquiditySourceSymbol?: string;
logoURI?: string;
originalLogoURI?: string;
market?: {
priceUsd?: number;
volume24h: number;
volume7d: number;
volume30d: number;
marketCapUsd?: number;
liquidityUsd: number;
lastUpdated: string;
};
totalSupply?: string;
totalSupplyRaw?: string;
circulatingSupply?: string;
circulatingSupplyFormula?: string;
supplyProofProvenance?: SupplyProofEnrichment['supplyProofProvenance'];
trackerCaveats?: string[];
pools: ReportPoolEntry[];
fromDb: boolean;
}> = [];
for (const spec of canonical) {
const address = spec.addresses[chainId];
if (!address || String(address).trim() === '') continue;
const [dbToken, marketData, pools] = await Promise.all([
tokenRepo.getToken(chainId, address),
marketDataRepo.getMarketData(chainId, address),
poolRepo.getPoolsByToken(chainId, address),
]);
const exposedPools = filterPoolsForExposure(chainId, pools);
const resolvedPools = await Promise.all(
exposedPools.map(async (p) => {
const { token0, token1 } = await resolvePoolTokenDisplays(tokenRepo, chainId, p.token0Address, p.token1Address);
return {
poolAddress: p.poolAddress,
dex: p.dexType,
token0,
token1,
tvl: p.totalLiquidityUsd,
volume24h: p.volume24h,
};
})
);
const fallbackPriceUsd = getCanonicalPriceUsd(chainId, address);
const market = marketData
? {
priceUsd: marketData.priceUsd ?? fallbackPriceUsd,
volume24h: marketData.volume24h,
volume7d: marketData.volume7d,
volume30d: marketData.volume30d,
marketCapUsd: marketData.marketCapUsd,
liquidityUsd: marketData.liquidityUsd,
lastUpdated: marketData.lastUpdated?.toISOString() ?? '',
}
: fallbackPriceUsd !== undefined
? {
priceUsd: fallbackPriceUsd,
volume24h: 0,
volume7d: 0,
volume30d: 0,
liquidityUsd: 0,
lastUpdated: `${getCanonicalPriceSnapshotGeneratedAt()}T00:00:00.000Z`,
}
: undefined;
const registryFamily = getTokenRegistryFamily(spec);
const originalLogoURI = getLogoUriForSpec(spec);
const logoURI = absoluteReportLogoUri(originalLogoURI, spec.symbol);
const supplyProof = buildSupplyProofEnrichment(chainId, address, spec.symbol, spec.type, registryFamily, market?.priceUsd);
if (supplyProof?.marketCapUsd !== undefined && market) {
market.marketCapUsd = supplyProof.marketCapUsd;
}
if (market) {
market.liquidityUsd =
normalizePossiblyRawLiquidityUsd(market.liquidityUsd, spec.decimals, market.priceUsd, supplyProof?.totalSupply) ??
market.liquidityUsd;
}
const dbPoolEntries: ReportPoolEntry[] = resolvedPools.map((p) => ({
poolAddress: p.poolAddress,
dex: p.dex,
token0: p.token0.address,
token1: p.token1.address,
token0Symbol: p.token0.symbol,
token1Symbol: p.token1.symbol,
tvl: p.tvl,
volume24h: p.volume24h,
source: 'indexed-db',
status: 'indexed',
statusReason: 'Pool is present in the token-aggregation indexed pool repository.',
}));
const liveUniswapV2PoolEntries = buildLiveUniswapV2FallbackPoolsForToken(chainId, address, dbPoolEntries);
const gruV2FallbackPoolEntries = await buildGruV2FallbackPoolsForToken(chainId, address, [
...dbPoolEntries,
...liveUniswapV2PoolEntries,
]);
const reportPools = [
...dbPoolEntries,
...liveUniswapV2PoolEntries,
...gruV2FallbackPoolEntries,
];
out.push({
chainId,
address: address.toLowerCase(),
symbol: spec.symbol,
name: dbToken?.name ?? spec.name,
type: spec.type,
decimals: spec.decimals,
currencyCode: spec.currencyCode,
registryFamily,
familySymbol: spec.familySymbol,
deploymentVersion: spec.deploymentVersion,
deploymentStatus: spec.deploymentStatus,
preferredForX402: spec.preferredForX402,
liquiditySourceSymbol: spec.liquiditySourceSymbol,
logoURI,
originalLogoURI,
market,
totalSupply: supplyProof?.totalSupply,
totalSupplyRaw: supplyProof?.totalSupplyRaw,
circulatingSupply: supplyProof?.circulatingSupply,
circulatingSupplyFormula: supplyProof?.circulatingSupplyFormula,
supplyProofProvenance: supplyProof?.supplyProofProvenance,
trackerCaveats: supplyProof?.trackerCaveats,
pools: reportPools,
fromDb: !!dbToken,
});
}
return out;
}
function describeToken(spec: { currencyCode?: string; registryFamily?: string }): string | undefined {
const family = String(spec.registryFamily || '').trim();
const code = String(spec.currencyCode || '').trim().toUpperCase();
if (!code) return undefined;
if (family === 'gas_native') {
return `Governance-approved gas-native ${code} compliant token`;
}
if (family === 'monetary_unit') {
return `GRU monetary-unit ${code} compliant token`;
}
if (family === 'commodity') {
return `Governance-approved commodity ${code} compliant token`;
}
return `ISO-4217 ${code} compliant token`;
}
function buildGruTransportOverview() {
const gruTransportMetadata = getGruTransportMetadata();
const deploymentStatus = loadDeploymentStatusFile()?.data;
if (!gruTransportMetadata && !deploymentStatus) return undefined;
const activeTransportPairs = getActiveTransportPairs();
const fallbackGasAssetFamilies = deploymentStatus ? buildGasAssetFamiliesFromDeploymentStatus(deploymentStatus) : [];
const fallbackRuntimePairs = deploymentStatus ? buildGasRuntimePairsFromDeploymentStatus(deploymentStatus) : [];
const gasAssetFamilies = gruTransportMetadata?.gasAssetFamilies ?? fallbackGasAssetFamilies;
const gasRedeemGroups = gruTransportMetadata?.gasRedeemGroups ?? [];
const gasProtocolExposure = gruTransportMetadata?.gasProtocolExposure ?? [];
const runtimePairs = activeTransportPairs.length > 0 ? activeTransportPairs : fallbackRuntimePairs;
return {
system: gruTransportMetadata?.system ?? {
name: 'GRU Monetary Transport Layer',
shortName: 'GRU Transport',
canonicalChainId: 138,
canonicalChainName: 'DeFi Oracle Meta Chain 138',
transportClass: 'deployment-status-fallback',
},
summary: gruTransportMetadata?.counts ?? {
enabledCanonicalTokens: 0,
enabledDestinationChains: new Set(runtimePairs.map((pair) => pair.destinationChainId)).size,
approvedBridgePeers: 0,
transportPairs: runtimePairs.length,
gasAssetFamilies: gasAssetFamilies.length,
gasRedeemGroups: gasRedeemGroups.length,
gasProtocolExposure: gasProtocolExposure.length,
gasTransportPairs: runtimePairs.length,
runtimeReadyTransportPairs: runtimePairs.filter((pair) => pair.runtimeReady === true).length,
publicPools: 0,
},
gasAssetFamilies,
gasRedeemGroups,
gasProtocolExposure,
activeTransportPairs: runtimePairs.map((pair) => ({
key: pair.key,
canonicalSymbol: pair.canonicalSymbol,
mirroredSymbol: pair.mirroredSymbol,
destinationChainId: pair.destinationChainId,
destinationChainName: pair.destinationChainName ?? null,
assetClass: pair.assetClass,
familyKey: pair.familyKey,
backingMode: pair.backingMode,
redeemPolicy: pair.redeemPolicy,
wrappedNativeQuoteSymbol: pair.wrappedNativeQuoteSymbol ?? null,
stableQuoteSymbol: pair.stableQuoteSymbol ?? null,
eligible: pair.eligible === true,
runtimeReady: pair.runtimeReady === true,
supplyInvariantSatisfied: pair.supplyInvariantSatisfied ?? null,
eligibilityBlockers: Array.isArray(pair.eligibilityBlockers)
? pair.eligibilityBlockers
: [],
runtimeMissingRequirements: Array.isArray(pair.runtimeMissingRequirements)
? pair.runtimeMissingRequirements
: [],
})),
};
}
function gasFamilyDefaults(familyKey: string, mirroredSymbol: string) {
const byFamily: Record<string, { canonicalSymbol138: string; assetClass: string; backingMode: string; redeemPolicy: string; laneGroup: string }> = {
eth_mainnet: {
canonicalSymbol138: 'cETH',
assetClass: 'gas_native',
backingMode: 'strict_escrow',
redeemPolicy: 'same_family_only',
laneGroup: 'eth_mainnet',
},
eth_l2: {
canonicalSymbol138: 'cETHL2',
assetClass: 'gas_native',
backingMode: 'hybrid_cap',
redeemPolicy: 'same_family_only',
laneGroup: 'eth_l2',
},
bnb: {
canonicalSymbol138: 'cBNB',
assetClass: 'gas_native',
backingMode: 'strict_escrow',
redeemPolicy: 'same_family_only',
laneGroup: 'bnb',
},
pol: {
canonicalSymbol138: 'cPOL',
assetClass: 'gas_native',
backingMode: 'strict_escrow',
redeemPolicy: 'same_family_only',
laneGroup: 'pol',
},
avax: {
canonicalSymbol138: 'cAVAX',
assetClass: 'gas_native',
backingMode: 'strict_escrow',
redeemPolicy: 'same_family_only',
laneGroup: 'avax',
},
cro: {
canonicalSymbol138: 'cCRO',
assetClass: 'gas_native',
backingMode: 'strict_escrow',
redeemPolicy: 'same_family_only',
laneGroup: 'cro',
},
xdai: {
canonicalSymbol138: 'cXDAI',
assetClass: 'gas_native',
backingMode: 'strict_escrow',
redeemPolicy: 'same_family_only',
laneGroup: 'xdai',
},
celo: {
canonicalSymbol138: 'cCELO',
assetClass: 'gas_native',
backingMode: 'strict_escrow',
redeemPolicy: 'same_family_only',
laneGroup: 'celo',
},
wemix: {
canonicalSymbol138: 'cWEMIX',
assetClass: 'gas_native',
backingMode: 'strict_escrow',
redeemPolicy: 'same_family_only',
laneGroup: 'wemix',
},
};
return byFamily[familyKey] ?? {
canonicalSymbol138: mirroredSymbol.replace(/^cW/, 'c'),
assetClass: 'gas_native',
backingMode: 'strict_escrow',
redeemPolicy: 'same_family_only',
laneGroup: familyKey,
};
}
function buildGasAssetFamiliesFromDeploymentStatus(data: DeploymentStatusFile): GruTransportGasAssetFamily[] {
const byFamily = new Map<string, GruTransportGasAssetFamily>();
for (const [chainIdText, chain] of Object.entries(data.chains ?? {})) {
const chainId = Number(chainIdText);
for (const pool of chain.gasPmmPools ?? []) {
const familyKey = typeof pool.familyKey === 'string' ? pool.familyKey : '';
const mirroredSymbol = typeof pool.base === 'string' ? pool.base : '';
if (!familyKey || !mirroredSymbol) continue;
const defaults = gasFamilyDefaults(familyKey, mirroredSymbol);
const existing = byFamily.get(familyKey) ?? {
familyKey,
active: true,
status: 'deployment_status_fallback',
canonicalSymbol138: defaults.canonicalSymbol138,
mirroredSymbol,
assetClass: defaults.assetClass,
originChains: [],
laneGroup: defaults.laneGroup,
backingMode: defaults.backingMode,
redeemPolicy: defaults.redeemPolicy,
wrappedNativeQuoteSymbol: 'WETH',
stableQuoteSymbol: 'USDC',
referenceVenue: 'deployment-status',
};
if (!existing.originChains.includes(chainId)) {
existing.originChains.push(chainId);
}
if (typeof pool.quote === 'string' && ['WETH', 'WBNB', 'WPOL', 'WAVAX', 'WCRO', 'WXDAI', 'CELO', 'WEMIX'].includes(pool.quote)) {
existing.wrappedNativeQuoteSymbol = pool.quote;
}
if (typeof pool.quote === 'string' && ['USDC', 'USDT', 'DAI'].includes(pool.quote)) {
existing.stableQuoteSymbol = pool.quote;
}
byFamily.set(familyKey, existing);
}
}
return Array.from(byFamily.values()).sort((a, b) => a.familyKey.localeCompare(b.familyKey));
}
function buildGasRuntimePairsFromDeploymentStatus(data: DeploymentStatusFile): GruTransportPair[] {
const familiesByKey = new Map(buildGasAssetFamiliesFromDeploymentStatus(data).map((family) => [family.familyKey, family]));
const pairs: GruTransportPair[] = [];
for (const [chainIdText, chain] of Object.entries(data.chains ?? {})) {
const destinationChainId = Number(chainIdText);
const seen = new Set<string>();
for (const pool of chain.gasPmmPools ?? []) {
const familyKey = typeof pool.familyKey === 'string' ? pool.familyKey : '';
const mirroredSymbol = typeof pool.base === 'string' ? pool.base : '';
if (!familyKey || !mirroredSymbol || seen.has(familyKey)) continue;
seen.add(familyKey);
const family = familiesByKey.get(familyKey);
const canonicalSymbol = family?.canonicalSymbol138 ?? gasFamilyDefaults(familyKey, mirroredSymbol).canonicalSymbol138;
pairs.push({
key: `138-${destinationChainId}-${canonicalSymbol}-${mirroredSymbol}`,
canonicalChainId: 138,
destinationChainId,
destinationChainName: chain.name ?? `Chain ${chainIdText}`,
active: true,
status: 'deployment_status_fallback',
canonicalSymbol,
mirroredSymbol,
mappingKey: `${canonicalSymbol}:${destinationChainId}`,
peerKey: `chain-${destinationChainId}`,
assetClass: 'gas_native',
familyKey,
laneGroup: family?.laneGroup,
backingMode: family?.backingMode,
redeemPolicy: family?.redeemPolicy,
wrappedNativeQuoteSymbol: family?.wrappedNativeQuoteSymbol,
stableQuoteSymbol: family?.stableQuoteSymbol,
mirrorDeploymentAddress: chain.gasMirrors?.[mirroredSymbol],
mirrorDeployed: !!chain.gasMirrors?.[mirroredSymbol],
canonicalEnabled: true,
destinationEnabled: true,
bridgeAvailable: chain.bridgeAvailable === true,
bridgePeerConfigured: chain.bridgeAvailable === true,
runtimeBridgeReady: chain.bridgeAvailable === true,
runtimeReady: chain.bridgeAvailable === true && !!chain.gasMirrors?.[mirroredSymbol],
eligible: true,
runtimeMissingRequirements: chain.bridgeAvailable === true ? [] : ['bridgeAvailable is not true in deployment-status'],
eligibilityBlockers: [],
});
}
}
return pairs.sort((a, b) => a.destinationChainId - b.destinationChainId || a.familyKey!.localeCompare(b.familyKey!));
}
function buildCanonicalCwFallback(chainIdFilter?: number | null): CwRegistryChain[] {
const grouped = new Map<number, CwRegistryChain>();
for (const spec of CANONICAL_TOKENS) {
if (spec.type !== 'w') continue;
for (const [chainIdText, address] of Object.entries(spec.addresses)) {
const chainId = Number(chainIdText);
if (!address || Number.isNaN(chainId)) continue;
if (chainIdFilter && chainId !== chainIdFilter) continue;
const existing = grouped.get(chainId) ?? {
chainId,
chainIdText,
name: `Chain ${chainIdText}`,
tokens: [],
};
existing.tokens.push({ symbol: spec.symbol, address });
grouped.set(chainId, existing);
}
}
return Array.from(grouped.values())
.map((row) => ({
...row,
tokens: row.tokens.sort((a, b) => a.symbol.localeCompare(b.symbol)),
}))
.sort((a, b) => a.chainId - b.chainId);
}
/** GET /report/assets/* — packaged report assets such as controlled token logos. */
router.get(/^\/assets\/(.+)$/, (req: Request, res: Response) => {
const assetPath = String(req.params[0] ?? '');
const publicRoot = path.resolve(__dirname, '../../../public');
const resolved = path.resolve(publicRoot, assetPath);
if (!resolved.startsWith(publicRoot) || !existsSync(resolved)) {
res.status(404).json({ error: 'Asset not found' });
return;
}
res.set('Cache-Control', 'public, max-age=86400, immutable');
res.sendFile(resolved);
});
/** GET /report/logo/:symbol — extensionless public logo route for proxies that block static file extensions. */
router.get('/logo/:symbol', (req: Request, res: Response) => {
const rawSymbol = String(req.params.symbol ?? '').trim();
const publicRoot = path.resolve(__dirname, '../../../public/token-logos');
const gruSymbol = rawSymbol.replace(/^cW(?=USD|EUR|GBP|AUD|JPY|CHF|CAD|XAU)/, 'c');
const candidates = [
rawSymbol === 'cWBTC' ? path.join(publicRoot, 'trustwallet/btc-logo.png') : '',
rawSymbol.startsWith('ipfs-') ? path.join(publicRoot, 'ipfs', rawSymbol.replace(/^ipfs-/, '')) : '',
path.join(publicRoot, 'gru', `${gruSymbol}.svg`),
].filter(Boolean);
const resolved = candidates.find((candidate) => existsSync(candidate));
if (!resolved) {
res.status(404).json({ error: 'Logo not found' });
return;
}
res.set('Cache-Control', 'public, max-age=86400, immutable');
res.sendFile(resolved);
});
/** GET /report/cross-chain — cross-chain pools, bridge volume, atomic swaps (Chain 138, ALL Mainnet) */
router.get(
'/cross-chain',
cacheMiddleware(2 * 60 * 1000),
async (req: Request, res: Response) => {
try {
const chainId = parseInt(req.query.chainId as string, 10) || 138;
const report = await buildCrossChainReport(chainId);
res.json({
...report,
format: 'cross-chain-report',
documentation: 'Use for CMC/CoinGecko submission alongside single-chain reports. Includes CCIP, Alltra, Trustless bridge events and volume by lane.',
});
} catch (error) {
logger.error('Error building report/cross-chain:', error);
res.status(500).json({
error: 'Internal server error',
crossChainPools: [],
volumeByLane: [],
atomicSwapVolume24h: 0,
bridgeVolume24hTotal: 0,
events: [],
});
}
}
);
/** GET /report/all — all tokens, pools, liquidity, volume (unified) + cross-chain */
router.get(
'/all',
cacheMiddleware(2 * 60 * 1000),
async (req: Request, res: Response) => {
try {
const chainIdParam = req.query.chainId as string | undefined;
const chainIds = chainIdParam
? [parseInt(chainIdParam, 10)].filter((n) => !isNaN(n))
: getSupportedChainIds();
const tokensByChain: Record<number, Awaited<ReturnType<typeof buildTokenReport>>> = {};
const poolsByChain: Record<number, Awaited<ReturnType<typeof poolRepo.getPoolsByChain>>> = {};
for (const chainId of chainIds) {
tokensByChain[chainId] = await buildTokenReport(chainId);
poolsByChain[chainId] = filterPoolsForExposure(chainId, await poolRepo.getPoolsByChain(chainId));
}
const crossChainReport = await buildCrossChainReport(138).catch(() => null);
const gruTransport = buildGruTransportOverview();
const totalLiquidityByChain: Record<number, number> = {};
const totalVolume24hByChain: Record<number, number> = {};
for (const chainId of chainIds) {
const pools = poolsByChain[chainId] || [];
totalLiquidityByChain[chainId] = pools.reduce((s, p) => s + (p.totalLiquidityUsd || 0), 0);
totalVolume24hByChain[chainId] = pools.reduce((s, p) => s + (p.volume24h || 0), 0);
}
res.json({
generatedAt: new Date().toISOString(),
chains: chainIds,
tokens: tokensByChain,
pools: poolsByChain,
summary: {
totalLiquidityUsdByChain: totalLiquidityByChain,
totalVolume24hUsdByChain: totalVolume24hByChain,
tokenCountByChain: Object.fromEntries(
chainIds.map((c) => [c, (tokensByChain[c] || []).length])
),
poolCountByChain: Object.fromEntries(
chainIds.map((c) => [c, (poolsByChain[c] || []).length])
),
crossChainBridgeVolume24h: crossChainReport?.bridgeVolume24hTotal,
crossChainAtomicSwapVolume24h: crossChainReport?.atomicSwapVolume24h,
},
crossChain: crossChainReport
? {
crossChainPools: crossChainReport.crossChainPools,
volumeByLane: crossChainReport.volumeByLane,
atomicSwapVolume24h: crossChainReport.atomicSwapVolume24h,
bridgeVolume24hTotal: crossChainReport.bridgeVolume24hTotal,
}
: undefined,
gruTransport,
});
} catch (error) {
logger.error('Error building report/all:', error);
res.status(500).json({ error: 'Internal server error' });
}
}
);
/** GET /report/coingecko — format suitable for CoinGecko submission / API */
router.get(
'/coingecko',
cacheMiddleware(2 * 60 * 1000),
async (req: Request, res: Response) => {
try {
const chainId = parseInt(req.query.chainId as string, 10) || 138;
const tokens = await buildTokenReport(chainId);
const coingeckoFormat = tokens.map((t) => ({
chain_id: chainId,
contract_address: t.address,
id: `${t.symbol.toLowerCase()}-${chainId}`,
symbol: t.symbol,
name: t.name,
asset_platform_id: chainId === 138 ? 'defi-oracle-meta' : chainId === 651940 ? 'all-mainnet' : `chain-${chainId}`,
decimals: t.decimals,
logo_uri: t.logoURI,
original_logo_uri: t.originalLogoURI,
description: describeToken(t),
total_supply: t.totalSupply ? Number(t.totalSupply) : undefined,
total_supply_raw: t.totalSupplyRaw,
circulating_supply: t.circulatingSupply ? Number(t.circulatingSupply) : undefined,
circulating_supply_formula: t.circulatingSupplyFormula,
supply_proof_provenance: t.supplyProofProvenance,
tracker_caveats: t.trackerCaveats,
market_data: t.market
? {
current_price: { usd: t.market.priceUsd },
total_volume: t.market.volume24h,
market_cap: t.market.marketCapUsd,
liquidity_usd: t.market.liquidityUsd,
last_updated: t.market.lastUpdated,
}
: undefined,
liquidity_pools: t.pools.map((p) => ({
pool_address: p.poolAddress,
dex_id: p.dex,
tvl_usd: p.tvl,
volume_24h_usd: p.volume24h,
source: p.source,
status: p.status,
status_reason: p.statusReason,
role: p.role,
section: p.section,
public_routing_enabled: p.publicRoutingEnabled,
base_symbol: p.token0Symbol,
quote_symbol: p.token1Symbol,
})),
}));
const crossChainReport = await buildCrossChainReport(chainId).catch(() => null);
res.json({
generatedAt: new Date().toISOString(),
chainId,
format: 'coingecko-submission',
tokens: coingeckoFormat,
crossChain: crossChainReport
? {
crossChainPools: crossChainReport.crossChainPools,
volumeByLane: crossChainReport.volumeByLane,
atomicSwapVolume24h: crossChainReport.atomicSwapVolume24h,
bridgeVolume24hTotal: crossChainReport.bridgeVolume24hTotal,
}
: undefined,
documentation: 'https://www.coingecko.com/en/api/documentation',
});
} catch (error) {
logger.error('Error building report/coingecko:', error);
res.status(500).json({ error: 'Internal server error' });
}
}
);
/** GET /report/cmc — format suitable for CoinMarketCap submission / API */
router.get(
'/cmc',
cacheMiddleware(2 * 60 * 1000),
async (req: Request, res: Response) => {
try {
const chainId = parseInt(req.query.chainId as string, 10) || 138;
const tokens = await buildTokenReport(chainId);
const cmcFormat = tokens.map((t) => ({
chain_id: chainId,
contract_address: t.address,
symbol: t.symbol,
name: t.name,
decimals: t.decimals,
logo_url: t.logoURI,
original_logo_url: t.originalLogoURI,
total_supply: t.totalSupply ? Number(t.totalSupply) : undefined,
total_supply_raw: t.totalSupplyRaw,
circulating_supply: t.circulatingSupply ? Number(t.circulatingSupply) : undefined,
circulating_supply_formula: t.circulatingSupplyFormula,
supply_proof_provenance: t.supplyProofProvenance,
tracker_caveats: t.trackerCaveats,
volume_24h: t.market?.volume24h,
market_cap: t.market?.marketCapUsd,
liquidity_usd: t.market?.liquidityUsd ?? t.pools.reduce((s, p) => s + p.tvl, 0),
pairs: t.pools.map((p) => ({
pair_address: p.poolAddress,
dex_id: p.dex,
base: t.address,
quote: p.token0 === t.address ? p.token1 : p.token0,
liquidity_usd: p.tvl,
volume_24h_usd: p.volume24h,
source: p.source,
status: p.status,
status_reason: p.statusReason,
role: p.role,
section: p.section,
public_routing_enabled: p.publicRoutingEnabled,
})),
}));
const crossChainReport = await buildCrossChainReport(chainId).catch(() => null);
res.json({
generatedAt: new Date().toISOString(),
chainId,
format: 'coinmarketcap-dex',
tokens: cmcFormat,
crossChain: crossChainReport
? {
crossChainPools: crossChainReport.crossChainPools,
volumeByLane: crossChainReport.volumeByLane,
atomicSwapVolume24h: crossChainReport.atomicSwapVolume24h,
bridgeVolume24hTotal: crossChainReport.bridgeVolume24hTotal,
}
: undefined,
documentation: 'https://coinmarketcap.com/api/documentation',
});
} catch (error) {
logger.error('Error building report/cmc:', error);
res.status(500).json({ error: 'Internal server error' });
}
}
);
/** GET /report/token-price/:symbol — compact reviewer-facing price and evidence snapshot. */
router.get(
'/token-price/:symbol',
cacheMiddleware(60 * 1000),
async (req: Request, res: Response) => {
try {
const symbol = String(req.params.symbol || '').trim();
const chainId = parseInt(req.query.chainId as string, 10) || 1;
const tokens = await buildTokenReport(chainId);
const token = tokens.find((entry) => entry.symbol.toLowerCase() === symbol.toLowerCase());
if (!token) {
res.status(404).json({
error: 'Token not found',
symbol,
chainId,
});
return;
}
const poolLiquidityUsd = token.pools.reduce((sum, pool) => sum + (pool.tvl || 0), 0);
const marketLiquidityUsd = token.market?.liquidityUsd ?? 0;
const liquidityUsd = marketLiquidityUsd > 0 ? marketLiquidityUsd : poolLiquidityUsd;
const priceUsd = token.market?.priceUsd;
const circulatingSupply = token.circulatingSupply ? Number(token.circulatingSupply) : undefined;
const totalSupply = token.totalSupply ? Number(token.totalSupply) : undefined;
const marketCapUsd =
token.market?.marketCapUsd ??
(priceUsd !== undefined && circulatingSupply !== undefined ? priceUsd * circulatingSupply : undefined);
res.json({
generatedAt: new Date().toISOString(),
schema: 'dbis-token-price-evidence/v1',
chainId,
token: {
address: token.address,
symbol: token.symbol,
name: token.name,
decimals: token.decimals,
type: token.type,
registryFamily: token.registryFamily,
logoURI: token.logoURI,
},
price: {
usd: priceUsd,
source: token.market ? 'token-aggregation' : priceUsd !== undefined ? 'canonical-fallback' : 'unavailable',
lastUpdated: token.market?.lastUpdated,
caveat:
chainId === 1 && token.symbol === 'cWUSDC'
? 'This is DBIS tracker-submission evidence. Etherscan USD Value appears only after Etherscan/CoinGecko/Dex indexers accept a public price source.'
: undefined,
},
supply: {
totalSupply,
totalSupplyRaw: token.totalSupplyRaw,
circulatingSupply,
circulatingSupplyFormula: token.circulatingSupplyFormula,
proof: token.supplyProofProvenance,
caveats: token.trackerCaveats ?? [],
},
valuation: {
marketCapUsd,
liquidityUsd,
volume24hUsd: token.market?.volume24h ?? 0,
},
pools: token.pools.map((pool) => ({
poolAddress: pool.poolAddress,
dexId: pool.dex,
tvlUsd: pool.tvl,
volume24hUsd: pool.volume24h,
source: pool.source,
status: pool.status,
statusReason: pool.statusReason,
role: pool.role,
publicRoutingEnabled: pool.publicRoutingEnabled,
token0: {
address: pool.token0,
symbol: pool.token0Symbol,
},
token1: {
address: pool.token1,
symbol: pool.token1Symbol,
},
})),
submissionLinks: {
coingeckoReport: `${resolvePublicBaseUrl(req)}/api/v1/report/coingecko?chainId=${chainId}`,
cmcReport: `${resolvePublicBaseUrl(req)}/api/v1/report/cmc?chainId=${chainId}`,
tokenList: `${resolvePublicBaseUrl(req)}/api/v1/report/token-list?chainId=${chainId}`,
etherscan:
chainId === 1
? `https://etherscan.io/token/${token.address}`
: undefined,
},
});
} catch (error) {
logger.error('Error building report/token-price:', error);
res.status(500).json({ error: 'Internal server error' });
}
}
);
/** GET /report/token-list — flat list of all canonical tokens (Uniswap token list format with logoURI).
* If TOKEN_LIST_JSON_URL is set (e.g. GitHub raw URL), fetches and returns that JSON; optional ?chainId= filters tokens.
*/
router.get(
'/token-list',
cacheMiddleware(5 * 60 * 1000),
async (req: Request, res: Response) => {
try {
const tokenListUrl = process.env.TOKEN_LIST_JSON_URL?.trim();
if (tokenListUrl) {
try {
const data = (await fetchRemoteJson(tokenListUrl)) as {
name?: string;
version?: string;
timestamp?: string;
logoURI?: string;
tokens?: Array<{ chainId?: number; address?: string; symbol?: string; name?: string; decimals?: number; [key: string]: unknown }>;
};
const chainIdParam = req.query.chainId as string | undefined;
const chainIdFilter = chainIdParam ? parseInt(chainIdParam, 10) : null;
let tokens = Array.isArray(data.tokens) ? data.tokens : [];
if (!isNaN(chainIdFilter as number)) {
tokens = tokens.filter((t) => t.chainId === chainIdFilter);
}
const normalizedTokens = tokens.map((token) => ({
...token,
logoURI: absolutePublicUrl(req, typeof token.logoURI === 'string' ? token.logoURI : undefined) ?? token.logoURI,
}));
return res.json({
name: data.name ?? 'Token List',
version: data.version ?? '1.0.0',
timestamp: data.timestamp ?? new Date().toISOString(),
logoURI: absolutePublicUrl(req, data.logoURI) ?? data.logoURI,
tokens: normalizedTokens,
});
} catch (err) {
logger.error('TOKEN_LIST_JSON_URL fetch failed, using built-in token list:', err);
}
}
const chainIdParam = req.query.chainId as string | undefined;
const chainIds = chainIdParam
? [parseInt(chainIdParam, 10)].filter((n) => !isNaN(n))
: getSupportedChainIds();
const list: Array<{
chainId: number;
address: string;
symbol: string;
name: string;
decimals: number;
type: string;
logoURI: string;
originalLogoURI?: string;
registryFamily?: string;
familySymbol?: string;
deploymentVersion?: string;
deploymentStatus?: string;
preferredForX402?: boolean;
}> = [];
for (const chainId of chainIds) {
const specs = getCanonicalTokensByChain(chainId);
for (const spec of specs) {
const address = spec.addresses[chainId];
if (address) {
const originalLogoURI = getLogoUriForSpec(spec);
list.push({
chainId,
address: address.toLowerCase(),
symbol: spec.symbol,
name: spec.name,
decimals: spec.decimals,
type: spec.type,
logoURI: absoluteLogoUri(req, originalLogoURI, spec.symbol),
originalLogoURI,
registryFamily: getTokenRegistryFamily(spec),
familySymbol: spec.familySymbol,
deploymentVersion: spec.deploymentVersion,
deploymentStatus: spec.deploymentStatus,
preferredForX402: spec.preferredForX402,
});
}
}
}
res.json({
name: 'GRU Canonical Token List',
version: '1.0.0',
timestamp: new Date().toISOString(),
logoURI: absolutePublicUrl(req, DBIS_CHAIN_138_LOGO_PATH),
tokens: list,
});
} catch (error) {
logger.error('Error building report/token-list:', error);
res.status(500).json({ error: 'Internal server error' });
}
}
);
/** GET /report/cw-registry — live cW* registry from deployment-status.json when available. */
router.get('/cw-registry', async (req: Request, res: Response) => {
try {
const chainIdParam = req.query.chainId as string | undefined;
const chainIdFilter = chainIdParam ? parseInt(chainIdParam, 10) : null;
const fileBackedRegistry = loadDeploymentStatusFile();
let chains = fileBackedRegistry
? buildCwRegistryChains(fileBackedRegistry.data)
: buildCanonicalCwFallback(chainIdFilter);
if (chainIdFilter && !Number.isNaN(chainIdFilter)) {
chains = chains.filter((row) => row.chainId === chainIdFilter);
}
res.set('Cache-Control', 'public, max-age=0, must-revalidate');
res.json({
generatedAt: new Date().toISOString(),
source: fileBackedRegistry ? 'deployment-status-file' : 'canonical-fallback',
complete: !!fileBackedRegistry,
version: fileBackedRegistry?.data.version,
updated: fileBackedRegistry?.data.updated,
lastModified: fileBackedRegistry?.lastModified,
chains,
});
} catch (error) {
logger.error('Error building report/cw-registry:', error);
res.status(500).json({ error: 'Internal server error', chains: [] });
}
});
/** GET /report/gru-v2-pmm-pools — all GRU v2 PMM pools from deployment-status (stable, volatile, gas) with resolved token addresses. */
router.get('/gru-v2-pmm-pools', async (req: Request, res: Response) => {
try {
const chainIdParam = req.query.chainId as string | undefined;
const chainIdFilter = chainIdParam ? parseInt(chainIdParam, 10) : null;
const fileBacked = loadDeploymentStatusFile();
let pools = getGruV2DeploymentPoolRows();
if (chainIdFilter && !Number.isNaN(chainIdFilter)) {
pools = pools.filter((p) => p.chainId === chainIdFilter);
}
res.set('Cache-Control', 'public, max-age=0, must-revalidate');
res.json({
generatedAt: new Date().toISOString(),
source: fileBacked ? 'deployment-status-file' : 'none',
complete: !!fileBacked,
version: fileBacked?.data.version,
updated: fileBacked?.data.updated,
lastModified: fileBacked?.lastModified,
homeChainId: fileBacked?.data.homeChainId,
count: pools.length,
pools,
});
} catch (error) {
logger.error('Error building report/gru-v2-pmm-pools:', error);
res.status(500).json({ error: 'Internal server error', pools: [] });
}
});
/** GET /report/adoption-readiness — summary gates for institutional and tracker/listing readiness. */
router.get('/adoption-readiness', async (_req: Request, res: Response) => {
try {
const chainIds = getSupportedChainIds();
const tokensByChain: Record<number, Awaited<ReturnType<typeof buildTokenReport>>> = {};
for (const chainId of chainIds) {
tokensByChain[chainId] = await buildTokenReport(chainId);
}
const allTokens = Object.values(tokensByChain).flat();
const candidates = allTokens.filter((token) =>
isGruSupplyTrackedCandidate(token.symbol, token.type, token.registryFamily)
);
const nonReportablePlaceholder = candidates.filter(
(token) => token.supplyProofProvenance?.source === 'deterministic-placeholder-address'
);
const reportableCandidates = candidates.filter(
(token) => token.supplyProofProvenance?.source !== 'deterministic-placeholder-address'
);
const proved = reportableCandidates.filter((token) => token.supplyProofProvenance && token.supplyProofProvenance.source !== 'missing-supply-proof');
const proofRequired = reportableCandidates.filter((token) => token.supplyProofProvenance?.source === 'missing-supply-proof');
const silent = reportableCandidates.filter((token) => !token.supplyProofProvenance);
const externalOfficialQuoteLiquidity = reportableCandidates.filter(hasExternalOfficialQuoteLiquidity);
const liquidityPositive = reportableCandidates.filter(
(token) => (token.market?.liquidityUsd ?? 0) > 0 || token.pools.some((pool) => pool.tvl > 0) || hasExternalOfficialQuoteLiquidity(token)
);
const poolIndexed = reportableCandidates.filter((token) => token.pools.length > 0);
const liquidityMissing = reportableCandidates.filter(
(token) => (token.market?.liquidityUsd ?? 0) <= 0 && !token.pools.some((pool) => pool.tvl > 0) && !hasExternalOfficialQuoteLiquidity(token)
);
const liquidityMissingWithPools = liquidityMissing.filter((token) => token.pools.length > 0);
const liquidityMissingWithoutPools = liquidityMissing.filter((token) => token.pools.length === 0);
const gruPools = getGruV2DeploymentPoolRows();
const groupTokensByChain = (tokens: typeof candidates) =>
tokens
.reduce<Array<{ chainId: number; count: number; symbols: string[] }>>((groups, token) => {
let group = groups.find((row) => row.chainId === token.chainId);
if (!group) {
group = { chainId: token.chainId, count: 0, symbols: [] };
groups.push(group);
}
group.count += 1;
group.symbols.push(token.symbol);
return groups;
}, [])
.map((group) => ({
...group,
symbols: Array.from(new Set(group.symbols)).sort(),
}))
.sort((a, b) => a.chainId - b.chainId);
res.set('Cache-Control', 'public, max-age=0, must-revalidate');
res.json({
generatedAt: new Date().toISOString(),
scope: 'gru-c-and-cw-assets',
counts: {
candidates: candidates.length,
reportableCandidates: reportableCandidates.length,
nonReportablePlaceholder: nonReportablePlaceholder.length,
proved: proved.length,
proofRequired: proofRequired.length,
silent: silent.length,
poolIndexed: poolIndexed.length,
liquidityPositive: liquidityPositive.length,
liquidityMissing: liquidityMissing.length,
liquidityMissingWithPools: liquidityMissingWithPools.length,
liquidityMissingWithoutPools: liquidityMissingWithoutPools.length,
externalOfficialQuoteLiquidity: externalOfficialQuoteLiquidity.length,
gruV2Pools: gruPools.length,
gruV2PoolsWithStatus: gruPools.filter((pool) => !!pool.status).length,
},
institutional: {
score: Math.round(
100 *
((silent.length === 0 ? 0.25 : 0) +
(proved.length / Math.max(reportableCandidates.length, 1)) * 0.35 +
(poolIndexed.length / Math.max(reportableCandidates.length, 1)) * 0.2 +
(gruPools.length > 0 && gruPools.every((pool) => !!pool.status) ? 0.2 : 0))
),
blockers: [
proofRequired.length > 0 ? `${proofRequired.length} assets still require supply-proof artifacts.` : null,
silent.length > 0 ? `${silent.length} assets have no proof state.` : null,
gruPools.some((pool) => !pool.status) ? 'One or more GRU v2 pools lacks lifecycle status.' : null,
].filter(Boolean),
},
cryptoListing: {
score: Math.round(
100 *
((proved.length / Math.max(reportableCandidates.length, 1)) * 0.45 +
(liquidityPositive.length / Math.max(reportableCandidates.length, 1)) * 0.35 +
(poolIndexed.length / Math.max(reportableCandidates.length, 1)) * 0.2)
),
blockers: [
proofRequired.length > 0 ? 'Most c*/cW* assets are proof-gated for supply and market-cap claims.' : null,
liquidityPositive.length < reportableCandidates.length ? 'Not every reportable c*/cW* asset has positive indexed liquidity in the report API.' : null,
].filter(Boolean),
},
blockerInventory: {
proofRequiredByChain: groupTokensByChain(proofRequired),
liquidityMissingByChain: groupTokensByChain(liquidityMissing),
liquidityMissingWithPoolsByChain: groupTokensByChain(liquidityMissingWithPools),
liquidityMissingWithoutPoolsByChain: groupTokensByChain(liquidityMissingWithoutPools),
liquidityMissingDetails: liquidityMissing
.map((token) => ({
chainId: token.chainId,
symbol: token.symbol,
address: token.address,
poolCount: token.pools.length,
zeroTvlPoolCount: token.pools.filter((pool) => pool.tvl <= 0).length,
marketLiquidityUsd: token.market?.liquidityUsd ?? 0,
category: token.pools.length > 0 ? 'configured_or_indexed_pools_zero_tvl' : 'no_visible_pool_binding',
}))
.sort((a, b) => a.chainId - b.chainId || a.symbol.localeCompare(b.symbol)),
externalOfficialQuoteLiquidityByChain: groupTokensByChain(externalOfficialQuoteLiquidity),
nonReportablePlaceholderByChain: groupTokensByChain(nonReportablePlaceholder),
gruV2PoolsMissingStatus: gruPools
.filter((pool) => !pool.status)
.map((pool) => ({
chainId: pool.chainId,
poolAddress: pool.poolAddress,
baseSymbol: pool.baseSymbol,
quoteSymbol: pool.quoteSymbol,
})),
notes: [
'proof_required means the report intentionally withholds totalSupply, circulatingSupply, and marketCap claims for that asset.',
'nonReportablePlaceholder means the configured address is intentionally visible for roadmap traceability but is not a deployed ERC-20 proof surface.',
'liquidityMissing means no positive indexed liquidity is currently visible through the report API for that asset.',
'External official quote mirrors (public-chain USDC/USDT and ALL AUSDC/AUSDT) are not GRU pool blockers when their canonical token binding is proved; their public venue liquidity is maintained by the issuer/external market and should not be confused with repo-native cW*/Chain 138 liquidity.',
'A configured pool or registry entry is not the same thing as tracker-grade supply proof or live positive liquidity.',
],
},
proofRequiredSample: proofRequired.slice(0, 20).map((token) => ({
chainId: token.chainId,
symbol: token.symbol,
address: token.address,
status: token.supplyProofProvenance?.status,
})),
});
} catch (error) {
logger.error('Error building report/adoption-readiness:', error);
res.status(500).json({ error: 'Internal server error' });
}
});
/** GET /report/gas-registry — live gas-family rollout registry from deployment-status.json plus GRU transport metadata. */
router.get('/gas-registry', async (req: Request, res: Response) => {
try {
const chainIdParam = req.query.chainId as string | undefined;
const chainIdFilter = chainIdParam ? parseInt(chainIdParam, 10) : null;
const fileBackedRegistry = loadDeploymentStatusFile();
const gruTransport = buildGruTransportOverview();
const loaderRuntimePairs = getActiveTransportPairs();
const fallbackRuntimePairs = fileBackedRegistry ? buildGasRuntimePairsFromDeploymentStatus(fileBackedRegistry.data) : [];
const runtimeGasPairs = (loaderRuntimePairs.length > 0 ? loaderRuntimePairs : fallbackRuntimePairs)
.filter((pair) => pair.assetClass === 'gas_native')
.map((pair) => ({
key: pair.key,
destinationChainId: pair.destinationChainId,
destinationChainName: pair.destinationChainName ?? null,
familyKey: pair.familyKey ?? null,
canonicalSymbol: pair.canonicalSymbol,
mirroredSymbol: pair.mirroredSymbol,
wrappedNativeQuoteSymbol: pair.wrappedNativeQuoteSymbol ?? null,
stableQuoteSymbol: pair.stableQuoteSymbol ?? null,
backingMode: pair.backingMode ?? null,
redeemPolicy: pair.redeemPolicy ?? null,
runtimeReady: pair.runtimeReady === true,
supplyInvariantSatisfied: pair.supplyInvariantSatisfied ?? null,
eligibilityBlockers: Array.isArray(pair.eligibilityBlockers) ? pair.eligibilityBlockers : [],
runtimeMissingRequirements: Array.isArray(pair.runtimeMissingRequirements)
? pair.runtimeMissingRequirements
: [],
}));
let chains = fileBackedRegistry ? buildGasRegistryChains(fileBackedRegistry.data) : [];
if (chainIdFilter && !Number.isNaN(chainIdFilter)) {
chains = chains.filter((row) => row.chainId === chainIdFilter);
}
res.set('Cache-Control', 'public, max-age=0, must-revalidate');
res.json({
generatedAt: new Date().toISOString(),
source: fileBackedRegistry ? 'deployment-status-file' : 'transport-config-only',
complete: !!fileBackedRegistry,
version: fileBackedRegistry?.data.version,
updated: fileBackedRegistry?.data.updated,
lastModified: fileBackedRegistry?.lastModified,
gasAssetFamilies: gruTransport?.gasAssetFamilies ?? [],
gasRedeemGroups: gruTransport?.gasRedeemGroups ?? [],
gasProtocolExposure: gruTransport?.gasProtocolExposure ?? [],
runtimePairs: runtimeGasPairs,
chains,
});
} catch (error) {
logger.error('Error building report/gas-registry:', error);
res.status(500).json({ error: 'Internal server error', chains: [] });
}
});
/** GET /report/canonical — raw canonical spec list (no DB merge) */
router.get(
'/canonical',
cacheMiddleware(10 * 60 * 1000),
async (req: Request, res: Response) => {
try {
const gruTransport = buildGruTransportOverview();
res.json({
generatedAt: new Date().toISOString(),
gruTransport,
tokens: CANONICAL_TOKENS.map((t) => ({
symbol: t.symbol,
name: t.name,
type: t.type,
decimals: t.decimals,
currencyCode: t.currencyCode,
registryFamily: getTokenRegistryFamily(t),
familySymbol: t.familySymbol,
deploymentVersion: t.deploymentVersion,
deploymentStatus: t.deploymentStatus,
preferredForX402: t.preferredForX402,
liquiditySourceSymbol: t.liquiditySourceSymbol,
addresses: t.addresses,
})),
});
} catch (error) {
logger.error('Error building report/canonical:', error);
res.status(500).json({ error: 'Internal server error' });
}
}
);
export default router;