Files
proxmox/scripts/verify/check-publication-pack-explorer-status.mjs
defiQUG dbd517b279 Sync workspace: config, docs, scripts, CI, operator rules, and submodule pointers.
- Update dbis_core, cross-chain-pmm-lps, explorer-monorepo, metamask-integration, pr-workspace/chains
- Omit embedded publish git dirs and empty placeholders from index

Made-with: Cursor
2026-04-12 06:12:20 -07:00

156 lines
5.0 KiB
JavaScript

#!/usr/bin/env node
import fs from 'fs';
import path from 'path';
import https from 'https';
const repoRoot = path.resolve(path.dirname(new URL(import.meta.url).pathname), '..', '..');
const packsRoot = path.join(repoRoot, 'reports', 'status', 'publication-packs');
const outJson = path.join(repoRoot, 'reports', 'status', 'publication-pack-explorer-status.json');
const outMd = path.join(repoRoot, 'docs', '11-references', 'PUBLICATION_PACK_EXPLORER_STATUS.md');
const chainIdToApi = {
'1': 'https://api.etherscan.io/v2/api',
'10': 'https://api.etherscan.io/v2/api',
'56': 'https://api.etherscan.io/v2/api',
'137': 'https://api.etherscan.io/v2/api',
'8453': 'https://api.etherscan.io/v2/api',
};
const apiKey = process.env.ETHERSCAN_API_KEY || '';
if (!apiKey) {
console.error('ETHERSCAN_API_KEY is required for pack explorer status checks.');
process.exit(1);
}
function ensureDir(filePath) {
fs.mkdirSync(path.dirname(filePath), { recursive: true });
}
function getJson(url) {
return new Promise((resolve, reject) => {
https.get(url, {
headers: {
'user-agent': 'proxmox-publication-status-checker/1.0',
accept: 'application/json',
},
}, (res) => {
let data = '';
res.on('data', (chunk) => {
data += chunk;
});
res.on('end', () => {
try {
resolve(JSON.parse(data));
} catch (err) {
reject(new Error(`Invalid JSON from ${url}: ${err.message}`));
}
});
}).on('error', reject);
});
}
function sleep(ms) {
return new Promise((resolve) => setTimeout(resolve, ms));
}
async function fetchStatus(chainId, address) {
const api = chainIdToApi[chainId];
if (!api) return { status: 'unsupported', detail: 'No configured explorer API' };
const url = `${api}?chainid=${chainId}&module=contract&action=getsourcecode&address=${address}&apikey=${apiKey}`;
for (let attempt = 1; attempt <= 4; attempt += 1) {
const json = await getJson(url);
const result = Array.isArray(json.result) ? json.result[0] : null;
if (result) {
const source = (result.SourceCode || '').trim();
const name = (result.ContractName || '').trim();
if (source || name) {
return {
status: 'verified',
contractName: name || null,
detail: result.CompilerVersion || 'verified',
};
}
return { status: 'unverified', detail: result.ABI || 'No source metadata' };
}
const message = typeof json.message === 'string' ? json.message : '';
const detail = typeof json.result === 'string' && json.result
? `${message}: ${json.result}`
: (message || 'No result');
const retryable = /rate limit|timeout|temporarily unavailable|busy/i.test(detail);
if (!retryable || attempt === 4) {
return { status: 'unknown', detail };
}
await sleep(400 * attempt);
}
return { status: 'unknown', detail: 'Status check exhausted retries' };
}
async function main() {
const packDirs = fs.readdirSync(packsRoot).sort();
const report = {
generatedAt: new Date().toISOString(),
packs: [],
};
for (const dir of packDirs) {
const packPath = path.join(packsRoot, dir, 'pack.json');
if (!fs.existsSync(packPath)) continue;
const pack = JSON.parse(fs.readFileSync(packPath, 'utf8'));
const entries = [];
for (const entry of pack.entries) {
try {
const status = await fetchStatus(entry.chainId, entry.address);
entries.push({ ...entry, explorerStatus: status.status, explorerDetail: status.detail, explorerContractName: status.contractName || null });
} catch (err) {
entries.push({ ...entry, explorerStatus: 'error', explorerDetail: err.message, explorerContractName: null });
}
}
const counts = entries.reduce((acc, entry) => {
acc[entry.explorerStatus] = (acc[entry.explorerStatus] || 0) + 1;
return acc;
}, {});
report.packs.push({
slug: dir,
chainId: pack.chainId,
chainName: pack.chainName,
explorer: pack.explorer,
counts,
entries,
});
}
const rows = report.packs.map((pack) => {
const verified = pack.counts.verified || 0;
const unverified = pack.counts.unverified || 0;
const unknown = pack.counts.unknown || 0;
const unsupported = pack.counts.unsupported || 0;
const error = pack.counts.error || 0;
return `| ${pack.chainId} | ${pack.chainName} | ${verified} | ${unverified} | ${unknown} | ${unsupported} | ${error} | ${pack.explorer} |`;
}).join('\n');
const md = `# Publication Pack Explorer Status
**Generated:** ${report.generatedAt}
Live explorer verification status for the grouped publication packs.
| Chain ID | Chain | Verified | Unverified | Unknown | Unsupported | Errors | Explorer |
| --- | --- | ---: | ---: | ---: | ---: | ---: | --- |
${rows}
`;
ensureDir(outJson);
ensureDir(outMd);
fs.writeFileSync(outJson, JSON.stringify(report, null, 2) + '\n');
fs.writeFileSync(outMd, md + '\n');
console.log(`Wrote:\n- ${outJson}\n- ${outMd}`);
}
main().catch((err) => {
console.error(err);
process.exit(1);
});