diff --git a/harnesses/axelar-gmp-latency/cmd/script/chains.go b/harnesses/axelar-gmp-latency/cmd/script/chains.go index 2c83c54f..2b62f176 100644 --- a/harnesses/axelar-gmp-latency/cmd/script/chains.go +++ b/harnesses/axelar-gmp-latency/cmd/script/chains.go @@ -14,6 +14,27 @@ import "strings" // - Cosmos chains (osmosis, injective, sei, celestia, kava) are // Axelar-exclusive coverage vs Wormhole/LayerZero/CCIP/Hyperlane // +// axelarTrackedChains is the set of OCB canonical slugs we record metrics for. +// Only messages where BOTH source AND destination are in this set are +// observed, capping cardinality at len²×buckets. +// Derived from the bench YAML provider slugs. +var axelarTrackedChains = map[string]bool{ + "ethereum": true, + "polygon": true, + "base": true, + "moonbeam": true, + "osmosis": true, + "arbitrum": true, + "avalanche": true, + "bnb": true, + "celo": true, + "injective": true, + "linea": true, + "mantle": true, + "optimism": true, + "scroll": true, +} + // Unknown names fall through to `chain-` so we never drop data. var axelarChainSlug = map[string]string{ // EVM L1s diff --git a/harnesses/axelar-gmp-latency/cmd/script/main.go b/harnesses/axelar-gmp-latency/cmd/script/main.go index a3652e99..55ff6b41 100644 --- a/harnesses/axelar-gmp-latency/cmd/script/main.go +++ b/harnesses/axelar-gmp-latency/cmd/script/main.go @@ -173,8 +173,10 @@ func poll(ctx context.Context, client *http.Client, seen *lruSet) error { totalMs := float64(m.TimeSpent.Total) * 1000 if totalMs > 0 && totalMs <= maxLatencyMs { dst := canonicalizeAxelarChain(m.Call.ReturnValues.DestinationChain) - axelarE2ELatencyMs.WithLabelValues(src, dst).Observe(totalMs) - axelarSeenTotal.WithLabelValues(src, dst).Inc() + if axelarTrackedChains[src] && axelarTrackedChains[dst] { + axelarE2ELatencyMs.WithLabelValues(src, dst).Observe(totalMs) + axelarSeenTotal.WithLabelValues(src, dst).Inc() + } } seen.add(m.ID) diff --git a/harnesses/chainlink-ccip-latency/cmd/script/chains.go b/harnesses/chainlink-ccip-latency/cmd/script/chains.go index e551d3c4..bb3a17fe 100644 --- a/harnesses/chainlink-ccip-latency/cmd/script/chains.go +++ b/harnesses/chainlink-ccip-latency/cmd/script/chains.go @@ -12,6 +12,32 @@ package main // makes the mapping legible and the audit trail obvious when CCIP // adds a new chain we didn't anticipate. // +// ccipTrackedChains is the set of OCB canonical slugs we record metrics for. +// Only messages where BOTH source AND destination are in this set are +// observed, capping cardinality at len²×buckets. +// Derived from the bench YAML provider slugs. +var ccipTrackedChains = map[string]bool{ + "ethereum": true, + "bnb": true, + "polygon": true, + "avalanche": true, + "arbitrum": true, + "base": true, + "robinhood": true, + "berachain": true, + "celo": true, + "ink": true, + "linea": true, + "mantle": true, + "monad": true, + "moonbeam": true, + "optimism": true, + "scroll": true, + "solana": true, + "unichain": true, + "world-chain": true, +} + // Only mainnet entries are mapped; testnet rows are dropped in main.go // via the `environment != "mainnet"` guard so we never emit test-chain // latency. diff --git a/harnesses/chainlink-ccip-latency/cmd/script/main.go b/harnesses/chainlink-ccip-latency/cmd/script/main.go index 19b6e3c8..c4a0cece 100644 --- a/harnesses/chainlink-ccip-latency/cmd/script/main.go +++ b/harnesses/chainlink-ccip-latency/cmd/script/main.go @@ -189,6 +189,10 @@ func poll(ctx context.Context, client *http.Client, seen *lruSet) error { seen.add(m.MessageID) continue } + if !ccipTrackedChains[srcSlug] || !ccipTrackedChains[dstSlug] { + seen.add(m.MessageID) + continue + } ccipLatencyMs.WithLabelValues(srcSlug, dstSlug).Observe(deltaMs) ccipSeenTotal.WithLabelValues(srcSlug, dstSlug).Inc() seen.add(m.MessageID) diff --git a/harnesses/layerzero-message-latency/cmd/script/chains.go b/harnesses/layerzero-message-latency/cmd/script/chains.go index 2c888741..ded44292 100644 --- a/harnesses/layerzero-message-latency/cmd/script/chains.go +++ b/harnesses/layerzero-message-latency/cmd/script/chains.go @@ -10,6 +10,35 @@ package main // - LayerZero exposes a bunch of exotic chains (orderly, flare, ape, // robinhood, hyperliquid) that map to our slugs where they exist. // +// lzTrackedChains is the set of OCB canonical slugs we record metrics for. +// Only messages where BOTH source AND destination are in this set are +// observed. This caps cardinality at len²×buckets instead of the full +// N×N cross-product of all chains LayerZero supports. +// Derived from the bench YAML provider slugs — add here when adding a +// new chain to the bench. +var lzTrackedChains = map[string]bool{ + "ethereum": true, + "solana": true, + "bnb": true, + "arbitrum": true, + "base": true, + "optimism": true, + "polygon": true, + "avalanche": true, + "robinhood": true, + "monad": true, + "berachain": true, + "celo": true, + "injective": true, + "ink": true, + "linea": true, + "mantle": true, + "moonbeam": true, + "scroll": true, + "sui": true, + "unichain": true, +} + // Unknown names fall through to a synthetic `chain-` slug in // main.go so we never drop data silently. var lzChainSlug = map[string]string{ diff --git a/harnesses/layerzero-message-latency/cmd/script/main.go b/harnesses/layerzero-message-latency/cmd/script/main.go index 5f7a4f6d..3ccfc0d0 100644 --- a/harnesses/layerzero-message-latency/cmd/script/main.go +++ b/harnesses/layerzero-message-latency/cmd/script/main.go @@ -206,6 +206,10 @@ func poll(ctx context.Context, client *http.Client, seen *lruSet) error { continue } dstSlug := chainSlug(m.Pathway.Receiver.Chain) + if !lzTrackedChains[srcSlug] || !lzTrackedChains[dstSlug] { + seen.add(m.GUID) + continue + } lzLatencyMs.WithLabelValues(srcSlug, dstSlug).Observe(deltaMs) lzSeenTotal.WithLabelValues(srcSlug, dstSlug).Inc() seen.add(m.GUID) diff --git a/src/app/api/fee-compare/route.ts b/src/app/api/fee-compare/route.ts index cd724592..b4f9e2e1 100644 --- a/src/app/api/fee-compare/route.ts +++ b/src/app/api/fee-compare/route.ts @@ -1110,7 +1110,10 @@ function augmentWithHlOpenPositions( } function reconstructGainsPositions(trades: GainsApiTrade[], cutoffMs: number): PositionSlice[] { - const OPEN_ACTIONS = new Set(["MarketOpened", "LimitOrderExecuted"]); + // v5 names: MarketOpened, LimitOrderExecuted — v6 names: TradeOpenedMarket, TradeOpenedLimit + const OPEN_ACTIONS = new Set(["MarketOpened", "LimitOrderExecuted", "TradeOpenedMarket", "TradeOpenedLimit"]); + // TradePosSizeIncrease updates the position size; use latest size as notional + const INCREASE_ACTIONS = new Set(["TradePosSizeIncrease"]); const CLOSE_ACTIONS = new Set(["TradeClosedMarket", "TradeClosedTP", "TradeClosedSL", "TradeClosedLIQ"]); const byId = new Map(); @@ -1118,19 +1121,25 @@ function reconstructGainsPositions(trades: GainsApiTrade[], cutoffMs: number): P if (!byId.has(t.id)) byId.set(t.id, {}); const e = byId.get(t.id)!; if (OPEN_ACTIONS.has(t.action)) e.open = t; + else if (INCREASE_ACTIONS.has(t.action) && e.open) { + e.open = { ...e.open, size: t.size, leverage: t.leverage }; + } else if (CLOSE_ACTIONS.has(t.action)) e.close = t; } + const now = Date.now(); const slices: PositionSlice[] = []; for (const { open, close } of byId.values()) { - if (!open || !close) continue; + if (!open) continue; const openMs = new Date(open.date).getTime(); if (openMs < cutoffMs) continue; + // Still-open positions use now as close time (same as reconstructHlPositions) + const closeMs = close ? new Date(close.date).getTime() : now; slices.push({ coin: open.pair.split("/")[0], notionalUsd: open.size * open.leverage, openMs, - closeMs: new Date(close.date).getTime(), + closeMs, isLong: open.buy !== false, }); } diff --git a/src/lib/aggregate-blob.ts b/src/lib/aggregate-blob.ts index 0c2ff32e..f920fb1f 100644 --- a/src/lib/aggregate-blob.ts +++ b/src/lib/aggregate-blob.ts @@ -26,6 +26,37 @@ import type { Benchmark } from "@/types/benchmark"; import { loadSpecsUncached } from "@/lib/materialize/load"; import { overlayEditorial, slimBenchmarkForCache } from "@/lib/spec"; +// Aggressive slim for the aggregate blob. Hub pages (homepage, categories, +// chains, products) only need card data — they never render editorial text +// or metric panels. Stripping these fields drops the serialized aggregate +// from ~4.3 MB to well under the 2 MB unstable_cache ceiling. +// +// Fields stripped beyond slimBenchmarkForCache (which already removes +// 7d/30d series): +// - extras.seriesByRegion24h (only used on bench detail pages) +// - metricPanels (only used on bench detail pages) +// - seoIntro, faq, disclaimer (editorial, bench detail only) +// - perChainExplainer (bench detail + worker's sitemap.json handles sitemap) +// - findings, methodology (bench detail only; required fields → []) +// - abstract (bench detail only; required field → "") +function slimForBlobAggregate(b: Benchmark): Benchmark { + const base = slimBenchmarkForCache(b); + // eslint-disable-next-line @typescript-eslint/no-unused-vars + const { seriesByRegion24h: _sbr, ...slimExtras } = base.extras; + return { + ...base, + extras: slimExtras, + metricPanels: undefined, + seoIntro: undefined, + faq: undefined, + perChainExplainer: undefined, + disclaimer: undefined, + findings: [], + methodology: [], + abstract: "", + }; +} + // On any Vercel deployment (production or preview), use the self-hosted // CDN proxy (/api/aggregate on openchainbench.com) so Vercel functions // pay ~1 ms (edge cache hit) instead of ~12 s fetching the 7.5 MB blob @@ -113,7 +144,7 @@ async function fetchAndProject(): Promise { for (const bench of raw.benches) { const spec = specBySlug.get(bench.slug); if (!spec) continue; // Bench in blob no longer has a spec — skip. - projected.push(slimBenchmarkForCache(overlayEditorial(bench, spec))); + projected.push(slimForBlobAggregate(overlayEditorial(bench, spec))); } return projected.sort((a, b) => (a.number ?? "").localeCompare(b.number ?? ""), @@ -130,6 +161,6 @@ async function fetchAndProject(): Promise { */ export const loadAggregateFromBlob = unstable_cache( fetchAndProject, - ["aggregate-blob-v2"], + ["aggregate-blob-v3"], { revalidate: 60, tags: ["bench-aggregate", "benchmarks"] }, );