From c3ef31d8f31bf6fa09247adbd333f4494a0e47c4 Mon Sep 17 00:00:00 2001 From: defiQUG Date: Mon, 11 May 2026 21:26:14 -0700 Subject: [PATCH] fix(token-aggregation): merge live Uniswap V2 catalog TVL into CMC pool rows Resolve pool catalog from multiple monorepo cwd layouts; prefer catalog totalLiquidityUsd over lower DB/repo-fallback TVL. Extend CMC report tests for cWUSDC pair liquidity assertions. Co-authored-by: Cursor --- .../src/api/routes/report.test.ts | 8 +++ .../src/api/routes/report.ts | 63 ++++++++++++++++--- 2 files changed, 62 insertions(+), 9 deletions(-) diff --git a/services/token-aggregation/src/api/routes/report.test.ts b/services/token-aggregation/src/api/routes/report.test.ts index fcd883e..e09dcb4 100644 --- a/services/token-aggregation/src/api/routes/report.test.ts +++ b/services/token-aggregation/src/api/routes/report.test.ts @@ -121,6 +121,12 @@ describe('Report API', () => { }), }); expect(cwusdc.tracker_caveats).toEqual(expect.arrayContaining([expect.stringContaining('on-chain supply proof')])); + expect(cwusdc.liquidity_usd).toBeGreaterThan(1_000_000); + const cwusdcUsdc = cwusdc.pairs.find( + (p: Record) => + String(p.pair_address).toLowerCase() === '0xc28706f899266b36bc43cc072b3a921bdf2c48d9' + ); + expect(cwusdcUsdc?.liquidity_usd).toBeGreaterThan(1_000_000); }); }); @@ -154,8 +160,10 @@ describe('Report API', () => { market_data: expect.objectContaining({ current_price: { usd: 1 }, market_cap: 10451316981.309788, + liquidity_usd: expect.any(Number), }), }); + expect(cwusdc.market_data.liquidity_usd).toBeGreaterThan(1_000_000); expect(cwusdc.tracker_caveats).toEqual(expect.arrayContaining([expect.stringContaining('No public tracker')])); }); diff --git a/services/token-aggregation/src/api/routes/report.ts b/services/token-aggregation/src/api/routes/report.ts index 9ae1c94..77b1f17 100644 --- a/services/token-aggregation/src/api/routes/report.ts +++ b/services/token-aggregation/src/api/routes/report.ts @@ -164,15 +164,29 @@ function resolveRepoRoot(): string { return process.cwd(); } +/** Directories that may contain `config/live-uniswap-v2-pool-catalog.json` (package cwd or monorepo layouts). */ +function resolveLiveUniswapPoolCatalogRoots(): string[] { + const cwd = process.cwd(); + const nestedTa = path.join(cwd, 'smom-dbis-138', 'services', 'token-aggregation'); + const ordered = [ + cwd, + ...(existsSync(path.join(nestedTa, DEFAULT_LIVE_UNISWAP_V2_POOL_CATALOG_RELATIVE_PATH)) ? [nestedTa] : []), + path.resolve(cwd, '..', '..', '..'), + path.resolve(cwd, '..'), + process.env.PROXMOX_REPO_ROOT, + process.env.PROJECT_ROOT, + ].filter(Boolean) as string[]; + return [...new Set(ordered)]; +} + function loadLiveUniswapV2PoolCatalog(): LiveUniswapV2PoolCatalogRow[] { const configuredCatalog = process.env.TOKEN_AGGREGATION_LIVE_UNISWAP_V2_POOL_CATALOG_JSON?.trim(); - const repoRoot = resolveRepoRoot(); - const candidates = [ + const catalogCandidates = [ configuredCatalog, - path.join(repoRoot, DEFAULT_LIVE_UNISWAP_V2_POOL_CATALOG_RELATIVE_PATH), + ...resolveLiveUniswapPoolCatalogRoots().map((root) => path.join(root, DEFAULT_LIVE_UNISWAP_V2_POOL_CATALOG_RELATIVE_PATH)), ].filter(Boolean) as string[]; - for (const candidate of candidates) { + for (const candidate of catalogCandidates) { if (!existsSync(candidate)) continue; try { const parsed = JSON.parse(readFileSync(candidate, 'utf8')) as LiveUniswapV2PoolCatalog; @@ -501,6 +515,29 @@ async function buildGruV2FallbackPoolsForToken( return Promise.all(pools.map((pool) => enrichGruV2FallbackPoolWithReserves(chainId, pool))); } +/** Prefer live catalog USD TVL when DB or repo-fallback rows carry the same pool with lower TVL. */ +function mergeLiveUniswapCatalogTvlIntoPools(chainId: number, pools: ReportPoolEntry[]): ReportPoolEntry[] { + const catalog = loadLiveUniswapV2PoolCatalog(); + const byPool = new Map( + catalog.filter((row) => row.chainId === chainId).map((row) => [row.poolAddress.toLowerCase(), row]) + ); + return pools.map((pool) => { + const row = byPool.get(pool.poolAddress.toLowerCase()); + if (!row) return pool; + const catalogUsd = Number(row.totalLiquidityUsd ?? 0); + if (!Number.isFinite(catalogUsd) || catalogUsd <= (pool.tvl || 0)) { + return pool; + } + const note = + 'Live Uniswap V2 pool catalog totalLiquidityUsd merged over lower indexed DB or repo-fallback TVL.'; + return { + ...pool, + tvl: catalogUsd, + statusReason: pool.statusReason ? `${pool.statusReason} ${note}` : note, + }; + }); +} + function buildLiveUniswapV2FallbackPoolsForToken( chainId: number, tokenAddress: string, @@ -707,11 +744,16 @@ async function buildTokenReport(chainId: number) { ...dbPoolEntries, ...liveUniswapV2PoolEntries, ]); - const reportPools = [ + const reportPools = mergeLiveUniswapCatalogTvlIntoPools(chainId, [ ...dbPoolEntries, ...liveUniswapV2PoolEntries, ...gruV2FallbackPoolEntries, - ]; + ]); + + const poolTvlSum = reportPools.reduce((sum, p) => sum + (Number.isFinite(p.tvl) ? p.tvl : 0), 0); + if (market && poolTvlSum > (market.liquidityUsd ?? 0)) { + market.liquidityUsd = poolTvlSum; + } out.push({ chainId, @@ -1155,7 +1197,9 @@ router.get( const chainId = parseInt(req.query.chainId as string, 10) || 138; const tokens = await buildTokenReport(chainId); - const coingeckoFormat = tokens.map((t) => ({ + const coingeckoFormat = tokens.map((t) => { + const poolTvlSum = t.pools.reduce((sum, p) => sum + (Number.isFinite(p.tvl) ? p.tvl : 0), 0); + return { chain_id: chainId, contract_address: t.address, id: `${t.symbol.toLowerCase()}-${chainId}`, @@ -1177,7 +1221,7 @@ router.get( current_price: { usd: t.market.priceUsd }, total_volume: t.market.volume24h, market_cap: t.market.marketCapUsd, - liquidity_usd: t.market.liquidityUsd, + liquidity_usd: Math.max(t.market.liquidityUsd ?? 0, poolTvlSum), last_updated: t.market.lastUpdated, } : undefined, @@ -1195,7 +1239,8 @@ router.get( base_symbol: p.token0Symbol, quote_symbol: p.token1Symbol, })), - })); + }; + }); const crossChainReport = await buildCrossChainReport(chainId).catch(() => null);