feat(checkpoint): expose mainnet Etherscan links on Chain 138 tx attestation.
Some checks failed
CI/CD Pipeline / Solidity Contracts (push) Failing after 1m12s
CI/CD Pipeline / Security Scanning (push) Successful in 2m38s
CI/CD Pipeline / Lint and Format (push) Failing after 40s
CI/CD Pipeline / Terraform Validation (push) Failing after 21s
CI/CD Pipeline / Kubernetes Validation (push) Successful in 24s
HYBX OMNL TypeScript & anchor / token-aggregation build + reconcile artifact (push) Failing after 24s
Validation / validate-genesis (push) Successful in 26s
Validation / validate-terraform (push) Failing after 21s
Validation / validate-kubernetes (push) Failing after 9s
Validation / validate-smart-contracts (push) Failing after 9s
Validation / validate-security (push) Failing after 1m7s
Validation / validate-documentation (push) Failing after 15s

Resolve etherscanLinks from batch metadata and optional mainnet log scans; serve a fast local-batch attestation path for the explorer CT.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
defiQUG
2026-06-08 08:13:59 -07:00
parent 183f3cffc9
commit d54882cd0f
18 changed files with 1310 additions and 105 deletions

View File

@@ -6,9 +6,22 @@ import { logger } from '../../utils/logger';
const router: Router = Router();
function indexerBase(): string {
return (process.env.CHECKPOINT_INDEXER_URL || 'http://127.0.0.1:3099').replace(/\/$/, '');
const port = process.env.CHECKPOINT_INDEXER_PORT || '3100';
return (
process.env.CHECKPOINT_INDEXER_URL || `http://127.0.0.1:${port}`
).replace(/\/$/, '');
}
type MainnetAttestationLink = {
layer: string;
label: string;
mainnetTxHash: string;
mainnetExplorerUrl: string;
contractAddress?: string;
logIndex?: number;
meta?: Record<string, string>;
};
function batchDir(): string {
const custom = process.env.CHECKPOINT_BATCH_DIR?.trim();
if (custom) return custom;
@@ -49,6 +62,16 @@ function loadLocalAttestation(txHash: string): {
batchId: string;
batchTotalUsd?: string;
leaf: CheckpointLeaf | null;
batchMeta: {
mainnetAttestation?: {
hubTx?: string;
mirrorTx?: string;
activityRegistryV1Tx?: string;
activityRegistryV2Tx?: string;
participantSurfaceTx?: string;
participantTopLevelTxs?: string[];
};
} | null;
} | null {
const dir = batchDir();
if (!existsSync(dir)) return null;
@@ -64,11 +87,25 @@ function loadLocalAttestation(txHash: string): {
batchId?: string;
batchTotalUsd?: string;
leaves?: CheckpointLeaf[];
mainnetAttestation?: {
hubTx?: string;
mirrorTx?: string;
activityRegistryV1Tx?: string;
activityRegistryV2Tx?: string;
participantSurfaceTx?: string;
participantTopLevelTxs?: string[];
};
};
const leaf = findLeaf(raw, want);
if (!leaf) continue;
const batchId = String(raw.batchId ?? file.replace(/^batch-|\.json$/gi, ''));
return { included: true, batchId, batchTotalUsd: raw.batchTotalUsd, leaf };
return {
included: true,
batchId,
batchTotalUsd: raw.batchTotalUsd,
leaf,
batchMeta: raw,
};
} catch {
continue;
}
@@ -76,6 +113,51 @@ function loadLocalAttestation(txHash: string): {
return null;
}
function linksFromBatchMeta(raw: {
mainnetAttestation?: {
hubTx?: string;
mirrorTx?: string;
activityRegistryV1Tx?: string;
activityRegistryV2Tx?: string;
participantSurfaceTx?: string;
participantTopLevelTxs?: string[];
};
} | null): MainnetAttestationLink[] {
const meta = raw?.mainnetAttestation;
if (!meta) return [];
const rows: Array<{ key: keyof NonNullable<typeof meta>; layer: string; label: string }> = [
{ key: 'hubTx', layer: 'checkpointHub', label: 'Checkpoint batch (hub)' },
{ key: 'mirrorTx', layer: 'transactionMirror', label: 'Transaction mirror' },
{ key: 'activityRegistryV1Tx', layer: 'activityRegistryV1', label: 'Activity registry V1' },
{ key: 'activityRegistryV2Tx', layer: 'activityRegistryV2', label: 'ISO-20022 PaymentAttested (V2)' },
{ key: 'participantSurfaceTx', layer: 'participantSurface', label: 'Participant surface' },
];
const links: MainnetAttestationLink[] = [];
for (const row of rows) {
const hash = meta[row.key];
if (typeof hash === 'string' && /^0x[a-fA-F0-9]{64}$/.test(hash)) {
links.push({
layer: row.layer,
label: row.label,
mainnetTxHash: hash,
mainnetExplorerUrl: `https://etherscan.io/tx/${hash}`,
});
}
}
if (Array.isArray(meta.participantTopLevelTxs)) {
for (const hash of meta.participantTopLevelTxs) {
if (!/^0x[a-fA-F0-9]{64}$/.test(hash)) continue;
links.push({
layer: 'participantWallet',
label: 'Participant wallet (0 ETH attestation tx)',
mainnetTxHash: hash,
mainnetExplorerUrl: `https://etherscan.io/tx/${hash}`,
});
}
}
return links;
}
function leafResponse(leaf: CheckpointLeaf | null) {
if (!leaf) return null;
return {
@@ -92,8 +174,54 @@ function leafResponse(leaf: CheckpointLeaf | null) {
};
}
function mergeLinks(...groups: MainnetAttestationLink[][]): MainnetAttestationLink[] {
const out: MainnetAttestationLink[] = [];
const seen = new Set<string>();
for (const group of groups) {
for (const link of group) {
const key = `${link.layer}:${link.mainnetTxHash}`;
if (seen.has(key)) continue;
seen.add(key);
out.push(link);
}
}
return out;
}
async function fetchIndexerAttestation(
txHash: string,
timeoutMs: number,
): Promise<{
txHash?: string;
included?: boolean;
batchId?: string;
payload?: { leaves?: CheckpointLeaf[]; batchTotalUsd?: string };
etherscanLinks?: MainnetAttestationLink[];
etherscan?: { base?: string; txUrlTemplate?: string };
} | null> {
const url = `${indexerBase()}/v1/tx/${txHash}/attestation`;
try {
const ctrl = new AbortController();
const timer = setTimeout(() => ctrl.abort(), timeoutMs);
const upstream = await fetch(url, { signal: ctrl.signal });
clearTimeout(timer);
if (!upstream.ok) return null;
return (await upstream.json()) as {
txHash?: string;
included?: boolean;
batchId?: string;
payload?: { leaves?: CheckpointLeaf[]; batchTotalUsd?: string };
etherscanLinks?: MainnetAttestationLink[];
etherscan?: { base?: string; txUrlTemplate?: string };
};
} catch (e) {
logger.debug('checkpoint indexer proxy miss', { txHash, error: String(e) });
return null;
}
}
/**
* GET /checkpoint/tx/:txHash/attestation — USD-enriched attestation (indexer proxy + local batch fallback).
* GET /checkpoint/tx/:txHash/attestation — USD-enriched attestation (local batch first, indexer enrich).
*/
router.get('/checkpoint/tx/:txHash/attestation', async (req: Request, res: Response) => {
const txHash = String(req.params.txHash || '').trim();
@@ -101,34 +229,45 @@ router.get('/checkpoint/tx/:txHash/attestation', async (req: Request, res: Respo
return res.status(400).json({ error: 'invalid tx hash' });
}
const url = `${indexerBase()}/v1/tx/${txHash}/attestation`;
try {
const ctrl = new AbortController();
const timer = setTimeout(() => ctrl.abort(), 12_000);
const upstream = await fetch(url, { signal: ctrl.signal });
clearTimeout(timer);
if (upstream.ok) {
const raw = (await upstream.json()) as {
txHash?: string;
included?: boolean;
batchId?: string;
payload?: { leaves?: CheckpointLeaf[]; batchTotalUsd?: string };
};
const leaf = findLeaf(raw.payload ?? null, txHash);
return res.json({
txHash: raw.txHash ?? txHash,
included: Boolean(raw.included),
batchId: raw.batchId ?? '0',
batchTotalUsd: raw.payload?.batchTotalUsd,
leaf: leafResponse(leaf),
source: 'checkpoint-indexer',
});
}
} catch (e) {
logger.debug('checkpoint indexer proxy miss', { txHash, error: String(e) });
}
const etherscanMeta = {
base: 'https://etherscan.io',
txUrlTemplate: 'https://etherscan.io/tx/{txHash}',
};
const local = loadLocalAttestation(txHash);
const metaLinks = local ? linksFromBatchMeta(local.batchMeta) : [];
// Fast path: synced batch files on the explorer CT already carry mainnet tx hashes.
if (local && metaLinks.length > 0) {
return res.json({
txHash,
included: true,
batchId: local.batchId,
batchTotalUsd: local.batchTotalUsd,
leaf: leafResponse(local.leaf),
etherscanLinks: metaLinks,
etherscan: etherscanMeta,
source: 'checkpoint-batch-dir',
});
}
const indexerTimeoutMs = Number(process.env.CHECKPOINT_INDEXER_FETCH_TIMEOUT_MS || 2500);
const remote = await fetchIndexerAttestation(txHash, indexerTimeoutMs);
if (remote?.included) {
const leaf = findLeaf(remote.payload ?? null, txHash) ?? local?.leaf ?? null;
return res.json({
txHash: remote.txHash ?? txHash,
included: true,
batchId: remote.batchId ?? local?.batchId ?? '0',
batchTotalUsd: remote.payload?.batchTotalUsd ?? local?.batchTotalUsd,
leaf: leafResponse(leaf),
etherscanLinks: mergeLinks(metaLinks, remote.etherscanLinks ?? []),
etherscan: remote.etherscan ?? etherscanMeta,
source: local ? 'checkpoint-batch-dir+indexer' : 'checkpoint-indexer',
});
}
if (local) {
return res.json({
txHash,
@@ -136,6 +275,8 @@ router.get('/checkpoint/tx/:txHash/attestation', async (req: Request, res: Respo
batchId: local.batchId,
batchTotalUsd: local.batchTotalUsd,
leaf: leafResponse(local.leaf),
etherscanLinks: metaLinks,
etherscan: etherscanMeta,
source: 'checkpoint-batch-dir',
});
}
@@ -145,6 +286,8 @@ router.get('/checkpoint/tx/:txHash/attestation', async (req: Request, res: Respo
included: false,
batchId: '0',
leaf: null,
etherscanLinks: [],
etherscan: etherscanMeta,
source: 'none',
});
});

View File

@@ -123,6 +123,10 @@ export class ApiServer {
this.app.use('/static', express.static(publicPath, staticOpts));
this.app.use('/omnl/static', express.static(publicPath, staticOpts));
this.app.use('/reserve/static', express.static(publicPath, staticOpts));
this.app.use(
'/chain138-attestation',
express.static(path.join(publicPath, 'chain138-attestation'), staticOpts)
);
}
}