4e317eff6d
Static SPA over UP33 (ve(3,3)) + the official Uniswap v2/v3 deployments: browse and add liquidity across all three, manage CL/v2 positions, one-token ZAP adds, aggregator vs native swaps, and LP-based limit orders. Ships with a zero-dependency pool indexer for uniswap discovery. Browser-wallet signing only; no key material anywhere.
80 lines
2.6 KiB
TypeScript
80 lines
2.6 KiB
TypeScript
import { formatUnits } from 'viem'
|
|
|
|
/** significant-digit number formatting with thousands separators */
|
|
export function fmtNum(x: number, sig = 5): string {
|
|
if (!Number.isFinite(x)) return '—'
|
|
if (x === 0) return '0'
|
|
sig = Math.max(1, Math.min(sig, 21)) // toPrecision throws outside [1,100]
|
|
const neg = x < 0
|
|
const a = Math.abs(x)
|
|
let s: string
|
|
if (a >= 1) {
|
|
const intDigits = Math.floor(Math.log10(a)) + 1
|
|
const frac = Math.max(0, Math.min(sig - intDigits, 8))
|
|
s = a.toLocaleString('en-US', { minimumFractionDigits: 0, maximumFractionDigits: frac })
|
|
} else {
|
|
s = a.toPrecision(sig)
|
|
if (s.includes('e')) {
|
|
const exp = Math.ceil(-Math.log10(a))
|
|
s = a.toFixed(Math.min(exp + sig, 18))
|
|
}
|
|
s = s.replace(/\.?0+$/, '')
|
|
}
|
|
return (neg ? '-' : '') + s
|
|
}
|
|
|
|
export function fmtAmount(v: bigint, decimals: number, sig = 5): string {
|
|
return fmtNum(Number(formatUnits(v, decimals)), sig)
|
|
}
|
|
|
|
/** compact amount for dense table cells: 24.9M, 338.4K, 12.4, 0.0421 */
|
|
export function fmtCompact(x: number): string {
|
|
if (!Number.isFinite(x)) return '—'
|
|
if (x !== 0 && Math.abs(x) < 1) return fmtNum(x, 3)
|
|
return new Intl.NumberFormat('en-US', { notation: 'compact', maximumFractionDigits: 1 }).format(x)
|
|
}
|
|
|
|
export function fmtCompactAmount(v: bigint, decimals: number): string {
|
|
return fmtCompact(Number(formatUnits(v, decimals)))
|
|
}
|
|
|
|
export function fmtUsd(x: number | string | undefined): string {
|
|
const n = typeof x === 'string' ? Number(x) : x
|
|
if (n === undefined || !Number.isFinite(n)) return ''
|
|
// bounded width: sub-cent USD precision is noise everywhere in this app,
|
|
// and long fractions (dust TVLs) were stretching table columns
|
|
if (n > 0 && n < 0.01) return '<$0.01'
|
|
return '$' + n.toLocaleString('en-US', { maximumFractionDigits: n >= 1000 ? 0 : 2 })
|
|
}
|
|
|
|
export function fmtPct(x: number, dp = 2): string {
|
|
if (!Number.isFinite(x)) return '—'
|
|
return x.toFixed(dp) + '%'
|
|
}
|
|
|
|
export function shortAddr(a: string): string {
|
|
return a.slice(0, 6) + '…' + a.slice(-4)
|
|
}
|
|
|
|
export function fmtDur(seconds: number): string {
|
|
if (seconds <= 0) return '0s'
|
|
const d = Math.floor(seconds / 86400)
|
|
const h = Math.floor((seconds % 86400) / 3600)
|
|
const m = Math.floor((seconds % 3600) / 60)
|
|
const s = Math.floor(seconds % 60)
|
|
if (d > 0) return `${d}d ${h}h ${m}m`
|
|
if (h > 0) return `${h}h ${m}m ${s}s`
|
|
if (m > 0) return `${m}m ${s}s`
|
|
return `${s}s`
|
|
}
|
|
|
|
/** signed bps difference of a vs b (positive = a better) */
|
|
export function bpsDiff(a: bigint, b: bigint): number {
|
|
if (b === 0n) return 0
|
|
return Number(((a - b) * 1_000_000n) / b) / 100
|
|
}
|
|
|
|
export function nowSec(): number {
|
|
return Math.floor(Date.now() / 1000)
|
|
}
|