diff --git a/site/api/explorer.mjs b/site/api/explorer.mjs index 6ee22e6b1..4a534fd91 100644 --- a/site/api/explorer.mjs +++ b/site/api/explorer.mjs @@ -22,7 +22,18 @@ import { neon, num, tablePrefix, chainIdOf } from './_neon.mjs'; import { classify } from '../lib/explorer.mjs'; const STALE_AFTER_S = 30; -export const NETWORK_LABEL = 'Devnet 3'; +export const NETWORK_LABEL = 'the Igneum devnet'; +/** The served name of a network from the node's own name (the coordinator's ruling of 8 October 2026, 18:09 UK): igneum-devnet-4 reads + * "the Igneum 2.0 devnet" (the plan's wording, as the app, the site's badge and the manifest say it); any other suffix reads + * "the Igneum devnet N" or "the Igneum testnet N", so a future chain is never mislabelled. The API's network field stays the raw name. */ +export const NETWORK_NAMES = { 'igneum-devnet-4': 'the Igneum 2.0 devnet' }; +export function labelOf(network) { + const n = String(network || ''); + if (NETWORK_NAMES[n]) return NETWORK_NAMES[n]; + const m = n.match(/^igneum-(devnet|testnet|mainnet)(?:-(\d+))?$/); + if (!m) return NETWORK_LABEL; + return m[1] === 'mainnet' ? 'Igneum' : `the Igneum ${m[1]}${m[2] ? ' ' + m[2] : ''}`; +} const ROW = `hash, number, blue_score, daa_score, timestamp_ms, parents, parent_hashes, is_chain_block, vote_key_hash, miner_address, evm_miner, engine, tx_count, proof_records, subsidy_sompi, paid_sompi, selected_parent, color, received_at`; // the EVM's own hash for the block's number (the indexer's explorer_blocks): a block the observer called a chain block that a tip @@ -101,7 +112,7 @@ export function createHandler({ env = process.env, sql, evm = evmCall } = {}) { // the highest number the indexer has ever read, less three: a node re-syncing from a snapshot reports a low tip for a while // (10:21 UTC on 8 October 2026: the observer node restarted and its tip read 1,981 against 27,145 indexed) and must not unsettle the table settledBelow = head[0].indexed_max === null || head[0].indexed_max === undefined ? -1 : Number(head[0].indexed_max) - 3; - const base = { ok: true, now: new Date(now).toISOString(), network: s ? s.network : null, network_label: NETWORK_LABEL, chain_id: ix && ix.chain_id ? num(ix.chain_id) : (s ? chainIdOf(s.network) : null), stale: updated === null || (now - updated) / 1000 > STALE_AFTER_S, evm_rpc_configured: !!EVM, indexer }; + const base = { ok: true, now: new Date(now).toISOString(), network: s ? s.network : null, network_label: labelOf(s ? s.network : null), chain_id: ix && ix.chain_id ? num(ix.chain_id) : (s ? chainIdOf(s.network) : null), stale: updated === null || (now - updated) / 1000 > STALE_AFTER_S, evm_rpc_configured: !!EVM, indexer }; const lockRow = await sql(`SELECT index, hash, blue_score, daa_score, fraction_total, voters, locked_at FROM ${T}live_checkpoints WHERE state = 'locked' ORDER BY index DESC LIMIT 1`).catch(() => []); const lock = lockRow[0] ? { index: num(lockRow[0].index), hash: lockRow[0].hash, blue_score: num(lockRow[0].blue_score), daa: num(lockRow[0].daa_score), fraction_total: num(lockRow[0].fraction_total), voters: num(lockRow[0].voters), locked_at: lockRow[0].locked_at } : null; base.lock = lock;