Files
smom-dbis-138/services/checkpoint-indexer/src/server.ts
defiQUG c336809676
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
Add mainnet checkpoint stack: ISO attestation, participant Etherscan surface, and services.
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>
2026-05-25 00:30:45 -07:00

321 lines
12 KiB
TypeScript

import express from 'express';
import * as fs from 'fs';
import * as path from 'path';
import { ethers } from 'ethers';
import { checkpointIndexerConfig as cfg } from './config';
import { enrichLeaves, type LeafRecord } from './tokenEnrich';
import { merkleVerifyValueWei } from '@dbis/checkpoint-core';
import { merkleProofs, paymentLeafHash } from './merkle';
import { jsonSafe } from './serialize';
import {
buildAddressIndex,
getAddressTransactions,
summarizeAddressActivity,
type AddressIndex,
} from './addressIndex';
import { chainlistSupplement, etherscanV2Api } from './etherscanV2Shim';
import { chain138ExplorerBase, chain138ExplorerTxUrl } from './chain138Explorer';
const CHECKPOINT_ABI = [
'function getLatestBatchId() view returns (uint64)',
'function latestCheckpointBlock() view returns (uint256)',
'function getLatestCheckpoint() view returns (tuple(uint64,uint64,uint64,uint256,uint256,uint256,bytes32,bytes32,bytes32,bytes32,uint16,uint32,uint64,address,bytes32))',
'function getCheckpoint(uint64) view returns (tuple(uint64,uint64,uint64,uint256,uint256,uint256,bytes32,bytes32,bytes32,bytes32,uint16,uint32,uint64,address,bytes32))',
'function isTxIncluded(bytes32) view returns (bool,uint64)',
];
const MIRROR_ABI = ['function getMirroredTransactionCount() view returns (uint256)'];
const app = express();
const mainnet = new ethers.JsonRpcProvider(cfg.mainnetRpc);
const chain138 = new ethers.JsonRpcProvider(cfg.chain138Rpc);
const hub = cfg.checkpointProxy ? new ethers.Contract(cfg.checkpointProxy, CHECKPOINT_ABI, mainnet) : null;
const mirror = new ethers.Contract(cfg.legacyMirror, MIRROR_ABI, mainnet);
let addressIndexCache: AddressIndex | null = null;
let addressIndexBuiltAt = 0;
const ADDRESS_INDEX_TTL_MS = 60_000;
function getAddressIndex(): AddressIndex {
const now = Date.now();
if (!addressIndexCache || now - addressIndexBuiltAt > ADDRESS_INDEX_TTL_MS) {
addressIndexCache = buildAddressIndex(cfg.batchPayloadDir);
addressIndexBuiltAt = now;
}
return addressIndexCache;
}
function loadBatchPayload(batchId: string): { batchId?: string; leaves?: LeafRecord[] } | null {
const p = path.join(cfg.batchPayloadDir, `batch-${batchId}.json`);
if (!fs.existsSync(p)) return null;
return JSON.parse(fs.readFileSync(p, 'utf8')) as { batchId?: string; leaves?: LeafRecord[] };
}
async function payloadWithEnrichedLeaves(
batchId: string,
raw: { batchId?: string; leaves?: LeafRecord[]; chainId?: number; paymentsRoot?: string } | null
): Promise<object | null> {
if (!raw?.leaves?.length) return raw;
const chainId = raw.chainId ?? 138;
let leaves = raw.leaves;
if (cfg.enrichTokenTransfers || cfg.usdEnrichEnabled) {
leaves = await enrichLeaves(leaves, cfg.blockscoutApi);
}
const hashes = leaves.map((l) =>
paymentLeafHash(chainId, {
txHash: String(l.txHash),
from: String(l.from),
to: String(l.to),
value: String(l.value ?? '0'),
nativeValueWei: l.nativeValueWei != null ? String(l.nativeValueWei) : undefined,
onChainValueWei: l.onChainValueWei != null ? String(l.onChainValueWei) : undefined,
tokenValue: l.tokenValue != null ? String(l.tokenValue) : undefined,
blockNumber: Number(l.blockNumber),
blockTimestamp: Number(l.blockTimestamp),
gasUsed: String(l.gasUsed ?? '0'),
success: Boolean(l.success),
})
);
const { root, proofs } = merkleProofs(hashes);
const leavesWithProofs = leaves.map((l, i) => ({
...l,
leafHash: hashes[i],
merkleProof: proofs[i],
}));
return {
...raw,
batchId: raw.batchId ?? batchId,
leaves: leavesWithProofs,
merkleRootComputed: root,
merkleRootMatches: raw.paymentsRoot ? root.toLowerCase() === String(raw.paymentsRoot).toLowerCase() : null,
walletAttestationCopy:
'Balances and payments as of Chain 138 checkpoint block in this batch, attested on Ethereum mainnet.',
};
}
app.get('/health', (_req, res) => {
res.json({ ok: true, proxy: cfg.checkpointProxy || null, batchDir: cfg.batchPayloadDir });
});
/** Etherscan V2 supplement for Chain 138 (unsupported on official chainlist). */
app.get('/v2/chainlist-supplement', chainlistSupplement);
app.get('/v2/api', (req, res) => {
void etherscanV2Api(req, res);
});
/** Public batch JSON for contentURI resolution (CHECKPOINT_PAYLOAD_PUBLIC_URL). */
app.get('/v1/payload/batch-:batchId.json', (req, res) => {
const raw = loadBatchPayload(req.params.batchId);
if (!raw) return res.status(404).json({ error: 'batch not found' });
res.type('json').send(JSON.stringify(raw, null, 2));
});
app.get('/v1/checkpoint/latest', async (_req, res) => {
if (!hub) return res.status(503).json({ error: 'CHAIN138_MAINNET_CHECKPOINT_PROXY not set' });
try {
const batchId = await hub.getLatestBatchId();
const header = await hub.getLatestCheckpoint();
let v1Count = 0n;
try {
v1Count = await mirror.getMirroredTransactionCount();
} catch {
v1Count = 0n;
}
const raw = batchId > 0n ? loadBatchPayload(batchId.toString()) : null;
const payload = raw ? await payloadWithEnrichedLeaves(batchId.toString(), raw) : null;
res.json({
source: 'v2',
batchId: batchId.toString(),
header: jsonSafe(header),
legacy: { mirrorCount: v1Count.toString(), tether: cfg.legacyTether },
payload,
});
} catch (e) {
res.status(500).json({ error: String(e) });
}
});
app.get('/v1/checkpoint/:batchId', async (req, res) => {
if (!hub) return res.status(503).json({ error: 'proxy not set' });
try {
const batchId = req.params.batchId;
const header = await hub.getCheckpoint(BigInt(batchId));
const raw = loadBatchPayload(batchId);
const payload = raw ? await payloadWithEnrichedLeaves(batchId, raw) : null;
res.json({ batchId, header: jsonSafe(header), payload });
} catch (e) {
res.status(500).json({ error: String(e) });
}
});
app.get('/v1/tx/:txHash/attestation', async (req, res) => {
if (!hub) return res.status(503).json({ error: 'proxy not set' });
try {
const txHash = req.params.txHash;
const [included, batchId] = await hub.isTxIncluded(txHash);
const raw = included && batchId > 0n ? loadBatchPayload(batchId.toString()) : null;
const payload = raw ? await payloadWithEnrichedLeaves(batchId.toString(), raw) : null;
res.json({ txHash, included, batchId: batchId.toString(), payload });
} catch (e) {
res.status(500).json({ error: String(e) });
}
});
/** Wallet helper: verify leaf against on-chain hub (client may also use @dbis/checkpoint-sdk). */
app.get('/v1/checkpoint/:batchId/verify/:leafIndex', async (req, res) => {
if (!hub) return res.status(503).json({ error: 'proxy not set' });
try {
const batchId = req.params.batchId;
const leafIndex = parseInt(req.params.leafIndex, 10);
const raw = loadBatchPayload(batchId);
if (!raw?.leaves?.length || leafIndex < 0 || leafIndex >= raw.leaves.length) {
return res.status(404).json({ error: 'leaf not found' });
}
const payload = await payloadWithEnrichedLeaves(batchId, raw);
const leaves = (payload as { leaves?: LeafRecord[] }).leaves;
const leaf = leaves?.[leafIndex];
if (!leaf || !(leaf as LeafRecord).merkleProof) {
return res.status(404).json({ error: 'proof missing' });
}
const VERIFY_ABI = [
'function verifyPaymentInBatch(uint64 batchId, tuple(bytes32,address,address,uint256,uint256,uint64,uint256,bool) leaf, bytes32[] proof) view returns (bool)',
];
const verifyHub = new ethers.Contract(cfg.checkpointProxy, VERIFY_ABI, mainnet);
const l = leaf as LeafRecord;
const ok = await verifyHub.verifyPaymentInBatch(
BigInt(batchId),
[
l.txHash,
l.from,
l.to,
merkleVerifyValueWei(l),
BigInt(String(l.blockNumber)),
BigInt(String(l.blockTimestamp)),
BigInt(String(l.gasUsed ?? 0)),
Boolean(l.success),
],
(l as { merkleProof: string[] }).merkleProof
);
res.json({ batchId, leafIndex, verified: ok, leaf });
} catch (e) {
res.status(500).json({ error: String(e) });
}
});
app.get('/v1/account/:address', async (req, res) => {
try {
const addr = req.params.address;
let blockTag: number | 'latest' = 'latest';
if (req.query.atBatch) {
if (!hub) return res.status(503).json({ error: 'proxy not set' });
const h = await hub.getCheckpoint(BigInt(String(req.query.atBatch)));
blockTag = Number(h.checkpointBlock);
} else if (hub) {
blockTag = Number(await hub.latestCheckpointBlock());
}
const balance = await chain138.getBalance(addr, blockTag);
const index = getAddressIndex();
const rows = index.byAddress[addr.toLowerCase()] ?? [];
const activity = summarizeAddressActivity(rows);
res.json({
address: addr,
chainId: 138,
blockTag,
balanceWei: balance.toString(),
attestedActivity: activity,
mainnetSurfaces: {
transactionMirror: cfg.legacyMirror,
addressActivityRegistry: cfg.addressActivityRegistry || null,
checkpointHub: cfg.checkpointProxy || null,
},
walletAttestationCopy:
'Native balance as of Chain 138 checkpoint block. Attested payments (ETH + USD) via checkpoint batches on Ethereum mainnet.',
});
} catch (e) {
res.status(500).json({ error: String(e) });
}
});
app.get('/v1/account/:address/transactions', async (req, res) => {
try {
const addr = req.params.address;
const limit = Math.min(parseInt(String(req.query.limit ?? '50'), 10) || 50, 200);
const offset = parseInt(String(req.query.offset ?? '0'), 10) || 0;
const index = getAddressIndex();
const { total, items } = getAddressTransactions(index, addr, limit, offset);
res.json({
address: addr,
chainId: 138,
total,
limit,
offset,
items,
indexMeta: {
builtAt: index.builtAt,
batchCount: index.batchCount,
txCount: index.txCount,
},
});
} catch (e) {
res.status(500).json({ error: String(e) });
}
});
app.get('/v1/account/:address/activity', async (req, res) => {
try {
const addr = req.params.address;
const index = getAddressIndex();
const rows = index.byAddress[addr.toLowerCase()] ?? [];
const summary = summarizeAddressActivity(rows);
res.json({
address: addr,
chainId: 138,
summary,
recent: rows.slice(0, 25).map((r) => ({
...r,
chain138ExplorerUrl: chain138ExplorerTxUrl(r.txHash),
})),
chain138Explorer: {
base: chain138ExplorerBase(),
txUrlTemplate: `${chain138ExplorerBase()}/tx/{txHash}`,
},
etherscan: {
transactionMirrorEvents: `https://etherscan.io/address/${cfg.legacyMirror}#events`,
addressActivityEvents: cfg.addressActivityRegistry
? `https://etherscan.io/address/${cfg.addressActivityRegistry}#events`
: null,
},
});
} catch (e) {
res.status(500).json({ error: String(e) });
}
});
app.get('/v1/tx/:txHash/logs', async (req, res) => {
try {
const txHash = req.params.txHash;
const receipt = await chain138.getTransactionReceipt(txHash);
if (!receipt) return res.status(404).json({ error: 'receipt not found on chain 138' });
res.json({
txHash,
chainId: 138,
blockNumber: receipt.blockNumber,
status: receipt.status,
gasUsed: receipt.gasUsed.toString(),
logCount: receipt.logs.length,
logs: receipt.logs.map((log, i) => ({
index: i,
address: log.address,
topics: log.topics,
data: log.data,
})),
});
} catch (e) {
res.status(500).json({ error: String(e) });
}
});
app.listen(cfg.port, () => {
console.log(`checkpoint-indexer listening on :${cfg.port}`);
});