diff --git a/src/components/perp-venues-leaderboard.tsx b/src/components/perp-venues-leaderboard.tsx
index c870348e..294986e1 100644
--- a/src/components/perp-venues-leaderboard.tsx
+++ b/src/components/perp-venues-leaderboard.tsx
@@ -298,12 +298,15 @@ function VolOiCell({ ratio }: { ratio: number | null }) {
if (ratio == null || !Number.isFinite(ratio)) {
return
... | ;
}
- // Color bands: <50x normal (teal), 50-200x high (amber), >200x flag (red).
- // The flagged band catches a wash-traded venue at a glance.
+ // Color bands: <80x normal (teal), 80-300x elevated (amber), >300x flag (red).
+ // Loosened from the original 50/200 thresholds because zero-fee venues
+ // (Lighter, ~115x today) and MM-heavy order books legitimately churn
+ // their OI multiple times per day without it being wash-traded. Only
+ // sustained ratios above 300x warrant a visual flag now.
const band =
- ratio < 50
+ ratio < 80
? { fg: "text-teal-700", bg: "bg-teal-500/10", border: "border-teal-500/30" }
- : ratio <= 200
+ : ratio <= 300
? { fg: "text-amber-700", bg: "bg-amber-500/10", border: "border-amber-500/30" }
: { fg: "text-red-700", bg: "bg-red-500/10", border: "border-red-500/30" };
return (
@@ -311,11 +314,11 @@ function VolOiCell({ ratio }: { ratio: number | null }) {
className="px-3 py-2 tabular-nums"
style={{ fontFamily: "var(--font-mono, monospace)" }}
title={
- ratio < 50
- ? "Vol / OI under 50x is typical for an active order-book venue."
- : ratio <= 200
- ? "Vol / OI between 50x and 200x is elevated; common for venues running incentive programs."
- : "Vol / OI above 200x is a wash-trade signal worth a closer look."
+ ratio < 80
+ ? "Vol / OI under 80x is typical for an active order-book venue."
+ : ratio <= 300
+ ? "Vol / OI above 80x means high churn, common for zero-fee venues like Lighter or MM-heavy books running incentive programs."
+ : "Vol / OI above 300x typically indicates wash trading and is worth a closer look."
}
>
{
// harness via Mobula), ETH only. Covers 12 venues. Falls back to
// the perp-funding bench (036, 7 venues) via PromQL `or` so any
// venue the harness has not picked up yet still surfaces a value.
+ //
+ // Mask risk (P1, audit 2026-06): PromQL `or` hides any RHS series
+ // whose label set already appears on the LHS. Today the two series
+ // share only `{venue, asset}` labels, and `perp_venue_funding_24h_bps`
+ // is the authoritative source (12 venues) while `perp_funding_hold_24h_bps`
+ // covers a disjoint 7-venue subset published by bench 036. As long
+ // as the LHS stays the broader feed and the RHS keeps the same
+ // label keys, the `or` behaves as expected and the fallback only
+ // fires for venues missing on the LHS. If either harness ever emits
+ // an extra label that overlaps (e.g. `region` or `instance`), the
+ // mask will silently swallow the fallback. The clean fix is to
+ // deprecate `perp_funding_hold_24h_bps` once the cohort harness
+ // covers all 16 venues; until then keep the `or` and audit RHS
+ // visibility via /diag/perp-funding-coverage.
queryVector(
prom,
`avg_over_time(perp_venue_funding_24h_bps{asset="ETH"}[24h]) or avg_over_time(perp_funding_hold_24h_bps{asset="ETH"}[24h])`,
diff --git a/src/lib/providers.ts b/src/lib/providers.ts
index a4beaa5c..6547509c 100644
--- a/src/lib/providers.ts
+++ b/src/lib/providers.ts
@@ -399,10 +399,18 @@ const PERP_VENUE_SEED = [
* ranking + wins + per-chain aggregation post-processing on top. A 60
* s revalidate aligns with the bench level ISR window and the standard
* `benchmarks` tag so any `revalidateTag('benchmarks')` clears this
- * along with the rest of the data layer. */
+ * along with the rest of the data layer.
+ *
+ * Key bump rationale (v1 to v2): adding PERP_VENUE_SEED changed the
+ * shape of the cached profile list (new entries for drift, vertex,
+ * edgex, extended, aevo, pacifica, variational, ostium, grvt). Without
+ * bumping the cache key, Vercel ISR keeps serving the pre-seed profile
+ * list until the `benchmarks` tag invalidates, which is what caused
+ * /products/ to keep 404-ing after PR #683 shipped. Always
+ * bump this key when the buildProviders() output shape changes. */
const buildProvidersCached = unstable_cache(
buildProviders,
- ["providers-v1"],
+ ["providers-v2"],
{ revalidate: 60, tags: ["benchmarks"] },
);