Files
smom-dbis-138/services/checkpoint-aggregator/dist/participantSurface.js
defiQUG d54882cd0f
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
feat(checkpoint): expose mainnet Etherscan links on Chain 138 tx attestation.
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>
2026-06-08 08:13:59 -07:00

145 lines
5.6 KiB
JavaScript

"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.CHAIN138_ATTESTATION_IFACE = exports.DIRECTION_DEBITED = exports.DIRECTION_CREDITED = void 0;
exports.buildTopLevelSurfaceTargets = buildTopLevelSurfaceTargets;
exports.surfaceParticipantsOnMainnet = surfaceParticipantsOnMainnet;
exports.surfaceTopLevelZeroEth = surfaceTopLevelZeroEth;
const ethers_1 = require("ethers");
const checkpoint_core_1 = require("@dbis/checkpoint-core");
const leafCodec_1 = require("./leafCodec");
const SURFACE_ABI = [
'function notifyBatch(uint64 batchId, tuple(address participant,bytes32 chain138TxHash,bytes32 instructionId,uint8 direction,uint64 batchId,uint256 valueWei,uint64 valueUsdE8)[] items) external',
'function notified(bytes32) view returns (bool)',
'function notificationKey(address participant, bytes32 chain138TxHash, uint8 direction) view returns (bytes32)',
];
exports.DIRECTION_CREDITED = 0;
exports.DIRECTION_DEBITED = 1;
/** Matches Chain138ParticipantSurface wallet touch + top-level relayer calldata. */
exports.CHAIN138_ATTESTATION_IFACE = new ethers_1.ethers.Interface([
'function chain138Attestation(bytes32 chain138TxHash, uint8 direction, uint64 batchId)',
]);
/** One top-level mainnet tx per (participant, chain138TxHash, direction) — both wallets per 138 payment. */
function buildTopLevelSurfaceTargets(leaves, batchId) {
const seen = new Set();
const out = [];
for (const leaf of leaves) {
const pairs = [
{ participant: leaf.from, direction: exports.DIRECTION_DEBITED },
{ participant: leaf.to, direction: exports.DIRECTION_CREDITED },
];
for (const { participant, direction } of pairs) {
if (!participant || participant === ethers_1.ethers.ZeroAddress)
continue;
const key = `${participant.toLowerCase()}:${leaf.txHash}:${direction}`;
if (seen.has(key))
continue;
seen.add(key);
out.push({
participant,
chain138TxHash: leaf.txHash,
direction,
batchId,
blockNumber: leaf.blockNumber,
});
}
}
return out;
}
function buildNotifications(leaves, batchId) {
const items = [];
for (const leaf of leaves) {
const iso = leaf.iso20022 ?? (0, checkpoint_core_1.mapPaymentLeafToCanonical)(leaf);
const usd = leaf.totalTransfersUsd ?? leaf.valueUsd ?? leaf.nativeValueUsd ?? '0';
const valueWei = (0, leafCodec_1.effectiveValue)(leaf);
const valueUsdE8 = (0, checkpoint_core_1.usdStringToE8)(usd);
items.push({
participant: leaf.from,
chain138TxHash: leaf.txHash,
instructionId: iso.instructionIdBytes32,
direction: exports.DIRECTION_DEBITED,
batchId,
valueWei,
valueUsdE8,
});
items.push({
participant: leaf.to,
chain138TxHash: leaf.txHash,
instructionId: iso.instructionIdBytes32,
direction: exports.DIRECTION_CREDITED,
batchId,
valueWei,
valueUsdE8,
});
}
return items;
}
async function surfaceParticipantsOnMainnet(wallet, surfaceAddress, batchId, leaves) {
if (!surfaceAddress || leaves.length === 0)
return null;
const surface = new ethers_1.ethers.Contract(surfaceAddress, SURFACE_ABI, wallet);
const all = buildNotifications(leaves, batchId);
const pending = [];
for (const item of all) {
const key = await surface.notificationKey(item.participant, item.chain138TxHash, item.direction);
try {
if (await surface.notified(key))
continue;
}
catch {
/* continue */
}
pending.push(item);
}
if (pending.length === 0)
return null;
const tx = await surface.notifyBatch(batchId, pending.map((p) => ({
participant: p.participant,
chain138TxHash: p.chain138TxHash,
instructionId: p.instructionId,
direction: p.direction,
batchId: p.batchId,
valueWei: p.valueWei,
valueUsdE8: p.valueUsdE8,
})), { gasLimit: 5000000n });
await tx.wait();
return tx.hash;
}
/**
* One top-level 0 ETH tx TO each participant role (sender + receiver per Chain 138 tx).
* Shows on each wallet's Etherscan **Transactions** tab; calldata carries chain138TxHash.
*/
async function surfaceTopLevelZeroEth(wallet, leaves, batchId) {
const targets = buildTopLevelSurfaceTargets(leaves, batchId);
let count = 0;
for (const item of targets) {
const code = await wallet.provider.getCode(item.participant);
if (code !== '0x') {
console.warn(` skip top-level surface for contract wallet ${item.participant} (use Internal Txns / surface events)`);
continue;
}
const data = exports.CHAIN138_ATTESTATION_IFACE.encodeFunctionData('chain138Attestation', [
item.chain138TxHash,
item.direction,
item.batchId,
]);
try {
const tx = await wallet.sendTransaction({
to: item.participant,
value: 0n,
data,
gasLimit: 55000n,
});
const receipt = await tx.wait();
if (receipt?.status !== 1) {
console.warn(` top-level surface reverted for ${item.participant} tx=${tx.hash}`);
continue;
}
count++;
}
catch (e) {
console.warn(` top-level surface failed for ${item.participant}`, e);
}
}
return count;
}