Skip to content

Ship: anti-cache RPC probe, keyed bench 069, indexing-freshness 070, Monad+MegaETH 071-072, explorer coverage 068 - #976

Closed
Flotapponnier wants to merge 167 commits into
mainfrom
dev
Closed

Ship: anti-cache RPC probe, keyed bench 069, indexing-freshness 070, Monad+MegaETH 071-072, explorer coverage 068#976
Flotapponnier wants to merge 167 commits into
mainfrom
dev

Conversation

@Flotapponnier

Copy link
Copy Markdown
Collaborator

Contenu du ship

  • Probe RPC anti-cache (eth_getBlockByNumber + rotating id) sur les 23 chains + purge données ère-cache
  • Bench 068 explorer-chain-coverage (ecc)
  • Bench 069 rpc-keyed-latency: Alchemy/Infura/Chainstack/Ankr/QuickNode/Helius, 3 régions pinnées, plan disclosures
  • Bench 070 indexing-freshness: Mobula/Zerion/Moralis/GoldRush/Allium, ground truth organique
  • Benches 071-072 monad-rpc + megaeth-rpc + hub /rpc + chain hubs + pages product (bloXroute, OnFinality, officials, Allium, Zerion, GoldRush, QuickNode, Chainstack, Infura, Ankr)
  • Harness agg-latency: métriques reconnect WS + grace period
  • Logos + registry pour toute la cohorte

Post-merge

vercel --prod manuel (ce PR ne déploie rien automatiquement)

Flotapponnier and others added 30 commits June 26, 2026 14:41
…740)

After dev → main reconciliation (PRs A-E), dev was hard-reset to
main. This commit re-introduces the 4 production-ready benchmarks
that lived on the old dev branch as staging-only content. They will
NOT be promoted to main until they pass staging review.

Benches:
- bridge-revenue.yml (188L, status: live) — bridge revenue tracking
- evm-quote-latency.yml (194L, status: live) — EVM aggregator quote
  latency (Mobula vs Codex vs GeckoTerminal etc.)
- solana-dex-quote-latency.yml (192L, status: live) — Solana DEX
  quote latency (Jupiter vs Mobula vs others)
- solana-tx-landing.yml (218L, status: live) — Solana tx landing
  benchmark

Companion editorial content (for SEO long-tail):
- answers/which-evm-aggregator-has-the-fastest-quote.yml
- answers/which-solana-dex-aggregator-is-the-fastest.yml
- alternatives/jupiter.yml

Workflow going forward:
- main = prod (openchainbench.com), source of truth
- dev = staging buffer (auto-deploy to staging Preview URL)
- These 4 benches live on dev. When approved, open a dedicated
  PR cherry-picking them to main.
- Future feature work: open a feature branch from dev (or main),
  PR to dev for staging review, then promote to main.

Co-authored-by: Florent Tapponnier <contact@mobula.io>
… 043)

Adds 8 venues sourced via Mobula's CEFI funding-rate aggregator (perp_venue_funding_24h_bps, freshness via perp_venue_last_refresh_unix):
- Bitget, Coinbase, Deribit, Gate, Kraken, KuCoin, Lighter, MEXC

Same stddev-over-time semantics on a 24h window, BTC/ETH/SOL split per asset. Cohort jumps from 7 to 15 venues.

Logos: 5 PNGs sourced from CoinMarketCap exchange CDN (bitget, deribit, gate, kraken, mexc); 2 SVGs from simpleicons (coinbase, kucoin); 1 SVG from simpleicons for binance (was reusing /logos/bnb.png, the chain logo, not the exchange brand).

Bench copy + manifest entries updated.
Dev was force-pushed after PR #730 and the venue support in BenchmarkFilters (materialize/filters.ts) was lost in the spec/snapshot refactor PR #739. The worker still references f.venue and dims.venue, so typecheck failed. Re-add venue across:
- BenchmarkFilters union (load.ts)
- parseFilterSig allowlist
- Spec.dimensions schema (spec-schema.ts) so YAML venue dim parses
feat(perp-funding-stability): scale to 15 venues (+8 via Mobula) + logos
…ble in dark mode)

simpleicons SVGs are monochrome (renders black) so coinbase and kucoin logos vanished in dark mode. Replace with the colored 200x200 PNGs from the CoinMarketCap exchange CDN, same source as bitget/deribit/gate/kraken/mexc.
…ored

fix(logos): coinbase + kucoin colored PNG (was invisible in dark mode)
PR #743 created src/app/benchmarks/category/[cat]/page.tsx + benchmark-grid.tsx referencing @/lib/categories but the helper module was not cherry-picked to main, breaking typecheck.

Also re-apply binance: /logos/binance.svg (was reverted by auto-merge against pre-existing main entry pointing to /logos/bnb.png).
* feat(sample-health): wire UI badge + API surface + insufficient silencing (follow-up A) (#741)

Completes PR #732 (sample-health system) by surfacing the computed
dataConfidence/sampleHealth/expectedN to users + machine-readable
consumers. Was dead-code on the data layer until this PR.

UI (ledger-table.tsx):
- Filter rows tagged 'insufficient' out of the sorted ranking.
- 'Low sample' pill on rows with dataConfidence='low' + tooltip
  showing the actual sample-health ratio ('45% of expected').

APIs:
- /api/citable: returns expectedN + dataConfidence per row. When
  bench-wide aggregate is 'insufficient', returns value=null +
  leader=null (refuses to crown a leader from undersized samples).
- /api/stat/[slug]: same treatment + drops 'insufficient' rows
  from the machine-readable ranking array. Per-row sampleHealth +
  dataConfidence exposed for downstream tooling.

Library (citation.ts):
- citationCandidates() drops 'insufficient' rows from the leader
  candidate set.
- fieldValue/leader/headlineSentence return null/'insufficient
  data' when bench-wide is insufficient.
- isInsufficient() helper preserved from main (used by hub cards +
  /api/citable hot path).

Effect: bench pages where YAML declares expected_n now visually
communicate sample confidence; agents/journalists hitting the
citable API can't accidentally quote a leader drawn from 3 data
points; the methodology page's 'n >= 1000' claim becomes honest.

Co-authored-by: Florent Tapponnier <contact@mobula.io>

* feat(seo): /benchmarks/category/[cat] hub pages + BenchmarkGrid lockedCategory (D.2) (#743)

Final cherry-pick from dev-archive — adds 7 new SEO-indexable hub
pages (one per benchmark category) that target long-tail queries
like 'best crypto aggregator benchmarks 2026'.

D.2 was deferred from PR D because the BenchmarkCard ↔ MiniChart ↔
BenchmarkCardData type chain had cascading TS errors. Follow-up A
(citation.ts isInsufficient → InsufficientCheckInput structural
subset) silently fixed those, so this lands cleanly now.

Files:
- src/app/benchmarks/category/[cat]/page.tsx (new) — server route
  that renders BenchmarkGrid pre-filtered to one category + breadcrumb
  + ItemList JSON-LD. generateStaticParams builds the 7 hub routes
  from CATEGORIES. Each page is fully indexable (canonical, OG, etc.).
- src/components/benchmark-grid.tsx (updated) — adds two optional
  props (lockedCategory, allCategories). Backward compatible: when
  unset the grid behaves exactly as before (used on /benchmarks).
  When set (category hub routes), the grid pre-filters and shows the
  full category pill list so users can hop between hubs.

URLs that ship after deploy:
- /benchmarks/category/aggregators
- /benchmarks/category/bridges
- /benchmarks/category/blockchains
- /benchmarks/category/trading
- /benchmarks/category/wallets
- /benchmarks/category/rpcs
- /benchmarks/category/nft-apis

Each renders the canonical bench grid filtered to that category with
its own H1, meta description, and structured data. Sitemap pickup
automatic on next regen.

Co-authored-by: Florent Tapponnier <contact@mobula.io>

* fix(seo): blacklist hex builder slugs and shared composite filter

Drop 15 anonymous 0x... HL builder slugs and 5 dead composite slugs
from /products listing, sitemap and individual /products/<slug> routes.
DEAD_COMPOSITE_SLUGS now lives in providers.ts as the single source of
truth; related-providers.ts imports it. Bumps providers cache key v3->v4.

---------

Co-authored-by: Florent Tapponnier <contact@mobula.io>
* feat(sample-health): wire UI badge + API surface + insufficient silencing (follow-up A) (#741)

Completes PR #732 (sample-health system) by surfacing the computed
dataConfidence/sampleHealth/expectedN to users + machine-readable
consumers. Was dead-code on the data layer until this PR.

UI (ledger-table.tsx):
- Filter rows tagged 'insufficient' out of the sorted ranking.
- 'Low sample' pill on rows with dataConfidence='low' + tooltip
  showing the actual sample-health ratio ('45% of expected').

APIs:
- /api/citable: returns expectedN + dataConfidence per row. When
  bench-wide aggregate is 'insufficient', returns value=null +
  leader=null (refuses to crown a leader from undersized samples).
- /api/stat/[slug]: same treatment + drops 'insufficient' rows
  from the machine-readable ranking array. Per-row sampleHealth +
  dataConfidence exposed for downstream tooling.

Library (citation.ts):
- citationCandidates() drops 'insufficient' rows from the leader
  candidate set.
- fieldValue/leader/headlineSentence return null/'insufficient
  data' when bench-wide is insufficient.
- isInsufficient() helper preserved from main (used by hub cards +
  /api/citable hot path).

Effect: bench pages where YAML declares expected_n now visually
communicate sample confidence; agents/journalists hitting the
citable API can't accidentally quote a leader drawn from 3 data
points; the methodology page's 'n >= 1000' claim becomes honest.

Co-authored-by: Florent Tapponnier <contact@mobula.io>

* feat(seo): /benchmarks/category/[cat] hub pages + BenchmarkGrid lockedCategory (D.2) (#743)

Final cherry-pick from dev-archive — adds 7 new SEO-indexable hub
pages (one per benchmark category) that target long-tail queries
like 'best crypto aggregator benchmarks 2026'.

D.2 was deferred from PR D because the BenchmarkCard ↔ MiniChart ↔
BenchmarkCardData type chain had cascading TS errors. Follow-up A
(citation.ts isInsufficient → InsufficientCheckInput structural
subset) silently fixed those, so this lands cleanly now.

Files:
- src/app/benchmarks/category/[cat]/page.tsx (new) — server route
  that renders BenchmarkGrid pre-filtered to one category + breadcrumb
  + ItemList JSON-LD. generateStaticParams builds the 7 hub routes
  from CATEGORIES. Each page is fully indexable (canonical, OG, etc.).
- src/components/benchmark-grid.tsx (updated) — adds two optional
  props (lockedCategory, allCategories). Backward compatible: when
  unset the grid behaves exactly as before (used on /benchmarks).
  When set (category hub routes), the grid pre-filters and shows the
  full category pill list so users can hop between hubs.

URLs that ship after deploy:
- /benchmarks/category/aggregators
- /benchmarks/category/bridges
- /benchmarks/category/blockchains
- /benchmarks/category/trading
- /benchmarks/category/wallets
- /benchmarks/category/rpcs
- /benchmarks/category/nft-apis

Each renders the canonical bench grid filtered to that category with
its own H1, meta description, and structured data. Sitemap pickup
automatic on next regen.

Co-authored-by: Florent Tapponnier <contact@mobula.io>

* fix(seo): drop dead /alternatives links from perp/PM venue footers + dead compare pair

Two SEO regressions feeding 404s into Google's crawl:

1. /products/<slug> for perp/PM venues without an alternatives YAML
   (lighter, manifold, vertex, grvt, ostium, variational, pacifica,
   aevo, edgex, paradex, extended, myriad, limitless, aster) hardcoded
   a /alternatives/<slug> footer link in PmVenueSection and
   PerpVenueSection. Now gated on loadAlternativeSlugs() membership so
   only venues with an actual YAML emit the link.

2. /compare hub rendered jupiter-vs-raydium even though raydium has
   no bench appearances, so /compare/jupiter-vs-raydium 404s
   downstream. Pre-filter COMPARE_PAIRS against getProvider() so dead
   pairs disappear from the list and the JSON-LD ItemList until both
   sides resolve.

Plus a defensive guard in buildAlternativesReverseMap() so the
emitted alt.slug is always a known live YAML, even if the alt list
source ever changes shape.

---------

Co-authored-by: Florent Tapponnier <contact@mobula.io>
Co-authored-by: Florent Tapponnier <contact@mobula.io>
chore(dev): sync main → dev (bring SEO hotfixes + canonical fixes)
…s to sitemap (#764)

Co-authored-by: Florent Tapponnier <contact@mobula.io>
…ge (#766)

Drop in #701 (2026-06-25) was driven by harness offline + 404 SEO. The Railway harness still runs and Prom keeps emitting solana_landing_probe_enabled=1 plus the slot/latency series, so restoring the YAML brings the page back live (or in 'awaiting samples' until a funded SOLANA_PROBE_KEYPAIR_BASE58 is set on Railway). Validate clean (34 specs).
When a chain rename rolls through Prom while the 24h window still holds samples on the legacy label (e.g. ton → gram during the harness redeploy lag), the loader rewrote each ton row to gram but did not dedupe — so /benchmarks/l1-finality rendered Gram twice (one row per source series).

Two-pass: rewrite then dedupe by canonical slug, preferring the row whose original slug already matched the canonical (current harness emit) over the alias-rewritten historical row.
fix(spec): dedupe results after legacy chain slug rewrite
Numeric column headers in LedgerTable now toggle the table's sort
on click. First click on a header descends on its value, second
click toggles to ascending. An up/down chevron next to the active
header's label marks the direction; hover lifts ink-muted to ink
so the headers read as interactive.

Sortable columns: Name (A-Z), every customCols entry (per-index
key), Value (when a panel tab is active), p50/p90/p99/Mean,
Delta field, Success, and Slot delta p50 (when hasSlots). Trend
and Number columns stay static.

The default sort (sortKey null) preserves the exact prior
comparator so OG renders, share cards, and snapshot tests keep
matching. A non-null sortKey routes through a new pickSortValue
helper instead. Missing values land at +/-Infinity so empty cells
consistently sort to the bottom regardless of direction.
feat(ledger): clickable column headers to sort leaderboard
feat(hl-frontends): long-window archive UI toggle + history API
…dev-hl-frontends

# Conflicts:
#	src/components/ledger-table.tsx
…rontends

chore(dev): sync main → dev (HL frontends + cache bumps)
…ab click (#777)

Co-authored-by: Florent Tapponnier <contact@mobula.io>
…i/series (#779)

Co-authored-by: Florent Tapponnier <contact@mobula.io>
The shared Upstash free-tier KV hit its 500k/day request cap; the Go service's PushUpstash silently failed with 'max requests limit exceeded' and the bench page stayed on 'archive_pending' for the long windows.

Swap the reader to call the hl-archive Railway HTTPS API (/v1/aggregates?window=all) directly. Same payload shape, one less moving part, no quota constraint, no JSON-encoded-string detour. Next.js unstable_cache (60s revalidate) still bounds the per-region request load to the Railway service.

Two new env vars on Vercel:
- HL_ARCHIVE_API_URL (e.g. https://hl-archive-production.up.railway.app)
- HL_ARCHIVE_API_KEY (matches the Go service's HL_ARCHIVE_API_KEY)

Existing Upstash env (KV_REST_API_URL / UPSTASH_*) is no longer read by this module.
feat(hl-archive): read directly from Railway service (skip Upstash)
Fold the standalone HlHistoryToggle into the chart pills so there is a
single source of truth for the active window. Short windows (1H..30D)
keep the existing Prom-sourced chart + ledger. Long windows (90D..ALL)
swap the chart data source to the archive's per-builder daily timeseries
and replace the ledger with the archive-ranked leaderboard.

- chart pills extended with 90D 180D 1Y ALL behind a hairline separator
- archive fetch lifted to BenchmarkBody, payload reused for chart + ledger
- /history API now passes through timeseries_daily on archive responses
- HlHistoryToggle removed, behaviour split into HlArchiveLeaderboard +
  the chart's new longRangeSeries prop
- long-range pills disable with a 'soon' hint when the archive is down
Flotapponnier and others added 27 commits July 7, 2026 13:00
…ts 20k credits/month) (#949)

Co-authored-by: Florent Tapponnier <contact@mobula.io>
…es (#950)

Co-authored-by: Florent Tapponnier <contact@mobula.io>
* rpc: anti-cache probe — eth_getBlockByNumber(latest) with rotating id replaces eth_blockNumber

* remove WIP explorer-chain-coverage spec, included by mistake

---------

Co-authored-by: Florent Tapponnier <contact@mobula.io>
…top-50 view, new Explorers category (#952)

Co-authored-by: Florent Tapponnier <contact@mobula.io>
…d broke) (#953)

Co-authored-by: Florent Tapponnier <contact@mobula.io>
… square mark), keyless registered for etherscan+subscan (#954)

Co-authored-by: Florent Tapponnier <contact@mobula.io>
…955)

Co-authored-by: Florent Tapponnier <contact@mobula.io>
… access) (#956)

Co-authored-by: Florent Tapponnier <contact@mobula.io>
…nce, etherscan chain-id allowlist, disclosed ceilings (#957)

Co-authored-by: Florent Tapponnier <contact@mobula.io>
* ecc: validity fixes — freshness ladder 60m/5m, quiet-chain second chance, etherscan chain-id allowlist, disclosed ceilings

* rpc-keyed-latency: free-tier keyed RPC harness (env-driven matrix, 90% quota guard, anti-cache probe)

* prom: scrape rpc-keyed-latency (3 regions)

* bench 069: rpc-keyed-latency spec (draft until 24h of data)

---------

Co-authored-by: Florent Tapponnier <contact@mobula.io>
… days) (#959)

Co-authored-by: Florent Tapponnier <contact@mobula.io>
…e specs) (#960)

Co-authored-by: Florent Tapponnier <contact@mobula.io>
…ed bench product pages) (#961)

Co-authored-by: Florent Tapponnier <contact@mobula.io>
…ure) + product page/logo (#962)

Co-authored-by: Florent Tapponnier <contact@mobula.io>
Co-authored-by: Florent Tapponnier <contact@mobula.io>
…lan disclosure extended) (#964)

Co-authored-by: Florent Tapponnier <contact@mobula.io>
…965)

Co-authored-by: Florent Tapponnier <contact@mobula.io>
… fleet) (#966)

Co-authored-by: Florent Tapponnier <contact@mobula.io>
…bility lag, 5 providers, quota guards (#967)

Co-authored-by: Florent Tapponnier <contact@mobula.io>
…exing bench product pages) (#968)

Co-authored-by: Florent Tapponnier <contact@mobula.io>
… on Base) (#969)

Co-authored-by: Florent Tapponnier <contact@mobula.io>
…9) (#970)

Co-authored-by: Florent Tapponnier <contact@mobula.io>
….0s) (#971)

Co-authored-by: Florent Tapponnier <contact@mobula.io>
…ive-verified (#972)

Co-authored-by: Florent Tapponnier <contact@mobula.io>
…ider pages + logos (bloxroute, onfinality, officials) (#973)

Co-authored-by: Florent Tapponnier <contact@mobula.io>
… + restore original logo (#974)

Co-authored-by: Florent Tapponnier <contact@mobula.io>
…975)

Co-authored-by: Florent Tapponnier <contact@mobula.io>
@Flotapponnier

Copy link
Copy Markdown
Collaborator Author

Remplacée par la branche de réconciliation ship/dev-to-main (main avait 5 hotfixes jamais ff-back dans dev)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant