import { resolveExplorerApiBase } from '@/libs/frontend-api-client/api-base' import type { Block } from './blocks' import type { Transaction } from './transactions' import type { TransactionSummary } from './addresses' export function getExplorerApiBase() { return resolveExplorerApiBase() } export async function fetchBlockscoutJson(path: string): Promise { const response = await fetch(`${getExplorerApiBase()}${path}`) if (!response.ok) { throw new Error(`HTTP ${response.status}`) } return response.json() as Promise } type HashLike = string | { hash?: string | null } | null | undefined export function extractHash(value: HashLike): string { if (!value) return '' return typeof value === 'string' ? value : value.hash || '' } function toNumber(value: unknown): number { if (typeof value === 'number') return value if (typeof value === 'string' && value.trim() !== '') return Number(value) return 0 } interface BlockscoutBlock { hash: string height: number | string timestamp: string miner: HashLike transaction_count: number | string gas_used: number | string gas_limit: number | string } interface BlockscoutTransaction { hash: string block_number: number | string from: HashLike to: HashLike value: string status?: string | null result?: string | null gas_price?: number | string | null gas_limit: number | string gas_used?: number | string | null max_fee_per_gas?: number | string | null max_priority_fee_per_gas?: number | string | null raw_input?: string | null timestamp: string created_contract?: HashLike } function normalizeStatus(raw: BlockscoutTransaction): number { const value = (raw.status || raw.result || '').toString().toLowerCase() if (value === 'success' || value === 'ok' || value === '1') return 1 if (value === 'error' || value === 'failed' || value === '0') return 0 return 0 } export function normalizeBlock(raw: BlockscoutBlock, chainId: number): Block { return { chain_id: chainId, number: toNumber(raw.height), hash: raw.hash, timestamp: raw.timestamp, miner: extractHash(raw.miner), transaction_count: toNumber(raw.transaction_count), gas_used: toNumber(raw.gas_used), gas_limit: toNumber(raw.gas_limit), } } export function normalizeTransaction(raw: BlockscoutTransaction, chainId: number): Transaction { return { chain_id: chainId, hash: raw.hash, block_number: toNumber(raw.block_number), block_hash: '', transaction_index: 0, from_address: extractHash(raw.from), to_address: extractHash(raw.to) || undefined, value: raw.value || '0', gas_price: raw.gas_price != null ? toNumber(raw.gas_price) : undefined, max_fee_per_gas: raw.max_fee_per_gas != null ? toNumber(raw.max_fee_per_gas) : undefined, max_priority_fee_per_gas: raw.max_priority_fee_per_gas != null ? toNumber(raw.max_priority_fee_per_gas) : undefined, gas_limit: toNumber(raw.gas_limit), gas_used: raw.gas_used != null ? toNumber(raw.gas_used) : undefined, status: normalizeStatus(raw), input_data: raw.raw_input || undefined, contract_address: extractHash(raw.created_contract) || undefined, created_at: raw.timestamp, } } export function normalizeTransactionSummary(raw: BlockscoutTransaction): TransactionSummary { return { hash: raw.hash, block_number: toNumber(raw.block_number), from_address: extractHash(raw.from), to_address: extractHash(raw.to) || undefined, value: raw.value || '0', status: normalizeStatus(raw), } }