diff --git a/benchmarks/bridge-execution-latency.yml b/benchmarks/bridge-execution-latency.yml
index 283cd621..28521273 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 fc5feae2..0dc0352d 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 9f01c03d..b20932d0 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 2beb6031..f1bec6c3 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 fc7092c8..51d71e6e 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 4a67f201..1d1f956c 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 c5e63bdc..9f14ec21 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 873017de..f856f97d 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 d76bd745..f87ccb4d 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 }) {
- {benchmark.title}: {benchmark.metric} per provider, last 24 hours.
+ {benchmark.title}: {benchmark.metric} per provider, last {win === "24h" ? "24 hours" : win}.
diff --git a/src/lib/citation.ts b/src/lib/citation.ts
index 1d4961e8..2a0842f3 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 e84f52ec..65cd03b7 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 93221595..3eb394d1 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 b7ee5737..98ed02d7 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, ...).