/** * SolaceScan Explorer (Blockscout v2) client for Chain 138. * * Base URL: https://api.explorer.d-bis.org (CORS *) * Fallback: https://explorer.d-bis.org/api/v2 (same data, different host) * * We hit the `api.*` subdomain by default because it returns clean JSON * without the Next.js HTML wrapper. */ import { httpJson } from './http'; import { endpoints } from '../config/endpoints'; const api = (path: string) => `${endpoints.explorer.apiBaseUrl}/api/v2${path}`; export interface ExplorerStats { total_blocks: number; total_transactions: number; total_addresses: number; latest_block: number; average_block_time: number; gas_prices: { average: number; fast?: number; slow?: number }; network_utilization_percentage: number; transactions_today: number; } export async function getExplorerStats(): Promise { const raw = await httpJson(api('/stats')); // Blockscout returns `average_block_time` in milliseconds; normalize to seconds // so callers can display `${value.toFixed(1)}s` directly. Chain-138 block time // is ~4s, so a raw value > 60 is a reliable signal that it is still in ms. const average_block_time = typeof raw.average_block_time === 'number' && raw.average_block_time > 60 ? raw.average_block_time / 1000 : raw.average_block_time; return { ...raw, average_block_time }; } export interface ExplorerBlock { height: number; hash: string; timestamp: string; tx_count: number; gas_used: string; gas_limit: string; size: number; miner: { hash: string }; } export async function getLatestBlocks(): Promise { return httpJson(api('/main-page/blocks')); } export interface ExplorerTx { hash: string; block_number: number; timestamp: string; from: { hash: string }; to: { hash: string } | null; value: string; // wei gas_used: string; gas_price: string; status: 'ok' | 'error' | null; method: string | null; fee: { value: string }; } interface PagedTxResponse { items: ExplorerTx[]; next_page_params?: unknown } export async function getLatestTransactions(limit = 20): Promise { const data = await httpJson(api('/transactions')); return (data.items ?? []).slice(0, limit); } export async function getAddressTransactions(address: string, limit = 20): Promise { const data = await httpJson(api(`/addresses/${address}/transactions`)); return (data.items ?? []).slice(0, limit); } export function explorerTxUrl(hash: string): string { return `${endpoints.explorer.baseUrl}/tx/${hash}`; } export function explorerAddressUrl(address: string): string { return `${endpoints.explorer.baseUrl}/address/${address}`; } export function explorerBlockUrl(height: number): string { return `${endpoints.explorer.baseUrl}/block/${height}`; }