"use client"; import { ChevronDown } from "lucide-react"; import Link from "next/link"; import { useEffect, useState } from "react"; import { CoverageBadge } from "@/components/market/coverage-badge"; import { RightsBadge, StatusBadge } from "@/components/ui/status-badge"; import { clientApi } from "@/lib/client-api"; import { COMPARABILITY_LABEL, OBSERVATION_TYPE_LABEL, ROLE_LABEL, deltaTone, formatDeltaBps, reasonLabel, roleOf } from "@/lib/coverage"; import { cx, formatBps, formatDateTime, formatDuration, formatQuoteValue } from "@/lib/format"; import type { Provenance } from "@/lib/types"; const ROLE_TONE: Record = { votes: "bg-positive-soft text-positive", confirms: "bg-accent-soft text-accent", validates: "bg-surface-3 text-ink-2", excluded: "bg-stale-soft text-stale" }; /** * "Why this price?" — the Source Coverage Matrix: every contribution behind the canonical value with its * observation type, its distance to the consensus and its role (votes / confirms / validates / excluded). */ export function ProvenancePanel({ instrumentId, assetClass, defaultOpen = false, className }: { instrumentId: string; assetClass?: string | null; defaultOpen?: boolean; className?: string }) { const [open, setOpen] = useState(defaultOpen); const [data, setData] = useState(null); const [err, setErr] = useState(null); useEffect(() => { if (!open) return; let alive = true; const load = () => clientApi(`/v1/quotes/${encodeURIComponent(instrumentId)}/provenance`) .then((d) => alive && setData(d)) .catch((e) => alive && setErr(e instanceof Error ? e.message : "unavailable")); load(); const t = setInterval(load, 5000); return () => { alive = false; clearInterval(t); }; }, [open, instrumentId]); const canonicalClass = data?.comparability ?? null; const included = data?.contributions.filter((c) => c.included) ?? []; const voters = included.filter((c) => roleOf(c) === "votes"); return (
{open && (
{err &&
{err}
} {!data && !err &&
Loading provenance…
} {data && ( <> {data.coverage && (
Source coverage →
)}

Method (consensus v{data.consensus_version}): {data.method}. Status · rights · computed {formatDateTime(data.computed_at, { seconds: true })}.

{data.shared_upstream_pairs && data.shared_upstream_pairs.length > 0 && (

Likely shared upstream:{" "} {data.shared_upstream_pairs.map((p) => `${p.sourceA} ≈ ${p.sourceB} (similarity ${(p.similarity * 100).toFixed(1)} % over ${p.samples} samples)`).join("; ")} — these sources are counted as one family.

)}
{data.contributions.map((c) => { const role = roleOf(c); const withheld = c.value == null; return ( ); })}
Source Type Value Age Δ consensus Role Weight Reliability
{c.source?.name ?? c.source_id} {c.connector_id} {c.source?.family && c.source.family !== c.source_id ? ` · family ${c.source.family}` : ""} {c.observation_type ? OBSERVATION_TYPE_LABEL[c.observation_type] : "—"} {withheld ? ( — validator ) : ( formatQuoteValue(c.value, assetClass, data.currency) )} {formatDuration(c.age_ms)} {withheld && role === "validates" ? ( {c.reason === "validator_disagrees" ? "disagrees" : c.delta_bps == null ? "—" : "agrees"} ) : ( formatDeltaBps(c.delta_bps) )} {ROLE_LABEL[role]} {!c.included && {reasonLabel(c.reason, c.observation_type, canonicalClass)}} {c.included ? c.weight.toFixed(3) : "—"} {Math.round(c.reliability_score * 100)}

Votes set the canonical value; proxies (stablecoin markets, derived crosses) confirm it with reduced weight and never count as independent families; validators (restricted rights) are compared but never displayed. Observations of another class (official fixing vs live market, last close vs today) are reported, never mixed. Reliability is the operational score of the connector, not a judgement of the venue. See methodology.

)}
)}
); } function Fact({ label, value, sub }: { label: string; value: string; sub?: string }) { return (
{label}
{value}
{sub &&
{sub}
}
); }