Some checks failed
CI/CD Pipeline / Solidity Contracts (push) Failing after 1m3s
CI/CD Pipeline / Security Scanning (push) Successful in 2m18s
CI/CD Pipeline / Lint and Format (push) Failing after 34s
CI/CD Pipeline / Terraform Validation (push) Failing after 20s
CI/CD Pipeline / Kubernetes Validation (push) Successful in 22s
Deploy ChainID 138 / Deploy ChainID 138 (push) Failing after 40s
HYBX OMNL TypeScript & anchor / token-aggregation build + reconcile artifact (push) Failing after 49s
OMNL reconcile anchor / Run omnl:reconcile and upload artifacts (push) Failing after 21s
Validation / validate-genesis (push) Successful in 25s
Validation / validate-terraform (push) Failing after 21s
Validation / validate-kubernetes (push) Failing after 8s
Validation / validate-smart-contracts (push) Failing after 8s
Validation / validate-security (push) Failing after 1m11s
Validation / validate-documentation (push) Failing after 14s
Verify Deployment / Verify Deployment (push) Failing after 45s
Ship AddressActivityRegistry V1/V2, ISO20022IntakeGateway, Chain138ParticipantSurface, checkpoint hub contracts, checkpoint-core package, aggregator/indexer/sdk services, relay profile guards, M00 diamond bridge facet, and OMNL compliance contracts. Co-authored-by: Cursor <cursoragent@cursor.com>
346 lines
13 KiB
JavaScript
346 lines
13 KiB
JavaScript
"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_<SYMBOL>). */
|
|
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;
|
|
}
|