feat(token-aggregation): reports, PMM quotes, config; Engine X flash vaults
- Expand token-aggregation API (report routes), canonical tokens, pools - Add flash vault contracts + tests (indexed, DODO cwUSDC, XAUT borrow) - PMM pools JSON, deploy/export scripts, metamask verified list Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -0,0 +1,163 @@
|
||||
import fs from 'fs';
|
||||
import path from 'path';
|
||||
|
||||
type DiscoveryPayload = {
|
||||
generated_at?: string;
|
||||
entries?: Array<{
|
||||
chain_id: number;
|
||||
network?: string;
|
||||
factoryAddress?: string;
|
||||
routerAddress?: string;
|
||||
pairsChecked?: Array<{
|
||||
base: string;
|
||||
quote: string;
|
||||
poolAddress: string;
|
||||
live: boolean;
|
||||
health?: {
|
||||
baseReserveRaw?: string;
|
||||
quoteReserveRaw?: string;
|
||||
baseReserveUnits?: string;
|
||||
quoteReserveUnits?: string;
|
||||
priceQuotePerBase?: string;
|
||||
deviationBps?: string;
|
||||
healthy?: boolean;
|
||||
depthOk?: boolean;
|
||||
parityOk?: boolean;
|
||||
};
|
||||
}>;
|
||||
}>;
|
||||
};
|
||||
|
||||
type DeploymentStatusChain = {
|
||||
name?: string;
|
||||
cwTokens?: Record<string, string>;
|
||||
anchorAddresses?: Record<string, string>;
|
||||
gasMirrors?: Record<string, string>;
|
||||
gasQuoteAddresses?: Record<string, string>;
|
||||
};
|
||||
|
||||
type DeploymentStatus = {
|
||||
chains?: Record<string, DeploymentStatusChain>;
|
||||
};
|
||||
|
||||
const PRICE_USD: Record<string, number> = {
|
||||
USDC: 1,
|
||||
USDT: 1,
|
||||
cUSDC: 1,
|
||||
cUSDT: 1,
|
||||
cWUSDC: 1,
|
||||
cWUSDT: 1,
|
||||
cWAUSDT: 1,
|
||||
};
|
||||
|
||||
function findRepoRoot(): 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 (fs.existsSync(path.join(candidate, 'reports/extraction/promod-uniswap-v2-live-pair-discovery-latest.json'))) {
|
||||
return candidate;
|
||||
}
|
||||
}
|
||||
return path.resolve(process.cwd(), '..', '..', '..');
|
||||
}
|
||||
|
||||
function readJson<T>(filePath: string): T {
|
||||
return JSON.parse(fs.readFileSync(filePath, 'utf8')) as T;
|
||||
}
|
||||
|
||||
function resolveTokenAddress(chain: DeploymentStatusChain, symbol: string): string | undefined {
|
||||
return (
|
||||
chain.cwTokens?.[symbol] ||
|
||||
chain.anchorAddresses?.[symbol] ||
|
||||
chain.gasMirrors?.[symbol] ||
|
||||
chain.gasQuoteAddresses?.[symbol]
|
||||
);
|
||||
}
|
||||
|
||||
function parsePositiveNumber(value?: string): number {
|
||||
const parsed = Number(value);
|
||||
return Number.isFinite(parsed) && parsed > 0 ? parsed : 0;
|
||||
}
|
||||
|
||||
function main(): void {
|
||||
const repoRoot = findRepoRoot();
|
||||
const discoveryPath = path.join(repoRoot, 'reports/extraction/promod-uniswap-v2-live-pair-discovery-latest.json');
|
||||
const deploymentStatusPath = path.join(repoRoot, 'cross-chain-pmm-lps/config/deployment-status.json');
|
||||
const outPath = path.join(process.cwd(), 'config/live-uniswap-v2-pool-catalog.json');
|
||||
|
||||
const discovery = readJson<DiscoveryPayload>(discoveryPath);
|
||||
const deploymentStatus = readJson<DeploymentStatus>(deploymentStatusPath);
|
||||
const pools = [];
|
||||
|
||||
for (const entry of discovery.entries ?? []) {
|
||||
const chainId = Number(entry.chain_id);
|
||||
const chain = deploymentStatus.chains?.[String(chainId)];
|
||||
if (!chain) continue;
|
||||
|
||||
for (const pair of entry.pairsChecked ?? []) {
|
||||
if (!pair.live || !pair.poolAddress?.startsWith('0x') || !pair.health) continue;
|
||||
const baseAddress = resolveTokenAddress(chain, pair.base);
|
||||
const quoteAddress = resolveTokenAddress(chain, pair.quote);
|
||||
if (!baseAddress || !quoteAddress) continue;
|
||||
|
||||
const baseReserveUnits = parsePositiveNumber(pair.health.baseReserveUnits);
|
||||
const quoteReserveUnits = parsePositiveNumber(pair.health.quoteReserveUnits);
|
||||
const basePriceUsd = PRICE_USD[pair.base] ?? 0;
|
||||
const quotePriceUsd = PRICE_USD[pair.quote] ?? 0;
|
||||
const reserve0Usd = baseReserveUnits * basePriceUsd;
|
||||
const reserve1Usd = quoteReserveUnits * quotePriceUsd;
|
||||
const totalLiquidityUsd = reserve0Usd + reserve1Usd;
|
||||
if (totalLiquidityUsd <= 0) continue;
|
||||
|
||||
pools.push({
|
||||
chainId,
|
||||
chainName: entry.network ?? chain.name ?? `Chain ${chainId}`,
|
||||
poolAddress: pair.poolAddress.toLowerCase(),
|
||||
dex: 'uniswap_v2',
|
||||
factoryAddress: entry.factoryAddress,
|
||||
routerAddress: entry.routerAddress,
|
||||
baseSymbol: pair.base,
|
||||
quoteSymbol: pair.quote,
|
||||
baseAddress: baseAddress.toLowerCase(),
|
||||
quoteAddress: quoteAddress.toLowerCase(),
|
||||
baseReserveRaw: pair.health.baseReserveRaw,
|
||||
quoteReserveRaw: pair.health.quoteReserveRaw,
|
||||
baseReserveUnits: pair.health.baseReserveUnits,
|
||||
quoteReserveUnits: pair.health.quoteReserveUnits,
|
||||
priceQuotePerBase: pair.health.priceQuotePerBase,
|
||||
deviationBps: pair.health.deviationBps,
|
||||
depthOk: pair.health.depthOk,
|
||||
parityOk: pair.health.parityOk,
|
||||
healthy: pair.health.healthy,
|
||||
reserve0Usd,
|
||||
reserve1Usd,
|
||||
totalLiquidityUsd,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
pools.sort((a, b) => a.chainId - b.chainId || a.poolAddress.localeCompare(b.poolAddress));
|
||||
const payload = {
|
||||
schema: 'token-aggregation-live-uniswap-v2-pool-catalog/v1',
|
||||
generatedAt: new Date().toISOString(),
|
||||
sourceArtifacts: [
|
||||
'reports/extraction/promod-uniswap-v2-live-pair-discovery-latest.json',
|
||||
'cross-chain-pmm-lps/config/deployment-status.json',
|
||||
],
|
||||
poolCount: pools.length,
|
||||
positiveLiquidityPoolCount: pools.filter((pool) => pool.totalLiquidityUsd > 0).length,
|
||||
pools,
|
||||
};
|
||||
|
||||
fs.mkdirSync(path.dirname(outPath), { recursive: true });
|
||||
fs.writeFileSync(outPath, `${JSON.stringify(payload, null, 2)}\n`);
|
||||
console.log(outPath);
|
||||
}
|
||||
|
||||
main();
|
||||
@@ -0,0 +1,238 @@
|
||||
#!/usr/bin/env ts-node
|
||||
|
||||
import { existsSync, mkdirSync, readFileSync, writeFileSync } from 'fs';
|
||||
import path from 'path';
|
||||
import { Contract, JsonRpcProvider, formatUnits } from 'ethers';
|
||||
import { CANONICAL_TOKENS, getTokenRegistryFamily } from '../src/config/canonical-tokens';
|
||||
import { getChainConfig } from '../src/config/chains';
|
||||
import { NETWORKS } from '../src/config/networks';
|
||||
|
||||
const ERC20_ABI = [
|
||||
'function totalSupply() view returns (uint256)',
|
||||
'function decimals() view returns (uint8)',
|
||||
'function symbol() view returns (string)',
|
||||
'function name() view returns (string)',
|
||||
];
|
||||
|
||||
type SupplyProof = {
|
||||
schema: string;
|
||||
generatedAt: string;
|
||||
network: {
|
||||
chainId: number;
|
||||
name: string;
|
||||
referenceBlock: number;
|
||||
};
|
||||
token: {
|
||||
address: string;
|
||||
name: string;
|
||||
symbol: string;
|
||||
onchainName?: string;
|
||||
onchainSymbol?: string;
|
||||
decimals: number;
|
||||
totalSupplyRaw: string;
|
||||
totalSupplyUnits: string;
|
||||
};
|
||||
circulatingSupplyMethodology: {
|
||||
status: string;
|
||||
recommendedFormula: string;
|
||||
currentConservativeReportableCirculatingSupplyUnits: string;
|
||||
notes: string[];
|
||||
};
|
||||
knownBalances?: unknown;
|
||||
trackerSurfaces?: unknown;
|
||||
[key: string]: unknown;
|
||||
};
|
||||
|
||||
type SupplyProofCatalog = {
|
||||
schema: string;
|
||||
generatedAt: string;
|
||||
proofs: SupplyProof[];
|
||||
proofFailures?: Array<{
|
||||
chainId: number;
|
||||
symbol: string;
|
||||
address: string;
|
||||
reason: string;
|
||||
}>;
|
||||
};
|
||||
|
||||
function isCandidate(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 || '')))
|
||||
);
|
||||
}
|
||||
|
||||
function catalogKey(chainId: number, address: string): string {
|
||||
return `${chainId}:${address.toLowerCase()}`;
|
||||
}
|
||||
|
||||
function rpcUrlsForChain(chainId: number, primary?: string): string[] {
|
||||
const network = NETWORKS.find((row) => row.chainIdDecimal === chainId);
|
||||
return Array.from(new Set([primary, ...(network?.rpcUrls ?? [])].filter(Boolean) as string[]));
|
||||
}
|
||||
|
||||
function loadProofFile(proofPath: string): SupplyProof[] {
|
||||
if (!existsSync(proofPath)) return [];
|
||||
const parsed = JSON.parse(readFileSync(proofPath, 'utf8')) as Partial<SupplyProofCatalog> | SupplyProof;
|
||||
const proofs = Array.isArray((parsed as Partial<SupplyProofCatalog>).proofs)
|
||||
? ((parsed as SupplyProofCatalog).proofs)
|
||||
: [parsed as SupplyProof];
|
||||
return proofs.filter((proof) => proof?.network?.chainId && proof?.token?.address);
|
||||
}
|
||||
|
||||
function loadExistingCatalog(seedPaths: string[]): Map<string, SupplyProof> {
|
||||
const proofs = new Map<string, SupplyProof>();
|
||||
for (const seedPath of seedPaths) {
|
||||
for (const proof of loadProofFile(seedPath)) {
|
||||
proofs.set(catalogKey(proof.network.chainId, proof.token.address), proof);
|
||||
}
|
||||
}
|
||||
return proofs;
|
||||
}
|
||||
|
||||
function shouldPreserveExistingProof(proof: SupplyProof | undefined): boolean {
|
||||
if (!proof) return false;
|
||||
return (
|
||||
proof.knownBalances !== undefined ||
|
||||
proof.trackerSurfaces !== undefined ||
|
||||
proof.circulatingSupplyMethodology?.status === 'ready_for_tracker_review'
|
||||
);
|
||||
}
|
||||
|
||||
async function withTimeout<T>(promise: Promise<T>, timeoutMs: number, label: string): Promise<T> {
|
||||
let timeout: NodeJS.Timeout | undefined;
|
||||
const timer = new Promise<never>((_, reject) => {
|
||||
timeout = setTimeout(() => reject(new Error(`${label} timed out after ${timeoutMs}ms`)), timeoutMs);
|
||||
});
|
||||
try {
|
||||
return await Promise.race([promise, timer]);
|
||||
} finally {
|
||||
if (timeout) clearTimeout(timeout);
|
||||
}
|
||||
}
|
||||
|
||||
async function main() {
|
||||
const serviceRoot = path.resolve(__dirname, '..');
|
||||
const catalogPath = path.resolve(
|
||||
process.env.TOKEN_AGGREGATION_SUPPLY_PROOF_CATALOG_JSON ||
|
||||
path.join(serviceRoot, 'config/supply-proof-catalog.json')
|
||||
);
|
||||
const repoRoot = path.resolve(serviceRoot, '..', '..', '..');
|
||||
const timeoutMs = Number(process.env.SUPPLY_PROOF_RPC_TIMEOUT_MS || 12000);
|
||||
const generatedAt = new Date().toISOString();
|
||||
const existing = loadExistingCatalog([
|
||||
catalogPath,
|
||||
path.join(repoRoot, 'reports/status/mainnet-cwusdc-supply-proof-20260508.json'),
|
||||
]);
|
||||
const proofs = new Map(existing);
|
||||
const failures: SupplyProofCatalog['proofFailures'] = [];
|
||||
|
||||
for (const spec of CANONICAL_TOKENS) {
|
||||
const registryFamily = getTokenRegistryFamily(spec);
|
||||
if (!isCandidate(spec.symbol, spec.type, registryFamily)) continue;
|
||||
|
||||
for (const [chainIdText, rawAddress] of Object.entries(spec.addresses)) {
|
||||
const chainId = Number(chainIdText);
|
||||
const address = String(rawAddress || '').trim();
|
||||
if (!address.startsWith('0x')) continue;
|
||||
const chain = getChainConfig(chainId);
|
||||
if (!chain?.rpcUrl) {
|
||||
failures.push({ chainId, symbol: spec.symbol, address, reason: 'missing_rpc_url' });
|
||||
continue;
|
||||
}
|
||||
|
||||
let proved = false;
|
||||
const reasons: string[] = [];
|
||||
for (const rpcUrl of rpcUrlsForChain(chainId, chain.rpcUrl)) {
|
||||
try {
|
||||
const provider = new JsonRpcProvider(rpcUrl, chainId, { staticNetwork: true });
|
||||
const contract = new Contract(address, ERC20_ABI, provider);
|
||||
const [referenceBlock, totalSupplyRaw, decimals, onchainSymbol, onchainName] = await withTimeout(
|
||||
Promise.all([
|
||||
provider.getBlockNumber(),
|
||||
contract.totalSupply() as Promise<bigint>,
|
||||
contract.decimals().catch(() => spec.decimals) as Promise<number>,
|
||||
contract.symbol().catch(() => undefined) as Promise<string | undefined>,
|
||||
contract.name().catch(() => undefined) as Promise<string | undefined>,
|
||||
]),
|
||||
timeoutMs,
|
||||
`${chainId}:${spec.symbol}`
|
||||
);
|
||||
const decimalsNumber = Number(decimals);
|
||||
const totalSupplyRawText = totalSupplyRaw.toString();
|
||||
const totalSupplyUnits = formatUnits(totalSupplyRaw, decimalsNumber);
|
||||
const key = catalogKey(chainId, address);
|
||||
if (!shouldPreserveExistingProof(proofs.get(key))) {
|
||||
proofs.set(key, {
|
||||
schema: 'token-aggregation-supply-proof/v1',
|
||||
generatedAt,
|
||||
network: {
|
||||
chainId,
|
||||
name: chain.name,
|
||||
referenceBlock,
|
||||
},
|
||||
token: {
|
||||
address,
|
||||
name: spec.name,
|
||||
symbol: spec.symbol,
|
||||
onchainName,
|
||||
onchainSymbol,
|
||||
decimals: decimalsNumber,
|
||||
totalSupplyRaw: totalSupplyRawText,
|
||||
totalSupplyUnits,
|
||||
},
|
||||
circulatingSupplyMethodology: {
|
||||
status: 'onchain_total_supply_proved_circulating_review_required',
|
||||
recommendedFormula: 'circulatingSupply = totalSupply - protocolControlledNonCirculatingBalances',
|
||||
currentConservativeReportableCirculatingSupplyUnits: totalSupplyUnits,
|
||||
notes: [
|
||||
'totalSupply was read from the mapped token contract at the reference block.',
|
||||
'circulatingSupply is conservatively set to totalSupply until protocol-controlled non-circulating balances are supplied.',
|
||||
'Tracker acceptance is external and not implied by this API response.',
|
||||
],
|
||||
},
|
||||
});
|
||||
}
|
||||
proved = true;
|
||||
break;
|
||||
} catch (error) {
|
||||
reasons.push(`${rpcUrl}: ${error instanceof Error ? error.message : String(error)}`);
|
||||
}
|
||||
}
|
||||
|
||||
if (!proved) {
|
||||
failures.push({
|
||||
chainId,
|
||||
symbol: spec.symbol,
|
||||
address,
|
||||
reason: reasons.join(' | '),
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const catalog: SupplyProofCatalog = {
|
||||
schema: 'token-aggregation-supply-proof-catalog/v1',
|
||||
generatedAt,
|
||||
proofs: Array.from(proofs.values()).sort(
|
||||
(a, b) => a.network.chainId - b.network.chainId || a.token.symbol.localeCompare(b.token.symbol)
|
||||
),
|
||||
proofFailures: failures,
|
||||
};
|
||||
|
||||
mkdirSync(path.dirname(catalogPath), { recursive: true });
|
||||
writeFileSync(catalogPath, `${JSON.stringify(catalog, null, 2)}\n`);
|
||||
console.log(JSON.stringify({
|
||||
catalogPath,
|
||||
proofs: catalog.proofs.length,
|
||||
failures: failures.length,
|
||||
}, null, 2));
|
||||
}
|
||||
|
||||
main().catch((error) => {
|
||||
console.error(error);
|
||||
process.exit(1);
|
||||
});
|
||||
Reference in New Issue
Block a user