"use strict"; /** Transfer-time USD for Chain 138 checkpoint leaves (off-chain metadata only). */ Object.defineProperty(exports, "__esModule", { value: true }); exports.CHAIN138_KNOWN_TOKEN_USD = exports.CHAIN138_STABLE_USD_PEG = exports.CHAIN138_NATIVE_PRICING_ADDRESS = void 0; exports.usdStringToE8 = usdStringToE8; exports.formatAmountUsd = formatAmountUsd; exports.blockTimestampToIso = blockTimestampToIso; exports.fetchHistoricalPriceUsd = fetchHistoricalPriceUsd; exports.priceTransferLine = priceTransferLine; exports.sumUsdStrings = sumUsdStrings; exports.enrichLeafUsdFields = enrichLeafUsdFields; exports.CHAIN138_NATIVE_PRICING_ADDRESS = '0xc02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2'; /** Canonical Chain 138 stables — $1 peg when price-at API is unavailable. */ exports.CHAIN138_STABLE_USD_PEG = { '0x93e66202a11b1772e55407b32b44e5cd8eda7f22': 1, // cUSDT '0xf22258f57794cc8e06237084b353ab30ffa640b': 1, // cUSDC }; /** Known Chain 138 assets (override via env CHECKPOINT_USD_PEG_). */ exports.CHAIN138_KNOWN_TOKEN_USD = { ...exports.CHAIN138_STABLE_USD_PEG, '0xb7721dd53a8c629d9f1ba31a5819afe250002b03': 15, // LINK (set CHECKPOINT_USD_PEG_LINK) '0xe94260c555ac1d9d3cc9e1632883452ebdf0082e': 90000, // cBTC '0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2': 2490, // WETH pricing proxy }; const SYMBOL_USD_ESTIMATE = { CUSDT: 1, CUSDC: 1, USDT: 1, USDC: 1, CJPYC: 0.006285683794888213, CGBPC: 1.3550353712543854, CEURC: 1.178, CAUDC: 0.7136366390016357, CCADC: 0.7255928549430243, CCHFC: 1.2776572668112798, LINK: 15, CBTC: 90000, WETH: 2490, ETH: 2490, }; function knownTokenUsd(address, symbol) { const addr = address.toLowerCase(); const envKey = symbol ? `CHECKPOINT_USD_PEG_${symbol.toUpperCase()}` : ''; if (symbol && envKey && process.env[envKey]) { const n = Number(process.env[envKey]); if (Number.isFinite(n) && n > 0) return n; } const fromAddr = exports.CHAIN138_KNOWN_TOKEN_USD[addr]; if (fromAddr != null && fromAddr > 0) return fromAddr; if (symbol) { const fromSym = SYMBOL_USD_ESTIMATE[symbol.toUpperCase()]; if (fromSym != null && fromSym > 0) return fromSym; } return undefined; } /** Convert decimal USD string (e.g. batch JSON) to 8-decimal fixed point for on-chain events. */ function usdStringToE8(usd) { if (usd == null || usd === '') return 0n; const n = Number(usd); if (!Number.isFinite(n) || n < 0) return 0n; return BigInt(Math.round(n * 1e8)); } function decimalToScaledInteger(value, scale) { if (!Number.isFinite(value)) return null; const normalized = value.toFixed(scale); const negative = normalized.startsWith('-'); const unsigned = negative ? normalized.slice(1) : normalized; const [whole, fraction = ''] = unsigned.split('.'); try { const scaled = BigInt(whole + fraction.padEnd(scale, '0')); return { scaled: negative ? -scaled : scaled, scale: 10n ** BigInt(scale) }; } catch { return null; } } /** USD string with `outputScale` fractional digits (default 6). */ function formatAmountUsd(rawAmount, decimals, priceUsd, priceScale = 8, outputScale = 6) { if (!rawAmount || !Number.isFinite(priceUsd) || decimals < 0) return undefined; try { const amount = BigInt(rawAmount); const parsedPrice = decimalToScaledInteger(priceUsd, priceScale); if (!parsedPrice) return undefined; const numerator = amount * parsedPrice.scaled * (10n ** BigInt(outputScale)); const denominator = (10n ** BigInt(decimals)) * parsedPrice.scale; const rounded = (numerator + denominator / 2n) / denominator; const divisor = 10n ** BigInt(outputScale); const whole = rounded / divisor; const fraction = (rounded % divisor).toString().padStart(outputScale, '0').replace(/0+$/, ''); return fraction ? `${whole.toString()}.${fraction}` : whole.toString(); } catch { return undefined; } } function blockTimestampToIso(blockTimestamp) { const sec = blockTimestamp > 1e12 ? Math.floor(blockTimestamp / 1000) : blockTimestamp; return new Date(sec * 1000).toISOString(); } const priceCache = new Map(); async function fetchHistoricalPriceUsd(cfg, tokenAddress, blockTimestamp) { if (cfg.enabled === false) return null; const chainId = cfg.chainId ?? 138; const iso = blockTimestampToIso(blockTimestamp); const key = `${chainId}:${tokenAddress.toLowerCase()}:${iso}`; if (priceCache.has(key)) return priceCache.get(key) ?? null; const base = cfg.apiBaseUrl.replace(/\/$/, ''); const url = `${base}/tokens/${tokenAddress}/price-at?chainId=${chainId}×tamp=${encodeURIComponent(iso)}`; try { let res = null; for (let attempt = 0; attempt < 4; attempt++) { res = await fetch(url); if (res.ok || res.status < 500) break; await new Promise((r) => setTimeout(r, 200 * (attempt + 1))); } if (!res) { priceCache.set(key, null); return null; } if (!res.ok) { const pegFail = knownTokenUsd(tokenAddress); if (pegFail != null) { const snap = { chainId, tokenAddress: tokenAddress.toLowerCase(), requestedTimestamp: iso, effectiveTimestamp: iso, priceUsd: pegFail, source: 'canonical_token_peg', }; priceCache.set(key, snap); return snap; } priceCache.set(key, null); return null; } const body = (await res.json()); if (body?.error || body?.priceUsd == null || !Number.isFinite(body.priceUsd)) { const peg = knownTokenUsd(tokenAddress); if (peg != null) { const snap = { chainId, tokenAddress: tokenAddress.toLowerCase(), requestedTimestamp: iso, effectiveTimestamp: iso, priceUsd: peg, source: 'canonical_token_peg', }; priceCache.set(key, snap); return snap; } priceCache.set(key, null); return null; } priceCache.set(key, body); return body; } catch { const peg = knownTokenUsd(tokenAddress); if (peg != null) { const snap = { chainId, tokenAddress: tokenAddress.toLowerCase(), requestedTimestamp: iso, effectiveTimestamp: iso, priceUsd: peg, source: 'canonical_token_peg', }; priceCache.set(key, snap); return snap; } priceCache.set(key, null); return null; } } async function priceTransferLine(cfg, line, blockTimestamp) { const amount = BigInt(line.amountRaw || '0'); if (amount === 0n) { return { ...line, valueUsd: '0.000000', priceUsd: line.priceUsd, priceSource: line.priceSource ?? 'zero_amount' }; } const pricingAddress = line.kind === 'native' ? exports.CHAIN138_NATIVE_PRICING_ADDRESS : (line.token || '').trim(); if (!pricingAddress) { return { ...line, valueUsd: undefined, priceSource: 'unavailable' }; } let snap = await fetchHistoricalPriceUsd(cfg, pricingAddress, blockTimestamp); if (!snap) { const peg = knownTokenUsd(pricingAddress, line.tokenSymbol); if (peg != null) { snap = { chainId: cfg.chainId ?? 138, tokenAddress: pricingAddress.toLowerCase(), requestedTimestamp: blockTimestampToIso(blockTimestamp), effectiveTimestamp: blockTimestampToIso(blockTimestamp), priceUsd: peg, source: 'canonical_token_peg', }; } } if (!snap) { return { ...line, valueUsd: undefined, priceSource: 'unavailable' }; } const decimals = line.kind === 'native' ? 18 : (line.tokenDecimals ?? 18); const valueUsd = formatAmountUsd(line.amountRaw, decimals, snap.priceUsd); return { ...line, priceUsd: snap.priceUsd, priceSource: snap.source, priceEffectiveTimestamp: snap.effectiveTimestamp, valueUsd, }; } /** Sum USD strings (6 decimal places) without floating-point drift. */ function sumUsdStrings(values) { const scale = 6n; const factor = 10n ** scale; let sumMicro = 0n; let any = false; for (const v of values) { if (v == null || v === '') continue; const neg = v.startsWith('-'); const parts = (neg ? v.slice(1) : v).split('.'); const whole = BigInt(parts[0] || '0'); const frac = (parts[1] || '').padEnd(Number(scale), '0').slice(0, Number(scale)); const micro = whole * factor + BigInt(frac || '0'); sumMicro += neg ? -micro : micro; any = true; } if (!any) return undefined; const negOut = sumMicro < 0n; const abs = negOut ? -sumMicro : sumMicro; const whole = abs / factor; const frac = (abs % factor).toString().padStart(Number(scale), '0').replace(/0+$/, ''); const body = frac ? `${whole}.${frac}` : whole.toString(); return negOut ? `-${body}` : body; } async function enrichLeafUsdFields(cfg, leaf, allErc20) { const out = { ...leaf }; for (const k of [ 'valueUsd', 'nativeValueUsd', 'tokenValueUsd', 'nativePriceUsd', 'tokenPriceUsd', 'priceSource', 'priceEffectiveTimestamp', 'totalTransfersUsd', 'transfers', 'usdEnrichedAt', ]) { delete out[k]; } const blockTimestamp = Number(out.blockTimestamp ?? 0); const nativeWei = BigInt(String(out.nativeValueWei ?? out.value ?? '0')); const lines = []; if (nativeWei > 0n) { lines.push({ kind: 'native', tokenSymbol: 'ETH', tokenDecimals: 18, from: String(out.from ?? ''), to: String(out.to ?? ''), amountRaw: nativeWei.toString(), }); } for (const t of allErc20) { lines.push({ kind: 'erc20', token: t.token, tokenSymbol: t.tokenSymbol, tokenDecimals: t.tokenDecimals, from: t.from, to: t.to, amountRaw: t.value.toString(), logIndex: t.logIndex, }); } const priced = []; for (const line of lines) { priced.push(await priceTransferLine(cfg, line, blockTimestamp)); if (cfg.requestDelayMs && cfg.requestDelayMs > 0) { await new Promise((r) => setTimeout(r, cfg.requestDelayMs)); } } out.transfers = priced; const nativeLine = priced.find((l) => l.kind === 'native'); const tokenLines = priced.filter((l) => l.kind === 'erc20'); const primaryToken = tokenLines.reduce((best, cur) => { if (!best) return cur; try { return BigInt(cur.amountRaw) > BigInt(best.amountRaw) ? cur : best; } catch { return best; } }, undefined); if (nativeLine?.valueUsd != null) out.nativeValueUsd = nativeLine.valueUsd; if (nativeLine?.priceUsd != null) out.nativePriceUsd = nativeLine.priceUsd; if (primaryToken) { if (primaryToken.valueUsd != null) out.tokenValueUsd = primaryToken.valueUsd; if (primaryToken.priceUsd != null) out.tokenPriceUsd = primaryToken.priceUsd; if (primaryToken.priceSource) out.priceSource = primaryToken.priceSource; if (primaryToken.priceEffectiveTimestamp) { out.priceEffectiveTimestamp = primaryToken.priceEffectiveTimestamp; } } const effectiveToken = BigInt(String(out.tokenValue ?? '0')); if (effectiveToken > 0n && primaryToken?.valueUsd != null) { out.valueUsd = primaryToken.valueUsd; } else if (nativeLine?.valueUsd != null) { out.valueUsd = nativeLine.valueUsd; } else if (priced.length === 0) { out.valueUsd = '0.000000'; out.priceSource = 'no_transfer_value'; } else { out.valueUsd = sumUsdStrings(priced.map((l) => l.valueUsd)); out.priceSource = priced.some((l) => l.priceSource && l.priceSource !== 'unavailable') ? 'multi_transfer_sum' : 'unavailable'; } out.totalTransfersUsd = sumUsdStrings(priced.map((l) => l.valueUsd)); out.usdEnrichedAt = new Date().toISOString(); return out; }