From a91ff6f6f8bf09506344bf405dad6a2012641eda Mon Sep 17 00:00:00 2001 From: Flotapponnier Date: Sat, 19 Sep 2026 23:35:02 +0200 Subject: [PATCH] bridge execution benches (261, 263), audit round 2: one quoted-fee definition (ticket minus quoted output), realized cost published only from the on-chain read, realized fee in bps of the ticket from an execution-only metric, stale threshold and window wording follow the spec, pre-pulse history purged ocb-bench-audit on bridge-realized-cost: NOT READY, 4 blockers, 7 majors. - Blocker 4: Mobula's slippage was a constant negative offset at every ticket (-$0.0015 on Base at $3 and at $30) because its quoted fee summed TotalFeeUsd + GasFeeUsd, gas the realized fee (ticket minus landed) can never contain; Relay and LI.FI derived theirs from the output. Every bridge now uses ticket minus the output the quote promised (QuotedOutputUSD), so both sides of the subtraction are defined the same way. Gas we pay stays its own panel. - Blocker 3 and major 2: the "Realized fee" panel read bridge_fees_usd, which the quote loop also writes at $5/$50/$300 and on off-triangle routes (LI.FI $0.417 shown against $0.032 executed), and the realized output panel was a ticket-mix number. New execution-only pulses bridge_exec_realized_fee_usd and bridge_exec_realized_fee_bps; the panel is "Realized fee (bps of ticket)"; the output panel is gone. - Major 1 and 3: fee, output and slippage pulses are published only when the destination balance delta was read (RealizedOnChain); a failed leg no longer publishes the quote's projection as "landed" and a fallback is counted in bridge_realized_fallback_total instead of an exact zero. - Blocker 1: the 7-day window still held the pre-pulse persistent samples (about 2,880 per old execution against 3 per new one), so no new execution could move a median before 2026-09-26. Deleted from Prometheus for the execution cost series and the retired 24h rules; the benches read empty until the 10:00 UTC cycle tomorrow. - Major 4: LiveIndicator takes staleAfterSec from the bench (prometheus.expected_freshness_seconds, carried on Benchmark as expectedFreshnessSec), so a daily bench is not "Stale" 23 hours a day. - Major 5: Benchmark.window (prometheus.window) drives the Results caption, the infobox "Leader (7d)" and the citation suffix instead of a hardcoded 24h. - Copy: quoted-fee definition, gating, cohort of four, 7-day findings. Rules reloaded (14), harness redeployed. validate 236, tsc, eslint, bun test 255, go vet/build/test. Co-Authored-By: Claude Opus 5 (1M context) Claude-Session: https://claude.ai/code/session_01HJgbZCqjR4nvCfcJSzofbw --- benchmarks/bridge-execution-latency.yml | 8 ++-- benchmarks/bridge-realized-cost.yml | 33 ++++++-------- .../bridge-monitor/cmd/monitor/executor.go | 45 ++++++++++++++----- .../bridge-monitor/cmd/monitor/metrics.go | 16 +++++++ .../prometheus/ocb_bridge_execution.yml | 6 ++- src/app/benchmarks/[slug]/page.tsx | 6 ++- src/components/bench-infobox.tsx | 2 +- src/components/live-indicator.tsx | 7 ++- src/components/static-ledger.tsx | 5 ++- src/lib/citation.ts | 10 ++--- src/lib/materialize/load.ts | 2 + src/lib/spec.ts | 2 + src/types/benchmark.ts | 6 +++ 13 files changed, 103 insertions(+), 45 deletions(-) diff --git a/benchmarks/bridge-execution-latency.yml b/benchmarks/bridge-execution-latency.yml index 283cd621f..285212732 100644 --- a/benchmarks/bridge-execution-latency.yml +++ b/benchmarks/bridge-execution-latency.yml @@ -89,12 +89,12 @@ source: https://github.com/ChainBench/OpenChainBench/tree/main/harnesses/bridge- # are documented in the methodology until they accrue enough samples to panel. metric_panels: - id: fee - label: Realized fee - metric: avg by (bridge) (ocb:bridge_fees_usd:avg_7d{region="eu-west"}) + label: Realized fee (bps of ticket) + metric: avg by (bridge) (ocb:bridge_exec_realized_fee_bps:avg_7d{region="eu-west"}) label_key: bridge - unit: usd + unit: bps higher_is_better: false - description: "Amount sent minus amount actually received on the destination chain, in USD, averaged over the last 7 days of executions. The fee a user really paid, read on-chain rather than from the quote." + description: "Ticket minus the value that actually landed on the destination chain, in basis points of the ticket so the $3 and $30 executions compare, averaged over the last 7 days of settled executions whose destination balance was read on-chain." - id: slippage label: Slippage vs quote metric: avg by (bridge) (ocb:bridge_quote_slippage_usd:mean_7d{region="eu-west"}) diff --git a/benchmarks/bridge-realized-cost.yml b/benchmarks/bridge-realized-cost.yml index fc5feae23..0dc0352d8 100644 --- a/benchmarks/bridge-realized-cost.yml +++ b/benchmarks/bridge-realized-cost.yml @@ -4,7 +4,7 @@ slug: bridge-realized-cost number: "263" title: "Bridge slippage vs quote: what actually settles versus what was quoted" seo_title: "Bridge execution slippage 2026: realized cost vs the quote" -seo_description: "Live gap between a bridge quote and the real on-chain settlement for Mobula, Relay and LI.FI, plus the gas the user actually pays, measured on real executed USDC transfers across Solana, Base and Arbitrum from EU-West." +seo_description: "Live gap between a bridge quote and the real on-chain settlement for Mobula, Relay, LI.FI and Near Intents, plus the gas the user actually pays, measured on real executed USDC transfers across Solana, Base and Arbitrum from EU-West." subtitle: The gap between what a bridge quotes and what actually settles on-chain, in USD, on real executed USDC transfers, plus our own gas, at $3 and $30, measured from EU-West. category: Bridges status: live @@ -24,7 +24,7 @@ dimensions: seo_intro: | Every bridge quotes a cost. This benchmark measures whether that quote survives contact with the chain. We broadcast and settle real USDC - transfers through Mobula, Relay and LI.FI on a self-conserving triangle, + transfers through Mobula, Relay, LI.FI and Near Intents on a self-conserving triangle, then record the realized fee against the fee the quote promised. The headline is that gap, in dollars: a bridge whose settlement matches its quote sits near zero, a bridge that fills below quote reads positive, and @@ -45,23 +45,25 @@ abstract: | methodology: - "Route: a self-conserving all-USDC triangle (Solana to Base, Base to Arbitrum, Arbitrum to Solana). Each bridge completes the full triangle before the next starts, so inventory returns to origin and only fees burn." - - "Slippage vs quote: realized fee minus quote-projected fee, in USD (bridge_quote_slippage_usd). Realized fee is amount sent minus amount actually received on-chain. Near zero means the settlement matched the quote; positive means it settled worse; negative means it beat the quote." + - "Slippage vs quote: realized fee minus quoted fee, in USD (bridge_quote_slippage_usd). Realized fee is the ticket minus what actually landed on the destination, read from the balance delta; quoted fee is the ticket minus the output the quote promised, one definition for every bridge. Near zero means the settlement matched the quote; positive means it settled worse; negative means it beat the quote." + - "Why not the providers' own fee fields: some mix in gas paid in the native token, which the realized fee (ticket minus landed) can never contain, so one bridge read a constant negative offset at every ticket size. Both sides of the subtraction are now defined against the ticket." - "All-in gas: our own on-chain gas to execute (approve plus deposit), measured as the source-chain native balance delta before and after (bridge_exec_gas_usd). This is the cost the user bears on top of the bridge fee." - - "Realized output: the USD value that actually landed on the destination chain (bridge_realized_output_usd), waited for on-chain rather than taken from the quote." - - "Ticket sizes: $3 and $30. Small on purpose, since this measures the quote-to-settlement gap, not large-ticket depth." + - "Realized fee: ticket minus the value that landed on the destination chain, in basis points of the ticket (bridge_exec_realized_fee_bps), so $3 and $30 executions compare. Published only for settled executions whose destination balance was read on-chain; a leg whose credit was not visible within 30 s publishes nothing and is counted in bridge_realized_fallback_total." + - "Ticket sizes: $3 daily and $30 on Monday and Thursday, pooled. Small on purpose, since this measures the quote-to-settlement gap, not large-ticket depth. Window: 7 days, about 7 to 9 executions per bridge and corridor." - "Region: EU-West only. The execution node runs a single wallet, so a second origin would race the same inventory." - - "Cohort: Mobula, Relay and LI.FI, the bridges executed on all three legs. Quote-only providers are covered by the bridge-fee and bridge-quote-latency benchmarks." + - "Cohort: Mobula, Relay, LI.FI and Near Intents, the bridges executed on all three legs. Quote-only providers are covered by the bridge-fee and bridge-quote-latency benchmarks." findings: - - "{{name:mobula}} settles within {{p50:mobula}} of its quote (median, 24 h) on executed USDC transfers." + - "{{name:mobula}} settles within {{p50:mobula}} of its quote (median, 7 d) on executed USDC transfers." - "{{name:relay}} settles within {{p50:relay}} of its quote." - "{{name:lifi}} settles within {{p50:lifi}} of its quote." + - "{{name:near-intents}} settles within {{p50:near-intents}} of its quote." faq: - q: "How is this different from the bridge fee benchmark?" a: "The fee benchmark measures the cost a quote advertises. This one measures the cost that actually settled on-chain and subtracts what the quote promised, so it captures the gap a user only discovers after they commit. A bridge can quote cheap and settle worse, and only an execution benchmark surfaces that." - q: "Why can the number be negative?" - a: "Because a bridge can settle better than it quoted. Slippage vs quote is realized fee minus quoted fee, so a solver that fills above the quoted output makes the figure negative. Near zero means the quote was honest; positive means the execution cost more than promised." + a: "Because a bridge can settle better than it quoted. Slippage vs quote is realized fee minus quoted fee, both defined against the ticket (ticket minus landed, ticket minus quoted output), so a solver that fills above the quoted output makes the figure negative. Near zero means the quote was honest; positive means the execution cost more than promised." - q: "Does this include the gas I pay?" a: "The headline is the bridge's own quote-to-settlement gap. Our own execution gas (approve plus deposit) is reported separately in the all-in gas panel, measured as the real native-token balance delta, because it is a cost the user bears on top of the bridge fee." - q: "Where does this run from?" @@ -71,12 +73,12 @@ source: https://github.com/ChainBench/OpenChainBench/tree/main/harnesses/bridge- metric_panels: - id: fee - label: Realized fee - metric: avg by (bridge) (ocb:bridge_fees_usd:avg_7d{region="eu-west"}) + label: Realized fee (bps of ticket) + metric: avg by (bridge) (ocb:bridge_exec_realized_fee_bps:avg_7d{region="eu-west"}) label_key: bridge - unit: usd + unit: bps higher_is_better: false - description: "Amount sent minus amount actually received on the destination chain, in USD, averaged over the last 7 days of executions. The fee a user really paid, read on-chain rather than from the quote." + description: "Ticket minus the value that actually landed on the destination chain, in basis points of the ticket so the $3 and $30 executions compare, averaged over the last 7 days of settled executions whose destination balance was read on-chain." - id: gas label: All-in gas we paid metric: avg by (bridge) (ocb:bridge_exec_gas_usd:avg_7d{region="eu-west"}) @@ -84,13 +86,6 @@ metric_panels: unit: usd higher_is_better: false description: "Our own on-chain gas (approve plus deposit), measured as the source-chain native balance delta. The cost the user bears on top of the bridge fee." - - id: realized - label: Realized output on-chain - metric: avg by (bridge) (ocb:bridge_realized_output_usd:avg_7d{region="eu-west"}) - label_key: bridge - unit: usd - higher_is_better: true - description: "The USD value that actually landed on the destination chain, waited for on-chain rather than read from the quote." prometheus: window: 7d diff --git a/harnesses/bridge-monitor/cmd/monitor/executor.go b/harnesses/bridge-monitor/cmd/monitor/executor.go index 9f01c03dc..b20932d0e 100644 --- a/harnesses/bridge-monitor/cmd/monitor/executor.go +++ b/harnesses/bridge-monitor/cmd/monitor/executor.go @@ -41,9 +41,11 @@ type ExecutionResult struct { ExecutionLatencyMs int64 // Settlement latency: destination block timestamp minus source block timestamp (on-chain); wall clock when a hash is missing ObservedLatencyMs int64 // Wall clock from broadcast to the poll that saw the terminal status (the pre-2026-09-19 figure), kept for the audit trail DestTxHash string - LatencyMethod string // "watch" (credit observed minus source inclusion observed, ms) | "watch-broadcast" (credit minus broadcast) | "blocks" (block timestamp delta) | "poll" - OnchainBlockDeltaMs int64 // destination block timestamp minus source block timestamp, -1 when unknown - E2ELatencyMs int64 // Time from quote start to funds received + LatencyMethod string // "watch" (credit observed minus source inclusion observed, ms) | "watch-broadcast" (credit minus broadcast) | "blocks" (block timestamp delta) | "poll" + OnchainBlockDeltaMs int64 // destination block timestamp minus source block timestamp, -1 when unknown + QuotedOutputUSD float64 // what the quote said would land (USD), before any on-chain read + RealizedOnChain bool // OutputUSD and ActualFeeUSD come from the destination balance delta, not from the quote + E2ELatencyMs int64 // Time from quote start to funds received Success bool Reverted bool Refunded bool // subset of Reverted: provider returned capital (status "refunded") @@ -438,6 +440,17 @@ func (e *Executor) executeOnBridge(bridge string, route TestRoute, amount, amoun // Prometheus correctly classify it (Reverted takes precedence over Success). result.Success = !result.Reverted + // One definition of the quoted fee for every bridge: ticket minus the + // output the quote promised. Provider fee sums mixed in gas paid in the + // native token (Mobula: TotalFeeUsd + GasFeeUsd), which the realized fee + // (ticket minus landed) can never contain, so their slippage read a + // constant negative offset regardless of ticket (-$0.0015 at $3 and at + // $30 on Base). Gas we pay ourselves is its own panel. + result.QuotedOutputUSD = result.OutputUSD + if amountUSD > 0 && result.QuotedOutputUSD > 0 && result.QuotedOutputUSD <= amountUSD { + result.QuoteFeeUSD = amountUSD - result.QuotedOutputUSD + } + // Read the destination balance again to compute the REALIZED fill on-chain. // Bridge status "filled" sometimes precedes the destination credit by 1-3 // blocks; pollRealizedFill waits up to 30s for the delta to materialise. @@ -451,6 +464,7 @@ func (e *Executor) executeOnBridge(bridge string, route TestRoute, amount, amoun realizedUSD := realizedToken * destinationUSDPerToken(route) log.Printf(" 💰 Realized fill on-chain: %.6f tokens = $%.4f (quote projected $%.4f)", realizedToken, realizedUSD, result.OutputUSD) result.OutputUSD = realizedUSD + result.RealizedOnChain = true // Recompute fees from realized: amount sent - amount received realFees := amountUSD - realizedUSD if realFees < 0 { @@ -1015,12 +1029,23 @@ func (e *Executor) recordExecutionMetrics(result *ExecutionResult) { } // Record fees + the new execution-cost metrics - pulse(bridgeFeesUSD, labels, result.ActualFeeUSD) - if result.AmountUSD > 0 { - pulse(bridgeFeesPercent, labels, (result.ActualFeeUSD/result.AmountUSD)*100) - } - if result.OutputUSD > 0 { - pulse(bridgeRealizedOutputUSD, labels, result.OutputUSD) + // Realized cost metrics come from the destination balance delta only. + // A failed leg, a 30 s destination poll miss or a pre-balance read + // failure leaves OutputUSD at the quote's projection: published as + // "realized", that was a quote number wearing an on-chain label (Relay + // $30 Base: one errored leg published $29.97 of "landed" value). + if result.Success && result.RealizedOnChain { + pulse(bridgeFeesUSD, labels, result.ActualFeeUSD) + pulse(bridgeExecRealizedFeeUSD, labels, result.ActualFeeUSD) + if result.AmountUSD > 0 { + pulse(bridgeFeesPercent, labels, (result.ActualFeeUSD/result.AmountUSD)*100) + pulse(bridgeExecRealizedFeeBps, labels, (result.ActualFeeUSD/result.AmountUSD)*10000) + } + if result.OutputUSD > 0 { + pulse(bridgeRealizedOutputUSD, labels, result.OutputUSD) + } + } else if result.Success { + bridgeRealizedFallback.WithLabelValues(result.Bridge, result.FromChain, result.ToChain, e.region).Inc() } // Execution slippage vs quote = realized fee - quote-projected fee. Only on // a real fill: on a revert / refund / pre-broadcast failure there is no @@ -1028,7 +1053,7 @@ func (e *Executor) recordExecutionMetrics(result *ExecutionResult) { // it would inject spurious 0 / negative samples into the realized-cost bench. // Slippage is only meaningful against a quoted fee; Near Intents used to // publish its whole realized fee here (QuoteFeeUSD was never set). - if result.Success && result.QuoteFeeUSD > 0 { + if result.Success && result.RealizedOnChain && result.QuoteFeeUSD > 0 { pulse(bridgeQuoteSlippageUSD, labels, result.ActualFeeUSD-result.QuoteFeeUSD) } if result.ExecGasUSD > 0 { diff --git a/harnesses/bridge-monitor/cmd/monitor/metrics.go b/harnesses/bridge-monitor/cmd/monitor/metrics.go index 2beb60316..f1bec6c39 100644 --- a/harnesses/bridge-monitor/cmd/monitor/metrics.go +++ b/harnesses/bridge-monitor/cmd/monitor/metrics.go @@ -143,6 +143,22 @@ var ( Help: "Settled executions whose published latency did not come from the two-ended watch, by method (watch-broadcast: source inclusion missed; blocks: block-timestamp delta; poll: wall clock to the status poll)", }, []string{"bridge", "from_chain", "to_chain", "region", "method"}) + // Execution-only realized fee (bridge_fees_usd is also written by the + // quote loop at $5/$50/$300 and off-triangle routes, so a panel reading + // it averaged quotes into "realized"). Pulses. + bridgeExecRealizedFeeUSD = promauto.NewGaugeVec(prometheus.GaugeOpts{ + Name: "bridge_exec_realized_fee_usd", + Help: "Realized fee of one settled execution in USD: ticket minus the value that landed on the destination, read from the balance delta (pulse)", + }, []string{"bridge", "from_chain", "to_chain", "from_token", "to_token", "amount_usd", "region", "chain"}) + bridgeExecRealizedFeeBps = promauto.NewGaugeVec(prometheus.GaugeOpts{ + Name: "bridge_exec_realized_fee_bps", + Help: "Realized fee of one settled execution in basis points of the ticket (comparable across $3 and $30) (pulse)", + }, []string{"bridge", "from_chain", "to_chain", "from_token", "to_token", "amount_usd", "region", "chain"}) + bridgeRealizedFallback = promauto.NewCounterVec(prometheus.CounterOpts{ + Name: "bridge_realized_fallback_total", + Help: "Settled executions whose destination balance could not be read (pre-balance read failed or the credit was not visible within 30 s): no realized fee, output or slippage published for them", + }, []string{"bridge", "from_chain", "to_chain", "region"}) + // Persistent: unix time of the last completed execution per bridge and // region, the site's freshness source (prometheus.freshness_timestamp_metric). bridgeLastExecutionTs = promauto.NewGaugeVec(prometheus.GaugeOpts{ diff --git a/infrastructure/monitoring/prometheus/ocb_bridge_execution.yml b/infrastructure/monitoring/prometheus/ocb_bridge_execution.yml index fc7092c8b..51d71e6ee 100644 --- a/infrastructure/monitoring/prometheus/ocb_bridge_execution.yml +++ b/infrastructure/monitoring/prometheus/ocb_bridge_execution.yml @@ -33,7 +33,9 @@ groups: expr: avg_over_time(bridge_exec_gas_usd[7d]) - record: ocb:bridge_realized_output_usd:avg_7d expr: avg_over_time(bridge_realized_output_usd[7d]) - - record: ocb:bridge_fees_usd:avg_7d - expr: avg_over_time(bridge_fees_usd[7d]) + - record: ocb:bridge_exec_realized_fee_usd:avg_7d + expr: avg_over_time(bridge_exec_realized_fee_usd[7d]) + - record: ocb:bridge_exec_realized_fee_bps:avg_7d + expr: avg_over_time(bridge_exec_realized_fee_bps[7d]) - record: ocb:bridge_exec_onchain_ms:mean_7d expr: avg_over_time(bridge_exec_onchain_ms[7d]) diff --git a/src/app/benchmarks/[slug]/page.tsx b/src/app/benchmarks/[slug]/page.tsx index 4a67f2015..1d1f956c3 100644 --- a/src/app/benchmarks/[slug]/page.tsx +++ b/src/app/benchmarks/[slug]/page.tsx @@ -549,7 +549,11 @@ export default async function BenchmarkPage({ )} {!isDraft && !insufficient && ( - + )} diff --git a/src/components/bench-infobox.tsx b/src/components/bench-infobox.tsx index c5e63bdcc..9f14ec212 100644 --- a/src/components/bench-infobox.tsx +++ b/src/components/bench-infobox.tsx @@ -122,7 +122,7 @@ export function BenchInfobox({ benchmark }: { benchmark: Benchmark }) { ) : null} {top && ( - + {top.name} {" "} diff --git a/src/components/live-indicator.tsx b/src/components/live-indicator.tsx index 873017dec..f856f97d2 100644 --- a/src/components/live-indicator.tsx +++ b/src/components/live-indicator.tsx @@ -19,7 +19,12 @@ import { useEffect, useState } from "react"; export function LiveIndicator({ lastRunAt, slug, + staleAfterSec = 300, }: { + /** Seconds after which the dot turns "Stale". Benches whose data moves + * once a day (bridge executions) pass their expected freshness; the + * default is the 5-minute ISR rhythm. */ + staleAfterSec?: number; lastRunAt: string; slug?: string; }) { @@ -72,7 +77,7 @@ export function LiveIndicator({ }, [slug]); const ageSec = Math.max(0, Math.floor((now - new Date(canonical).getTime()) / 1000)); - const stale = ageSec > 300; + const stale = ageSec > staleAfterSec; return ( diff --git a/src/components/static-ledger.tsx b/src/components/static-ledger.tsx index d76bd745a..f87ccb4d3 100644 --- a/src/components/static-ledger.tsx +++ b/src/components/static-ledger.tsx @@ -19,11 +19,12 @@ export function StaticLedger({ benchmark }: { benchmark: Benchmark }) { if (rows.length === 0) return null; const chain = rpcChainLabel(benchmark); const pausedOn = isStaleBench(benchmark) && benchmark.lastRunAt ? benchmark.lastRunAt.slice(0, 10) : null; + const win = benchmark.window ?? "24h"; const heading = chain && pausedOn ? `Results: measurement paused since ${pausedOn}, last ranking of ${rows.length} free public ${chain} RPC endpoint${rows.length === 1 ? "" : "s"}` : chain ? `Results: ${rows.length} free public ${chain} RPC endpoint${rows.length === 1 ? "" : "s"} ranked by p50 latency (24h, 3 regions)` - : `Results: ${rows.length} providers ranked by ${benchmark.metric} (p50, 24h)`; + : `Results: ${rows.length} providers ranked by ${benchmark.metric} (p50, ${win})`; const showTail = benchmark.unit === "ms" || benchmark.unit === "s"; return (
@@ -33,7 +34,7 @@ export function StaticLedger({ benchmark }: { benchmark: Benchmark }) {
diff --git a/src/lib/citation.ts b/src/lib/citation.ts index 1d4961e8a..2a0842f34 100644 --- a/src/lib/citation.ts +++ b/src/lib/citation.ts @@ -109,10 +109,10 @@ export function leader(b: Benchmark): { name: string; slug: string; value: numbe /** Honest window wording per unit. "(p50, 24h)" is only true for latency * style benches; USD revenue and count benches repurpose the p50 slot as * a plain rolling-window figure and percentile wording would mislead. */ -function windowSuffix(unit: string): string { - if (unit === "usd" || unit === "count") return "(24h)"; - if (unit === "pct" || unit === "bps") return "(24h avg)"; - return "(p50, 24h)"; +function windowSuffix(unit: string, window = "24h"): string { + if (unit === "usd" || unit === "count") return `(${window})`; + if (unit === "pct" || unit === "bps") return `(${window} avg)`; + return `(p50, ${window})`; } /** Short factual sentence ready to paste into an article. Templated, no LLM. */ @@ -177,7 +177,7 @@ export function headlineParts(b: Benchmark): { claim: string; rest: string } { const verb = b.higherIsBetter ? "leads" : "posts the lowest"; return { claim: `${top.name} ${verb} ${metricInSentence(b.metric)} at ${value}`, - rest: `${windowSuffix(b.unit)} on ${b.title}.`, + rest: `${windowSuffix(b.unit, b.window ?? "24h")} on ${b.title}.`, }; } diff --git a/src/lib/materialize/load.ts b/src/lib/materialize/load.ts index e84f52ec5..65cd03b73 100644 --- a/src/lib/materialize/load.ts +++ b/src/lib/materialize/load.ts @@ -151,6 +151,8 @@ export function buildEditorial( disclaimer: spec.disclaimer, faq: spec.faq, excludedProviders: spec.excluded_providers, + window: spec.prometheus?.window, + expectedFreshnessSec: spec.prometheus?.expected_freshness_seconds, perChainExplainer: spec.per_chain_explainer, subtitle: spec.subtitle, category: spec.category, diff --git a/src/lib/spec.ts b/src/lib/spec.ts index 932215959..3eb394d1b 100644 --- a/src/lib/spec.ts +++ b/src/lib/spec.ts @@ -135,6 +135,8 @@ export function overlayEditorial(stored: Benchmark, spec: Spec): Benchmark { seoIntro: spec.seo_intro ?? stored.seoIntro, faq: spec.faq ?? stored.faq, excludedProviders: spec.excluded_providers ?? stored.excludedProviders, + window: spec.prometheus?.window ?? stored.window, + expectedFreshnessSec: spec.prometheus?.expected_freshness_seconds ?? stored.expectedFreshnessSec, perChainExplainer: spec.per_chain_explainer ?? stored.perChainExplainer, abstract: spec.abstract ?? stored.abstract, methodology: spec.methodology ?? stored.methodology, diff --git a/src/types/benchmark.ts b/src/types/benchmark.ts index b7ee57378..98ed02d7a 100644 --- a/src/types/benchmark.ts +++ b/src/types/benchmark.ts @@ -238,6 +238,12 @@ export type Benchmark = { /** Providers audited and not listed, with the reason. Rendered under * the public endpoints table; never carries a URL (schema-refused). */ excludedProviders?: { name: string; reason: string; since?: string }[]; + /** Aggregation window of the headline queries ("24h" default, "7d" on the + * bridge execution benches). Drives the "(p50, 24h)" wording. */ + window?: string; + /** Seconds after which the data counts as stale for this bench (from + * prometheus.expected_freshness_seconds; 600 when unset). */ + expectedFreshnessSec?: number; /** Optional per-chain explainer blocks rendered as H2-anchored sections * below the main chart. Targets long-tail "X chain {metric}" queries * that benefit from a dedicated on-page anchor (#ethereum, #solana, ...).
- {benchmark.title}: {benchmark.metric} per provider, last 24 hours. + {benchmark.title}: {benchmark.metric} per provider, last {win === "24h" ? "24 hours" : win}.