import type { VaultSnapshot, FlowWindow, Alert } from "./monitor.js"; const fmt = (v: bigint, decimals: number, maxFrac = 4): string => { const neg = v < 0n; const abs = neg ? -v : v; const base = 10n ** BigInt(decimals); const whole = abs / base; const frac = abs % base; let fracStr = frac.toString().padStart(decimals, "0").slice(0, maxFrac).replace(/0+$/, ""); const wholeStr = whole.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ","); return (neg ? "-" : "") + wholeStr + (fracStr ? "." + fracStr : ""); }; const esc = (s: string): string => s.replace(/&/g, "&").replace(//g, ">"); /** * Render a self-contained HTML dashboard (KPI cards, health badges, alerts, flow summary, NAV * gauge) from a {@link VaultSnapshot}, an optional {@link FlowWindow}, and alerts. Chart.js is the * only external dependency (loaded from a CDN); everything else is inlined, so the file opens * directly in a browser. Re-run the monitor to refresh. */ export function renderDashboard( snap: VaultSnapshot, flows?: FlowWindow, alerts: Alert[] = [], ): string { const d = snap.decimals; const health = snap.accountant.isPaused || snap.teller.isPaused ? { label: "PAUSED", color: "#dc2626" } : alerts.some((a) => a.level === "critical") ? { label: "CRITICAL", color: "#dc2626" } : alerts.some((a) => a.level === "warn") ? { label: "WARNING", color: "#d97706" } : { label: "HEALTHY", color: "#16a34a" }; const kpis = [ { label: "NAV (rate)", value: fmt(snap.rate, d, 6) }, { label: "AUM", value: fmt(snap.aum, d, 2) }, { label: "Total supply", value: fmt(snap.totalSupply, d, 2) }, { label: "Fees owed", value: fmt(snap.accountant.feesOwedInBase, d, 2) }, ]; const badges = [ { label: "Accountant", on: !snap.accountant.isPaused }, { label: "Teller", on: !snap.teller.isPaused }, ...(snap.queue ? [{ label: "Queue", on: !snap.queue.isPaused }] : []), ]; const alertRows = alerts.length ? alerts .map( (a) => `
Flows over blocks ${flows.fromBlock}–${flows.toBlock}. Net share flow: ${fmt(flows.netShareFlow, d, 2)}.
` : ""; const data = JSON.stringify( { snapshot: snap, flows, alerts, }, (_k, v) => (typeof v === "bigint" ? v.toString() : v), 2, ); return `No flow window supplied.
'}| NAV bounds | +${snap.accountant.upperBoundBps} bps / -${snap.accountant.lowerBoundBps} bps, min delay ${snap.accountant.minimumUpdateDelaySec}s |
| Fees | platform ${snap.accountant.platformFeeBps} bps, performance ${snap.accountant.performanceFeeBps} bps |
| Share lock | ${snap.teller.shareLockPeriodSec}s |
| Last NAV update | ${snap.accountant.lastUpdateTimestamp ? new Date(snap.accountant.lastUpdateTimestamp * 1000).toISOString() : "—"} |
| Snapshot taken | ${new Date(snap.timestamp * 1000).toISOString()} |