import { Hex, Value as core_Value } from 'ox' /** Coerce a TIDX numeric column (bigint / number / string) to a JS number. */ export function toNumber(value: unknown) { if (typeof value === 'bigint') return Number(value) if (typeof value === 'number') return value if (typeof value === 'string') return Number(value) return undefined } /** * Coerce a TIDX text column (address / hash / topic / hex data) to a string. * TIDX already returns these columns as strings; this narrows the `unknown` * row value without an unsafe `String(...)` on a possible object. Returns * undefined for null/non-string values. */ export function toText(value: unknown): string | undefined { return typeof value === 'string' ? value : undefined } /** * Coerce a TIDX numeric column (bigint / number / string) to a non-negative * decimal integer string, preserving precision for values beyond * `Number.MAX_SAFE_INTEGER` (e.g. token balances). Returns undefined when the * value cannot be interpreted as a non-negative integer. */ export function toIntegerString(value: unknown) { if (typeof value === 'bigint') return value >= 0n ? value.toString() : undefined if (typeof value === 'number') { if (!Number.isFinite(value) || value < 0 || !Number.isInteger(value)) return undefined // Route through BigInt so large integers serialize as plain decimals rather // than exponential notation (e.g. `1e+21`). return BigInt(value).toString() } if (typeof value === 'string') { const trimmed = value.trim() return /^\d+$/.test(trimmed) ? trimmed : undefined } return undefined } /** Builds a token amount from its base-unit quantity and immutable token metadata. */ export function tokenAmount(options: tokenAmount.Options) { const baseUnits = options.baseUnits.toString() return { baseUnits, currency: options.currency, decimals: options.decimals, formatted: core_Value.format(BigInt(baseUnits), options.decimals), } } export declare namespace tokenAmount { /** Inputs needed to render a self-contained token amount. */ type Options = { /** Quantity in the token's smallest unit. */ baseUnits: bigint | string /** Monetary denomination reported by the token. */ currency: string /** Decimal places used by the token. */ decimals: number } } /** * Coerce a TIDX numeric column (bigint / number / decimal string) to a minimal * `0x`-prefixed hex quantity (`0x0` for zero), matching the JSON-RPC wire * format. Returns undefined when the value is null/undefined or cannot be * interpreted as a non-negative integer. */ export function toHex(value: unknown): Hex.Hex | undefined { let big: bigint if (typeof value === 'bigint') big = value else if (typeof value === 'number') { if (!Number.isFinite(value) || !Number.isInteger(value)) return undefined big = BigInt(value) } else if (typeof value === 'string') { const trimmed = value.trim() if (!/^\d+$/.test(trimmed)) return undefined big = BigInt(trimmed) } else return undefined if (big < 0n) return undefined return Hex.fromNumber(big) } /** * Parse a `0x`-prefixed hex quantity into a non-negative decimal integer string, * preserving precision beyond `Number.MAX_SAFE_INTEGER`. Returns undefined when * the value is not a valid hex quantity. */ export function fromHex(value: unknown): string | undefined { if (typeof value !== 'string' || !/^0x[0-9a-fA-F]+$/.test(value)) return undefined return BigInt(value).toString() } /** * Parse a `0x`-prefixed hex quantity into a JS number. Returns undefined when * the value is not a valid hex quantity. */ export function hexToNumber(value: unknown): number | undefined { const decimal = fromHex(value) return decimal === undefined ? undefined : Number(decimal) } /** * Convert a `0x`-prefixed hex Unix-seconds timestamp into an ISO 8601 datetime * string. Returns undefined when the value is not a valid hex quantity. */ export function hexSecondsToIso(value: unknown): string | undefined { const seconds = hexToNumber(value) return seconds === undefined ? undefined : new Date(seconds * 1000).toISOString() } /** * Read a block's timestamp as epoch milliseconds, preferring Tempo's * `timestampMillis` over the second-granularity `timestamp`. Accepts hex * quantities (raw RPC) and bigint/number (viem-formatted). */ function blockToMillis(block: unknown): number | undefined { if (typeof block !== 'object' || block === null) return undefined const { timestamp, timestampMillis } = block as { timestamp?: unknown timestampMillis?: unknown } const millis = quantity(timestampMillis) if (millis !== undefined) return millis const seconds = quantity(timestamp) return seconds === undefined ? undefined : seconds * 1_000 } /** Convert a block's timestamp to an ISO 8601 datetime string, preserving milliseconds. */ export function blockToIso(block: unknown): string | undefined { const millis = blockToMillis(block) return millis === undefined ? undefined : new Date(millis).toISOString() } /** Narrows a quantity that may arrive hex-encoded (raw RPC) or decoded (viem). */ function quantity(value: unknown): number | undefined { const number = toNumber(value) return number === undefined || !Number.isFinite(number) ? undefined : number } /** * Coerce a TIDX timestamp column (Date / epoch seconds / epoch ms / ISO string) * to an ISO 8601 datetime string. Returns undefined when the value cannot be * interpreted as a timestamp. */ export function toIsoDateTime(value: unknown) { if (value instanceof Date) return value.toISOString() // TIDX can surface timestamps as either decoded date strings or numeric // epochs depending on the query engine and transport codec. if (typeof value === 'number') return new Date(value > 1_000_000_000_000 ? value : value * 1000).toISOString() if (typeof value !== 'string') return undefined // ClickHouse formats `DateTime`/`DateTime64` as `YYYY-MM-DD HH:MM:SS[.sss]` // (UTC), which V8's Date parser treats as *local* time. Normalize to a // strict ISO 8601 UTC string so both engines hit the same `new Date(...)` // path. const normalized = /^\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}(\.\d+)?$/.test(value) ? `${value.replace(' ', 'T')}Z` : value const date = new Date(normalized) if (Number.isNaN(date.getTime())) return undefined return date.toISOString() }