- 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>
205 lines
6.1 KiB
TypeScript
205 lines
6.1 KiB
TypeScript
import fs from 'fs';
|
|
import path from 'path';
|
|
|
|
export interface DeploymentStatusFile {
|
|
version?: string;
|
|
updated?: string;
|
|
chains?: Record<
|
|
string,
|
|
{
|
|
name?: string;
|
|
cwTokens?: Record<string, string>;
|
|
gasMirrors?: Record<string, string>;
|
|
gasQuoteAddresses?: Record<string, string>;
|
|
gasPmmPools?: Array<Record<string, unknown>>;
|
|
gasReferenceVenues?: Array<Record<string, unknown>>;
|
|
[key: string]: unknown;
|
|
}
|
|
>;
|
|
[key: string]: unknown;
|
|
}
|
|
|
|
export interface LoadedDeploymentStatus {
|
|
data: DeploymentStatusFile;
|
|
lastModified?: string;
|
|
}
|
|
|
|
export interface CwRegistryChain {
|
|
chainId: number;
|
|
chainIdText: string;
|
|
name: string;
|
|
tokens: Array<{
|
|
symbol: string;
|
|
address: string;
|
|
assetClass?: string;
|
|
familyKey?: string;
|
|
}>;
|
|
}
|
|
|
|
export interface GasRegistryChain {
|
|
chainId: number;
|
|
chainIdText: string;
|
|
name: string;
|
|
families: Array<{
|
|
familyKey: string;
|
|
mirroredSymbol: string;
|
|
mirrorAddress?: string;
|
|
dodoPmm: Array<Record<string, unknown>>;
|
|
referenceVenues: Array<Record<string, unknown>>;
|
|
}>;
|
|
}
|
|
|
|
function uniquePaths(paths: Array<string | undefined | null>): string[] {
|
|
const seen = new Set<string>();
|
|
const out: string[] = [];
|
|
|
|
for (const candidate of paths) {
|
|
if (typeof candidate !== 'string') continue;
|
|
const trimmed = candidate.trim();
|
|
if (!trimmed || seen.has(trimmed)) continue;
|
|
seen.add(trimmed);
|
|
out.push(trimmed);
|
|
}
|
|
|
|
return out;
|
|
}
|
|
|
|
function buildDeploymentStatusCandidates(): string[] {
|
|
return uniquePaths([
|
|
process.env.DEPLOYMENT_STATUS_JSON_PATH,
|
|
process.env.CW_REGISTRY_JSON_PATH,
|
|
process.env.CROSS_CHAIN_PMM_DEPLOYMENT_STATUS_PATH,
|
|
process.env.PROXMOX_REPO_ROOT
|
|
? path.resolve(process.env.PROXMOX_REPO_ROOT, 'cross-chain-pmm-lps/config/deployment-status.json')
|
|
: undefined,
|
|
path.resolve(process.cwd(), 'cross-chain-pmm-lps/config/deployment-status.json'),
|
|
path.resolve(process.cwd(), '..', 'cross-chain-pmm-lps/config/deployment-status.json'),
|
|
path.resolve(process.cwd(), '..', '..', 'cross-chain-pmm-lps/config/deployment-status.json'),
|
|
path.resolve(__dirname, '../../../../../cross-chain-pmm-lps/config/deployment-status.json'),
|
|
]);
|
|
}
|
|
|
|
export function resolveDeploymentStatusPath(): string | null {
|
|
for (const candidate of buildDeploymentStatusCandidates()) {
|
|
if (fs.existsSync(candidate)) {
|
|
return candidate;
|
|
}
|
|
}
|
|
return null;
|
|
}
|
|
|
|
export function loadDeploymentStatusFile(): LoadedDeploymentStatus | null {
|
|
const filePath = resolveDeploymentStatusPath();
|
|
if (!filePath) return null;
|
|
|
|
try {
|
|
const raw = fs.readFileSync(filePath, 'utf8');
|
|
const stat = fs.statSync(filePath);
|
|
return {
|
|
data: JSON.parse(raw) as DeploymentStatusFile,
|
|
lastModified: stat.mtime.toISOString(),
|
|
};
|
|
} catch {
|
|
return null;
|
|
}
|
|
}
|
|
|
|
export function buildCwRegistryChains(data: DeploymentStatusFile): CwRegistryChain[] {
|
|
const chains = data.chains ?? {};
|
|
const rows: CwRegistryChain[] = [];
|
|
|
|
for (const [chainIdText, chain] of Object.entries(chains)) {
|
|
const gasFamilyByMirror = new Map<string, string>();
|
|
for (const pool of chain.gasPmmPools ?? []) {
|
|
const familyKey = typeof pool.familyKey === 'string' ? pool.familyKey : '';
|
|
const base = typeof pool.base === 'string' ? pool.base : '';
|
|
if (familyKey && base) {
|
|
gasFamilyByMirror.set(base, familyKey);
|
|
}
|
|
}
|
|
|
|
const tokens = [
|
|
...Object.entries(chain.cwTokens ?? {})
|
|
.filter(([, address]) => typeof address === 'string' && address.trim() !== '')
|
|
.map(([symbol, address]) => ({ symbol, address })),
|
|
...Object.entries(chain.gasMirrors ?? {})
|
|
.filter(([, address]) => typeof address === 'string' && address.trim() !== '')
|
|
.map(([symbol, address]) => ({
|
|
symbol,
|
|
address,
|
|
assetClass: 'gas_native',
|
|
familyKey: gasFamilyByMirror.get(symbol),
|
|
})),
|
|
];
|
|
|
|
if (tokens.length === 0) continue;
|
|
|
|
rows.push({
|
|
chainId: Number(chainIdText),
|
|
chainIdText,
|
|
name: chain.name || `Chain ${chainIdText}`,
|
|
tokens: tokens.sort((a, b) => a.symbol.localeCompare(b.symbol)),
|
|
});
|
|
}
|
|
|
|
return rows.sort((a, b) => a.chainId - b.chainId);
|
|
}
|
|
|
|
export function buildGasRegistryChains(data: DeploymentStatusFile): GasRegistryChain[] {
|
|
const rows: GasRegistryChain[] = [];
|
|
|
|
for (const [chainIdText, chain] of Object.entries(data.chains ?? {})) {
|
|
const familyMap = new Map<
|
|
string,
|
|
{
|
|
familyKey: string;
|
|
mirroredSymbol: string;
|
|
mirrorAddress?: string;
|
|
dodoPmm: Array<Record<string, unknown>>;
|
|
referenceVenues: Array<Record<string, unknown>>;
|
|
}
|
|
>();
|
|
|
|
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 existing = familyMap.get(familyKey) ?? {
|
|
familyKey,
|
|
mirroredSymbol,
|
|
mirrorAddress: chain.gasMirrors?.[mirroredSymbol],
|
|
dodoPmm: [],
|
|
referenceVenues: [],
|
|
};
|
|
existing.dodoPmm.push(pool);
|
|
familyMap.set(familyKey, existing);
|
|
}
|
|
|
|
for (const venue of chain.gasReferenceVenues ?? []) {
|
|
const familyKey = typeof venue.familyKey === 'string' ? venue.familyKey : '';
|
|
if (!familyKey) continue;
|
|
const existing = familyMap.get(familyKey) ?? {
|
|
familyKey,
|
|
mirroredSymbol: typeof venue.base === 'string' ? venue.base : '',
|
|
mirrorAddress: typeof venue.base === 'string' ? chain.gasMirrors?.[venue.base] : undefined,
|
|
dodoPmm: [],
|
|
referenceVenues: [],
|
|
};
|
|
existing.referenceVenues.push(venue);
|
|
familyMap.set(familyKey, existing);
|
|
}
|
|
|
|
const families = Array.from(familyMap.values()).sort((a, b) => a.familyKey.localeCompare(b.familyKey));
|
|
if (families.length === 0) continue;
|
|
|
|
rows.push({
|
|
chainId: Number(chainIdText),
|
|
chainIdText,
|
|
name: chain.name || `Chain ${chainIdText}`,
|
|
families,
|
|
});
|
|
}
|
|
|
|
return rows.sort((a, b) => a.chainId - b.chainId);
|
|
}
|