// The public stats contract: /api/stats and /api/supply answer with every field docs/api/public-stats.md documents // (FIELDS in each handler), computed from a fixture of what the observer writes. Runs without a database. import { test } from 'node:test'; import assert from 'node:assert/strict'; import { createHandler as stats, FIELDS as STATS_FIELDS, REWARD_FIELDS, LAST_BLOCK_FIELDS, rewardAt } from './stats.mjs'; import { createHandler as supply, FIELDS as SUPPLY_FIELDS, SCHEDULE_FIELDS } from './supply.mjs'; const NOW = '2026-10-05T19:21:56.000Z'; const state = { id: 1, block_count: 125066, header_count: 125066, blue_score: 122211, difficulty: 7748714.75, hashes_per_second_estimate: 27759288, peers: 7, mempool: 0, node_version: '2.1.0', network: 'igneum-devnet', blocks_60s: 58, blocks_per_minute: Array.from({ length: 60 }, (_, i) => [1791227000000 + i * 60000, 60]), observer_started_at: '2026-10-05T19:01:00.000Z', updated_at: '2026-10-05T19:21:55.000Z', finality: { chain_id: 'igneum-devnet', finality_active: true, latest_locked_index: 4073, latest_locked_blue_score: 122190 }, supply_check: { checked_at: NOW, sampled: 500, rule_match: 500, rule_mismatch: 0, sum_match: 480, sum_mismatch: 0, sum_skipped: 20, examples: [], bps: 1 }, }; const last = { hash: '2622db7698c6825b370bbffba03ea11bba250792040296971d2b790a954203ee', blue_score: 122211, daa_score: 125064, timestamp_ms: 1791227608731, miner_address: 'igneumdev:qr4yfyf9mzn643fj8faksflmxur0wxgtmpg8y7fa6qkm8pcjh3ngzqedfx47e', vote_key_hash: '1c3f1190b777365e419f02fd46ce365f464377d0fdbdf1332da6600d09a75a0e', tx_count: 0, is_chain_block: true, number: 81263 }; function fakeSql(rows) { return async (query) => { if (/row_to_json/.test(query) && /max\(daa_score\)/.test(query)) return [{ now: NOW, state, daa: 125064 }]; if (/row_to_json/.test(query)) return [{ now: NOW, state }]; if (/ORDER BY received_at DESC LIMIT 1/.test(query)) return [last]; if (/count\(DISTINCT vote_key_hash\)/.test(query)) return [{ n: 25 }]; return rows || []; }; } function fakeRes() { const r = { code: 0, headers: {}, body: null, status(c) { r.code = c; return r; }, setHeader(k, v) { r.headers[k] = v; }, json(o) { r.body = o; return r; } }; return r; } test('/api/stats carries every documented field and the reward of spec 2.5', async () => { const res = fakeRes(); await stats({ sql: fakeSql(), env: {} })({ method: 'GET', url: '/api/stats' }, res); assert.equal(res.code, 200); for (const f of STATS_FIELDS) assert.ok(f in res.body, `missing ${f}`); for (const f of REWARD_FIELDS) assert.ok(f in res.body.block_reward, `missing block_reward.${f}`); for (const f of LAST_BLOCK_FIELDS) assert.ok(f in res.body.last_block, `missing last_block.${f}`); assert.equal(res.body.chain_id, 4463); assert.equal(res.body.height, 81263); assert.equal(res.body.block_reward.sompi, '454486399'); // E(125,064), the payload of block 2622db76 assert.equal(res.body.block_reward.ign, '4.54486399'); assert.equal(res.body.block_reward.miner_ign, '3.6358912'); // 80%: 454486399 - 90897279 assert.equal(res.body.block_reward.proving_pool_ign, '0.90897279'); assert.equal(res.body.block_time_measured_s, 1.034); assert.equal(res.body.blocks_per_day_measured, 86400); assert.equal(res.body.stale, false); assert.equal(res.headers['Cache-Control'], 'public, max-age=10, s-maxage=10'); }); test('/api/stats marks a stale observer', async () => { const res = fakeRes(); const old = { ...state, updated_at: '2026-10-05T19:00:00.000Z' }; const sql = async (q) => (/row_to_json/.test(q) ? [{ now: NOW, state: old }] : /LIMIT 1/.test(q) ? [last] : [{ n: 0 }]); await stats({ sql, env: {} })({ method: 'GET', url: '/api/stats' }, res); assert.equal(res.body.stale, true); assert.ok(res.body.age_s > 1000); }); test('rewardAt: halving and ramp bookkeeping', () => { const r = rewardAt(63_115_200); assert.equal(r.sompi, '1584404390'); assert.equal(r.halving_period, 1); assert.equal(r.next_halving_daa, 126_230_400); assert.equal(r.ramp_factor, 1); assert.equal(rewardAt(0).ramp_factor, 0.1); }); test('/api/supply carries every documented field, the cap and a 33-row schedule', async () => { const res = fakeRes(); await supply({ sql: fakeSql(), env: {} })({ method: 'GET', url: '/api/supply' }, res); assert.equal(res.code, 200); for (const f of SUPPLY_FIELDS) assert.ok(f in res.body, `missing ${f}`); assert.equal(res.body.max_supply_ign, '4000000000'); assert.equal(res.body.schedule.length, 33); for (const f of SCHEDULE_FIELDS) assert.ok(f in res.body.schedule[0], `missing schedule.${f}`); assert.equal(res.body.schedule[0].per_second_ign, '31.68808781'); assert.equal(res.body.schedule[1].per_second_ign, '15.8440439'); assert.equal(res.body.schedule[1].start_daa, 63115200); assert.equal(res.body.minted_at_end_ign, '3963038988.86765648'); assert.equal(res.body.circulating_ign, '482350.69732271'); // mintedByRule(125,064): the rule summed over DAA 0 to 125,063 assert.equal(res.body.halving.next_halving_daa, 63115200); assert.equal(res.body.ramp.complete, false); assert.equal(res.body.check.rule_match, 500); }); test('both refuse anything but GET', async () => { for (const h of [stats({ sql: fakeSql(), env: {} }), supply({ sql: fakeSql(), env: {} })]) { const res = fakeRes(); await h({ method: 'POST', url: '/' }, res); assert.equal(res.code, 405); } });