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>
236 lines
9.5 KiB
JavaScript
236 lines
9.5 KiB
JavaScript
"use strict";
|
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
exports.linksFromBatchMeta = linksFromBatchMeta;
|
|
exports.resolveTxMainnetLinks = resolveTxMainnetLinks;
|
|
const ethers_1 = require("ethers");
|
|
const config_1 = require("./config");
|
|
const iso20022LogDecode_1 = require("./iso20022LogDecode");
|
|
const participantSurfaceLogDecode_1 = require("./participantSurfaceLogDecode");
|
|
const TRANSACTION_MIRRORED_TOPIC0 = '0xc25ce5062c7e42c68fa21fe088d21e609cc0c61b8bec3180681363bb5cf02a9e';
|
|
const PARTICIPANT_CREDITED_TOPIC0 = '0x853ebad0824f583553bcb4360a0df947d39654e86aba8d7c0625499cbf09f0d5';
|
|
const PARTICIPANT_DEBITED_TOPIC0 = '0xf122cad7c8ada37ddd6b6ddca0982b9f7be689cde15eedfd767cc207fa7dc2c8';
|
|
const CHECKPOINT_SUBMITTED_TOPIC0 = ethers_1.ethers.id('CheckpointSubmitted(uint64,uint256,bytes32,uint16,uint32,uint64,address,bytes32)');
|
|
const META_LAYER_MAP = [
|
|
{ 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' },
|
|
];
|
|
function linksFromBatchMeta(raw) {
|
|
const meta = raw?.mainnetAttestation;
|
|
if (!meta)
|
|
return [];
|
|
const links = [];
|
|
for (const row of META_LAYER_MAP) {
|
|
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: etherscanTx(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: etherscanTx(hash),
|
|
});
|
|
}
|
|
}
|
|
return links;
|
|
}
|
|
function etherscanTx(hash) {
|
|
return `https://etherscan.io/tx/${hash}`;
|
|
}
|
|
function logRows(logs) {
|
|
return logs.map((log) => ({
|
|
topics: log.topics,
|
|
transactionHash: log.transactionHash,
|
|
blockNumber: log.blockNumber.toString(),
|
|
logIndex: log.index.toString(),
|
|
data: log.data,
|
|
}));
|
|
}
|
|
function uniqLinks(links) {
|
|
const seen = new Set();
|
|
return links.filter((l) => {
|
|
const k = `${l.layer}:${l.mainnetTxHash}:${l.logIndex ?? ''}`;
|
|
if (seen.has(k))
|
|
return false;
|
|
seen.add(k);
|
|
return true;
|
|
});
|
|
}
|
|
const LOG_LOOKBACK_BLOCKS = parseInt(process.env.CHECKPOINT_MAINNET_LOGS_LOOKBACK || '120000', 10);
|
|
async function resolveFromBlock(provider, batchId) {
|
|
const head = await provider.getBlockNumber();
|
|
const fallback = Math.max(0, head - LOG_LOOKBACK_BLOCKS);
|
|
if (!batchId || batchId === '0' || !config_1.checkpointIndexerConfig.checkpointProxy)
|
|
return fallback;
|
|
try {
|
|
const batchTopic = ethers_1.ethers.zeroPadValue(ethers_1.ethers.toBeHex(BigInt(batchId)), 32);
|
|
const hubLogs = await provider.getLogs({
|
|
address: config_1.checkpointIndexerConfig.checkpointProxy,
|
|
fromBlock: fallback,
|
|
toBlock: 'latest',
|
|
topics: [CHECKPOINT_SUBMITTED_TOPIC0, batchTopic],
|
|
});
|
|
if (hubLogs.length) {
|
|
return Math.max(0, hubLogs[0].blockNumber - 10);
|
|
}
|
|
}
|
|
catch {
|
|
/* use fallback */
|
|
}
|
|
return fallback;
|
|
}
|
|
async function resolveTxMainnetLinks(provider, txHash, batchId) {
|
|
const links = [];
|
|
const want = txHash.toLowerCase();
|
|
const txTopic = ethers_1.ethers.zeroPadValue(txHash, 32);
|
|
const fromBlock = await resolveFromBlock(provider, batchId);
|
|
if (batchId && batchId !== '0' && config_1.checkpointIndexerConfig.checkpointProxy) {
|
|
try {
|
|
const batchTopic = ethers_1.ethers.zeroPadValue(ethers_1.ethers.toBeHex(BigInt(batchId)), 32);
|
|
const hubLogs = await provider.getLogs({
|
|
address: config_1.checkpointIndexerConfig.checkpointProxy,
|
|
fromBlock,
|
|
toBlock: 'latest',
|
|
topics: [CHECKPOINT_SUBMITTED_TOPIC0, batchTopic],
|
|
});
|
|
for (const log of hubLogs) {
|
|
links.push({
|
|
layer: 'checkpointHub',
|
|
label: `Checkpoint batch #${batchId}`,
|
|
mainnetTxHash: log.transactionHash,
|
|
mainnetExplorerUrl: etherscanTx(log.transactionHash),
|
|
contractAddress: config_1.checkpointIndexerConfig.checkpointProxy,
|
|
logIndex: log.index,
|
|
});
|
|
}
|
|
}
|
|
catch {
|
|
/* non-fatal — RPC may prune old logs */
|
|
}
|
|
}
|
|
try {
|
|
const mirrorLogs = await provider.getLogs({
|
|
address: config_1.checkpointIndexerConfig.legacyMirror,
|
|
fromBlock,
|
|
toBlock: 'latest',
|
|
topics: [TRANSACTION_MIRRORED_TOPIC0, txTopic],
|
|
});
|
|
for (const log of mirrorLogs) {
|
|
links.push({
|
|
layer: 'transactionMirror',
|
|
label: 'Transaction mirror',
|
|
mainnetTxHash: log.transactionHash,
|
|
mainnetExplorerUrl: etherscanTx(log.transactionHash),
|
|
contractAddress: config_1.checkpointIndexerConfig.legacyMirror,
|
|
logIndex: log.index,
|
|
});
|
|
}
|
|
}
|
|
catch {
|
|
/* non-fatal */
|
|
}
|
|
if (config_1.checkpointIndexerConfig.addressActivityRegistry) {
|
|
for (const [topic0, role] of [
|
|
[PARTICIPANT_CREDITED_TOPIC0, 'credited'],
|
|
[PARTICIPANT_DEBITED_TOPIC0, 'debited'],
|
|
]) {
|
|
try {
|
|
const regLogs = await provider.getLogs({
|
|
address: config_1.checkpointIndexerConfig.addressActivityRegistry,
|
|
fromBlock,
|
|
toBlock: 'latest',
|
|
topics: [topic0, txTopic],
|
|
});
|
|
for (const log of regLogs) {
|
|
links.push({
|
|
layer: 'activityRegistryV1',
|
|
label: `Activity registry V1 (${role})`,
|
|
mainnetTxHash: log.transactionHash,
|
|
mainnetExplorerUrl: etherscanTx(log.transactionHash),
|
|
contractAddress: config_1.checkpointIndexerConfig.addressActivityRegistry,
|
|
logIndex: log.index,
|
|
meta: { role },
|
|
});
|
|
}
|
|
}
|
|
catch {
|
|
/* non-fatal */
|
|
}
|
|
}
|
|
}
|
|
if (config_1.checkpointIndexerConfig.addressActivityRegistryV2) {
|
|
try {
|
|
const v2Logs = await provider.getLogs({
|
|
address: config_1.checkpointIndexerConfig.addressActivityRegistryV2,
|
|
fromBlock,
|
|
toBlock: 'latest',
|
|
topics: [iso20022LogDecode_1.PAYMENT_ATTESTED_TOPIC0, txTopic],
|
|
});
|
|
const decoded = (0, iso20022LogDecode_1.decodePaymentAttestedLogs)({ status: '1', result: logRows(v2Logs) });
|
|
for (const row of decoded) {
|
|
if (row.chain138TxHash.toLowerCase() !== want)
|
|
continue;
|
|
links.push({
|
|
layer: 'activityRegistryV2',
|
|
label: 'ISO-20022 PaymentAttested (V2)',
|
|
mainnetTxHash: row.mainnetTxHash,
|
|
mainnetExplorerUrl: row.mainnetExplorerUrl,
|
|
contractAddress: config_1.checkpointIndexerConfig.addressActivityRegistryV2,
|
|
logIndex: row.logIndex ? parseInt(row.logIndex, 10) : undefined,
|
|
meta: { instructionId: row.instructionId, uetr: row.uetr },
|
|
});
|
|
}
|
|
}
|
|
catch {
|
|
/* non-fatal */
|
|
}
|
|
}
|
|
if (config_1.checkpointIndexerConfig.participantSurface) {
|
|
try {
|
|
const surfaceLogs = await provider.getLogs({
|
|
address: config_1.checkpointIndexerConfig.participantSurface,
|
|
fromBlock,
|
|
toBlock: 'latest',
|
|
topics: [participantSurfaceLogDecode_1.CHAIN138_ACTIVITY_NOTIFIED_TOPIC0, null, txTopic],
|
|
});
|
|
const decoded = (0, participantSurfaceLogDecode_1.decodeChain138ActivityNotifiedLogs)({
|
|
status: '1',
|
|
result: logRows(surfaceLogs),
|
|
});
|
|
for (const row of decoded) {
|
|
if (row.chain138TxHash.toLowerCase() !== want)
|
|
continue;
|
|
links.push({
|
|
layer: 'participantSurface',
|
|
label: 'Participant surface notification',
|
|
mainnetTxHash: row.mainnetTxHash,
|
|
mainnetExplorerUrl: row.mainnetExplorerUrl,
|
|
contractAddress: config_1.checkpointIndexerConfig.participantSurface,
|
|
logIndex: row.logIndex ? parseInt(row.logIndex, 10) : undefined,
|
|
meta: {
|
|
participant: row.participant,
|
|
direction: String(row.direction),
|
|
},
|
|
});
|
|
}
|
|
}
|
|
catch {
|
|
/* non-fatal */
|
|
}
|
|
}
|
|
return uniqLinks(links);
|
|
}
|