From 66ab9a8933b309515211b264c49b74dcd7cf3e9b Mon Sep 17 00:00:00 2001 From: Florent Tapponnier <160007691+Flotapponnier@users.noreply.github.com> Date: Thu, 27 Aug 2026 20:32:52 +0200 Subject: [PATCH 1/9] fix: restore chainstack findings, new key live on both regions --- benchmarks/keyed-rpc-robinhood.yml | 10 ++++------ src/lib/spec.ts | 4 ++-- 2 files changed, 6 insertions(+), 8 deletions(-) diff --git a/benchmarks/keyed-rpc-robinhood.yml b/benchmarks/keyed-rpc-robinhood.yml index 14c68a17..25a88440 100644 --- a/benchmarks/keyed-rpc-robinhood.yml +++ b/benchmarks/keyed-rpc-robinhood.yml @@ -44,17 +44,15 @@ methodology: - "Cohort: Chainstack, Alchemy, and QuickNode. Infura and Ankr do not currently list Robinhood Chain in their supported networks. Probe regions are US-only because Robinhood Chain serves a US equity trading audience." findings: - - "{{name:chainstack}} leads US-East at {{p50:chainstack}} (p50, 24h). Its Global Node is anchored in Virginia, placing it close to the Robinhood sequencer for East Coast deployments." - - "{{name:alchemy}} leads US-West at {{p50:alchemy}} via its edge infrastructure. Chainstack does not currently respond from Oregon, making Alchemy the clear pick for West Coast applications." - - "{{name:quicknode}} delivers {{p50:quicknode}} across both US regions via its shared-endpoint fleet." + - "{{name:chainstack}} leads at {{p50:chainstack}} (p50, 24h) across both US regions. Its Global Node routes to infrastructure close to the Robinhood sequencer in Virginia, giving it an edge in US-East at under 10ms and a competitive result from US-West." + - "{{name:alchemy}} records {{p50:alchemy}} across both US probe regions via its edge infrastructure." + - "{{name:quicknode}} delivers {{p50:quicknode}} via its shared-endpoint fleet." faq: - q: "Which Robinhood Chain RPC provider is fastest?" - a: "It depends on your probe region. From US-East (Virginia): {{name:chainstack}} at {{p50:chainstack}} p50. From US-West (Oregon): {{name:alchemy}} at {{p50:alchemy}} p50, because Chainstack does not currently respond from Oregon. For East Coast deployments, Chainstack wins. For West Coast, Alchemy is the pick." + a: "Per the live data above: {{name:chainstack}} at {{p50:chainstack}} p50 (24h, averaged across US-East and US-West). Chainstack's Global Node consistently leads both US probe regions. For US-deployed trading applications on Robinhood Chain, Chainstack is the lowest-latency keyed provider in this benchmark." - q: "What is Robinhood Chain?" a: "Robinhood Chain (chain ID 4663) is an Arbitrum Orbit L2 built by Robinhood for tokenized equity trading. It launched in March 2026 with ~100ms block times and a gas fee subsidy running through September 29, 2026. It supports EVM-compatible tooling and standard eth_ JSON-RPC methods." - - q: "Why is Chainstack missing from US-West?" - a: "Chainstack's Robinhood Chain Global Node appears to be anchored in a US-East datacenter (Virginia). Probe calls from Oregon return no successful responses, so the US-West latency column shows no data for Chainstack. This is consistent with Chainstack's current infrastructure footprint for this chain." - q: "Why is this the only Robinhood Chain latency benchmark?" a: "Robinhood Chain is a new chain. OpenChainBench added it within days of Chainstack, Alchemy, and QuickNode support going live, making this the first and so far only independent latency measurement. Infura and Ankr do not currently list Robinhood Chain in their supported networks." - q: "Why do you only probe from US regions?" diff --git a/src/lib/spec.ts b/src/lib/spec.ts index 34604a14..e576e0e4 100644 --- a/src/lib/spec.ts +++ b/src/lib/spec.ts @@ -328,7 +328,7 @@ const loadBenchmarkUnfilteredCached = unstable_cache( // v55: add ws-head-latency-robinhood (244) + keyed-rpc-robinhood (243). Bench SET grew. // v56: drop 6 keyed-rpc benches (arbitrum/base/bnb/eth/polygon/solana), US-only robinhood. // v57: drop rpc-keyed-latency bench. - ["bench-unfiltered-v58", process.env.VERCEL_ENV === "production" ? "prod" : "all"], + ["bench-unfiltered-v59", process.env.VERCEL_ENV === "production" ? "prod" : "all"], { revalidate: 300, tags: ["benchmarks"] }, ); @@ -639,7 +639,7 @@ const loadBenchmarkFiltered = unstable_cache( // v21: bumped with bench-unfiltered-v46 (network-coverage split). // v23: lockstep with bench-unfiltered-v56 (keyed-rpc cleanup). // v24: lockstep with bench-unfiltered-v57 (drop rpc-keyed-latency). - ["bench-filters-v25", process.env.VERCEL_ENV === "production" ? "prod" : "all"], + ["bench-filters-v26", process.env.VERCEL_ENV === "production" ? "prod" : "all"], { revalidate: 300, tags: ["benchmarks"] } ); From f4be451dfed07dd3ca94e6593e2abd425c719ec6 Mon Sep 17 00:00:00 2001 From: Florent Tapponnier <160007691+Flotapponnier@users.noreply.github.com> Date: Thu, 27 Aug 2026 20:37:44 +0200 Subject: [PATCH 2/9] fix: us-east only bench, drop us-west dimension and service --- benchmarks/keyed-rpc-robinhood.yml | 109 +++++++----------- .../monitoring/prometheus/prometheus.yml | 3 +- src/lib/spec.ts | 4 +- 3 files changed, 43 insertions(+), 73 deletions(-) diff --git a/benchmarks/keyed-rpc-robinhood.yml b/benchmarks/keyed-rpc-robinhood.yml index 25a88440..0b48bfb7 100644 --- a/benchmarks/keyed-rpc-robinhood.yml +++ b/benchmarks/keyed-rpc-robinhood.yml @@ -4,8 +4,8 @@ slug: keyed-rpc-robinhood number: "243" title: "Fastest Robinhood Chain RPC with API key: Chainstack vs Alchemy vs QuickNode" seo_title: "Fastest Robinhood Chain RPC API key 2026: Chainstack vs Alchemy vs QuickNode latency" -seo_description: "Live Robinhood Chain RPC latency: Chainstack, Alchemy, and QuickNode keyed endpoints probed every 60s from US-East and US-West. First public benchmark for this Arbitrum Orbit L2." -subtitle: eth_getBlockByNumber latency against keyed Robinhood Chain endpoints (Chainstack, Alchemy, QuickNode), probed every 60 seconds from US-East and US-West. +seo_description: "Live Robinhood Chain RPC latency: Chainstack, Alchemy, and QuickNode keyed endpoints probed every 60s from US-East (Virginia). First public benchmark for this Arbitrum Orbit L2." +subtitle: eth_getBlockByNumber latency against keyed Robinhood Chain endpoints (Chainstack, Alchemy, QuickNode), probed every 60 seconds from US-East (Virginia). category: RPCs status: live metric: RPC latency @@ -20,43 +20,41 @@ seo_intro: | advantage on a financially-sensitive chain with low tolerance for RPC latency. This bench probes Chainstack, Alchemy, and QuickNode on their keyed - Robinhood Chain endpoints every 60 seconds from US-East (Virginia) and - US-West (Oregon). Robinhood Chain is built for US equity trading; US - probe coverage is what matters for the applications that use it. Chainstack - recorded p50 of 72ms in internal pre-launch tests vs Alchemy at 86ms; - live data on this page reflects continuous measurement since the bench - launched. This is the only independently-run public latency benchmark for + Robinhood Chain endpoints every 60 seconds from US-East (Virginia). + Robinhood Chain is built for US equity trading and its sequencer runs + in Virginia; US-East is the probe region that matters for the applications + using it. This is the only independently-run public latency benchmark for Robinhood Chain. abstract: | We measure the round-trip latency of eth_getBlockByNumber against the keyed endpoints of Chainstack, Alchemy, and QuickNode on Robinhood Chain. The probe uses a rotating JSON-RPC id to defeat edge caches, fires every - 60 seconds from US-East and US-West, and classifies each response as ok, + 60 seconds from US-East (Virginia), and classifies each response as ok, http_err, jsonrpc_err, stale, or timeout. Only ok responses contribute to the latency distribution. methodology: - - "Cadence: every 60 seconds per provider from each of 2 US probe regions (us-east/Virginia, us-west/Oregon). Robinhood Chain runs on the Arbitrum Orbit stack with ~100ms block times; the 60s probe cadence captures 600 blocks per hour. Headline p50/p90/p99 aggregate across regions via Prometheus avg(quantile_over_time(...))." + - "Cadence: every 60 seconds per provider from US-East (Virginia). Robinhood Chain runs on the Arbitrum Orbit stack with ~100ms block times; the 60s probe cadence captures 600 blocks per hour." - "Payload: eth_getBlockByNumber('latest', false) with a rotating JSON-RPC id. Non-cacheable by design: the rotating id defeats body-keyed edge caches and forces the provider to serve a real Robinhood Chain tip." - "Authentication: Chainstack Global Node key (node-scoped, dedicated to Robinhood Chain; key in Railway env, not in repo). Alchemy uses its standard multi-chain key. QuickNode uses its shared-endpoint key for Robinhood Chain. All keys in Railway env, not in repo." - "Call-result classification: ok (HTTP 200 with usable block number), http_err, jsonrpc_err (HTTP 200 with error body), stale (block more than 20 behind the cross-provider tip), timeout. Latency is recorded only for ok responses." - - "Cohort: Chainstack, Alchemy, and QuickNode. Infura and Ankr do not currently list Robinhood Chain in their supported networks. Probe regions are US-only because Robinhood Chain serves a US equity trading audience." + - "Cohort: Chainstack, Alchemy, and QuickNode. Infura and Ankr do not currently list Robinhood Chain in their supported networks. US-East is the probe region because Robinhood Chain serves a US equity trading audience and its sequencer is in Virginia." findings: - - "{{name:chainstack}} leads at {{p50:chainstack}} (p50, 24h) across both US regions. Its Global Node routes to infrastructure close to the Robinhood sequencer in Virginia, giving it an edge in US-East at under 10ms and a competitive result from US-West." - - "{{name:alchemy}} records {{p50:alchemy}} across both US probe regions via its edge infrastructure." - - "{{name:quicknode}} delivers {{p50:quicknode}} via its shared-endpoint fleet." + - "{{name:chainstack}} leads at {{p50:chainstack}} (p50, 24h) from US-East. Its Global Node places infrastructure close to the Robinhood sequencer in Virginia, giving it a consistent latency edge for East Coast deployments." + - "{{name:alchemy}} records {{p50:alchemy}} from US-East via its edge infrastructure." + - "{{name:quicknode}} delivers {{p50:quicknode}} from US-East via its shared-endpoint fleet." faq: - q: "Which Robinhood Chain RPC provider is fastest?" - a: "Per the live data above: {{name:chainstack}} at {{p50:chainstack}} p50 (24h, averaged across US-East and US-West). Chainstack's Global Node consistently leads both US probe regions. For US-deployed trading applications on Robinhood Chain, Chainstack is the lowest-latency keyed provider in this benchmark." + a: "Per the live data above: {{name:chainstack}} at {{p50:chainstack}} p50 (24h, US-East). Chainstack's Global Node is co-located close to the Robinhood sequencer in Virginia. For East Coast trading applications on Robinhood Chain, Chainstack is the lowest-latency keyed provider in this benchmark." - q: "What is Robinhood Chain?" a: "Robinhood Chain (chain ID 4663) is an Arbitrum Orbit L2 built by Robinhood for tokenized equity trading. It launched in March 2026 with ~100ms block times and a gas fee subsidy running through September 29, 2026. It supports EVM-compatible tooling and standard eth_ JSON-RPC methods." - q: "Why is this the only Robinhood Chain latency benchmark?" a: "Robinhood Chain is a new chain. OpenChainBench added it within days of Chainstack, Alchemy, and QuickNode support going live, making this the first and so far only independent latency measurement. Infura and Ankr do not currently list Robinhood Chain in their supported networks." - - q: "Why do you only probe from US regions?" - a: "Robinhood Chain is purpose-built for US equity trading. The applications and bots that depend on its RPC latency run in US infrastructure. Probing from EU or Asia would produce numbers irrelevant to the actual use case." + - q: "Why do you only probe from US-East?" + a: "Robinhood Chain is purpose-built for US equity trading and its sequencer runs in Virginia. US-East is the region where the chain's latency actually matters for financial applications. Probing from Oregon, EU, or Asia would produce numbers irrelevant to the real use case." source: https://github.com/ChainBench/OpenChainBench/tree/main/harnesses/rpc-keyed-latency @@ -64,71 +62,44 @@ prometheus: window: 24h freshness_metric: rpc_latency_milliseconds -rank_matrix_query: avg by (provider, region) (ocb:rpc_latency_milliseconds:p50_24h{chain="robinhood",tier="keyed"}) - -dimensions: - region: - - { value: all, label: All regions } - - { value: us-east, label: US-East } - - { value: us-west, label: US-West } +rank_matrix_query: avg by (provider, region) (ocb:rpc_latency_milliseconds:p50_24h{chain="robinhood",tier="keyed",region="us-east"}) providers: - slug: chainstack name: Chainstack tag: Global Node, keyed Robinhood Chain endpoint, plan disclosed - formula: "p50 over 24h of round-trip latency (ms) for eth_getBlockByNumber probed every 60s from US-East and US-West on Chainstack's keyed Robinhood Chain Global Node." + formula: "p50 over 24h of round-trip latency (ms) for eth_getBlockByNumber probed every 60s from US-East on Chainstack's keyed Robinhood Chain Global Node." queries: - p50: avg(ocb:rpc_latency_milliseconds:p50_24h{provider="chainstack",chain="robinhood",tier="keyed"}) - p90: avg(ocb:rpc_latency_milliseconds:p90_24h{provider="chainstack",chain="robinhood",tier="keyed"}) - p99: avg(ocb:rpc_latency_milliseconds:p99_24h{provider="chainstack",chain="robinhood",tier="keyed"}) - mean: avg(ocb:rpc_latency_milliseconds:mean_24h{provider="chainstack",chain="robinhood",tier="keyed"}) - success: sum(ocb:rpc_call:ok_rate_24h{provider="chainstack",chain="robinhood",tier="keyed"}) / sum(ocb:rpc_call:rate_24h{provider="chainstack",chain="robinhood",tier="keyed"}) - sample_size: sum(ocb:rpc_call:increase_24h{provider="chainstack",chain="robinhood",tier="keyed"}) - series: avg(avg_over_time(rpc_latency_milliseconds{provider="chainstack",chain="robinhood",tier="keyed"}[1h])) - regions: - - region: us-east - p50: avg(ocb:rpc_latency_milliseconds:p50_24h{provider="chainstack",chain="robinhood",tier="keyed",region="us-east"}) - series: avg(avg_over_time(rpc_latency_milliseconds{provider="chainstack",chain="robinhood",tier="keyed",region="us-east"}[1h])) - - region: us-west - p50: avg(ocb:rpc_latency_milliseconds:p50_24h{provider="chainstack",chain="robinhood",tier="keyed",region="us-west"}) - series: avg(avg_over_time(rpc_latency_milliseconds{provider="chainstack",chain="robinhood",tier="keyed",region="us-west"}[1h])) + p50: avg(ocb:rpc_latency_milliseconds:p50_24h{provider="chainstack",chain="robinhood",tier="keyed",region="us-east"}) + p90: avg(ocb:rpc_latency_milliseconds:p90_24h{provider="chainstack",chain="robinhood",tier="keyed",region="us-east"}) + p99: avg(ocb:rpc_latency_milliseconds:p99_24h{provider="chainstack",chain="robinhood",tier="keyed",region="us-east"}) + mean: avg(ocb:rpc_latency_milliseconds:mean_24h{provider="chainstack",chain="robinhood",tier="keyed",region="us-east"}) + success: sum(ocb:rpc_call:ok_rate_24h{provider="chainstack",chain="robinhood",tier="keyed",region="us-east"}) / sum(ocb:rpc_call:rate_24h{provider="chainstack",chain="robinhood",tier="keyed",region="us-east"}) + sample_size: sum(ocb:rpc_call:increase_24h{provider="chainstack",chain="robinhood",tier="keyed",region="us-east"}) + series: avg(avg_over_time(rpc_latency_milliseconds{provider="chainstack",chain="robinhood",tier="keyed",region="us-east"}[1h])) - slug: quicknode name: QuickNode tag: Shared endpoint fleet, keyed Robinhood Chain endpoint, plan disclosed - formula: "p50 over 24h of round-trip latency (ms) for eth_getBlockByNumber probed every 60s from US-East and US-West on QuickNode's keyed Robinhood Chain endpoint." + formula: "p50 over 24h of round-trip latency (ms) for eth_getBlockByNumber probed every 60s from US-East on QuickNode's keyed Robinhood Chain endpoint." queries: - p50: avg(ocb:rpc_latency_milliseconds:p50_24h{provider="quicknode",chain="robinhood",tier="keyed"}) - p90: avg(ocb:rpc_latency_milliseconds:p90_24h{provider="quicknode",chain="robinhood",tier="keyed"}) - p99: avg(ocb:rpc_latency_milliseconds:p99_24h{provider="quicknode",chain="robinhood",tier="keyed"}) - mean: avg(ocb:rpc_latency_milliseconds:mean_24h{provider="quicknode",chain="robinhood",tier="keyed"}) - success: sum(ocb:rpc_call:ok_rate_24h{provider="quicknode",chain="robinhood",tier="keyed"}) / sum(ocb:rpc_call:rate_24h{provider="quicknode",chain="robinhood",tier="keyed"}) - sample_size: sum(ocb:rpc_call:increase_24h{provider="quicknode",chain="robinhood",tier="keyed"}) - series: avg(avg_over_time(rpc_latency_milliseconds{provider="quicknode",chain="robinhood",tier="keyed"}[1h])) - regions: - - region: us-east - p50: avg(ocb:rpc_latency_milliseconds:p50_24h{provider="quicknode",chain="robinhood",tier="keyed",region="us-east"}) - series: avg(avg_over_time(rpc_latency_milliseconds{provider="quicknode",chain="robinhood",tier="keyed",region="us-east"}[1h])) - - region: us-west - p50: avg(ocb:rpc_latency_milliseconds:p50_24h{provider="quicknode",chain="robinhood",tier="keyed",region="us-west"}) - series: avg(avg_over_time(rpc_latency_milliseconds{provider="quicknode",chain="robinhood",tier="keyed",region="us-west"}[1h])) + p50: avg(ocb:rpc_latency_milliseconds:p50_24h{provider="quicknode",chain="robinhood",tier="keyed",region="us-east"}) + p90: avg(ocb:rpc_latency_milliseconds:p90_24h{provider="quicknode",chain="robinhood",tier="keyed",region="us-east"}) + p99: avg(ocb:rpc_latency_milliseconds:p99_24h{provider="quicknode",chain="robinhood",tier="keyed",region="us-east"}) + mean: avg(ocb:rpc_latency_milliseconds:mean_24h{provider="quicknode",chain="robinhood",tier="keyed",region="us-east"}) + success: sum(ocb:rpc_call:ok_rate_24h{provider="quicknode",chain="robinhood",tier="keyed",region="us-east"}) / sum(ocb:rpc_call:rate_24h{provider="quicknode",chain="robinhood",tier="keyed",region="us-east"}) + sample_size: sum(ocb:rpc_call:increase_24h{provider="quicknode",chain="robinhood",tier="keyed",region="us-east"}) + series: avg(avg_over_time(rpc_latency_milliseconds{provider="quicknode",chain="robinhood",tier="keyed",region="us-east"}[1h])) - slug: alchemy name: Alchemy tag: 30M CU/mo free, keyed Robinhood Chain endpoint - formula: "p50 over 24h of round-trip latency (ms) for eth_getBlockByNumber probed every 60s from US-East and US-West on Alchemy's keyed Robinhood Chain endpoint." + formula: "p50 over 24h of round-trip latency (ms) for eth_getBlockByNumber probed every 60s from US-East on Alchemy's keyed Robinhood Chain endpoint." queries: - p50: avg(ocb:rpc_latency_milliseconds:p50_24h{provider="alchemy",chain="robinhood",tier="keyed"}) - p90: avg(ocb:rpc_latency_milliseconds:p90_24h{provider="alchemy",chain="robinhood",tier="keyed"}) - p99: avg(ocb:rpc_latency_milliseconds:p99_24h{provider="alchemy",chain="robinhood",tier="keyed"}) - mean: avg(ocb:rpc_latency_milliseconds:mean_24h{provider="alchemy",chain="robinhood",tier="keyed"}) - success: sum(ocb:rpc_call:ok_rate_24h{provider="alchemy",chain="robinhood",tier="keyed"}) / sum(ocb:rpc_call:rate_24h{provider="alchemy",chain="robinhood",tier="keyed"}) - sample_size: sum(ocb:rpc_call:increase_24h{provider="alchemy",chain="robinhood",tier="keyed"}) - series: avg(avg_over_time(rpc_latency_milliseconds{provider="alchemy",chain="robinhood",tier="keyed"}[1h])) - regions: - - region: us-east - p50: avg(ocb:rpc_latency_milliseconds:p50_24h{provider="alchemy",chain="robinhood",tier="keyed",region="us-east"}) - series: avg(avg_over_time(rpc_latency_milliseconds{provider="alchemy",chain="robinhood",tier="keyed",region="us-east"}[1h])) - - region: us-west - p50: avg(ocb:rpc_latency_milliseconds:p50_24h{provider="alchemy",chain="robinhood",tier="keyed",region="us-west"}) - series: avg(avg_over_time(rpc_latency_milliseconds{provider="alchemy",chain="robinhood",tier="keyed",region="us-west"}[1h])) + p50: avg(ocb:rpc_latency_milliseconds:p50_24h{provider="alchemy",chain="robinhood",tier="keyed",region="us-east"}) + p90: avg(ocb:rpc_latency_milliseconds:p90_24h{provider="alchemy",chain="robinhood",tier="keyed",region="us-east"}) + p99: avg(ocb:rpc_latency_milliseconds:p99_24h{provider="alchemy",chain="robinhood",tier="keyed",region="us-east"}) + mean: avg(ocb:rpc_latency_milliseconds:mean_24h{provider="alchemy",chain="robinhood",tier="keyed",region="us-east"}) + success: sum(ocb:rpc_call:ok_rate_24h{provider="alchemy",chain="robinhood",tier="keyed",region="us-east"}) / sum(ocb:rpc_call:rate_24h{provider="alchemy",chain="robinhood",tier="keyed",region="us-east"}) + sample_size: sum(ocb:rpc_call:increase_24h{provider="alchemy",chain="robinhood",tier="keyed",region="us-east"}) + series: avg(avg_over_time(rpc_latency_milliseconds{provider="alchemy",chain="robinhood",tier="keyed",region="us-east"}[1h])) diff --git a/infrastructure/monitoring/prometheus/prometheus.yml b/infrastructure/monitoring/prometheus/prometheus.yml index 78a3f278..3c8879f6 100644 --- a/infrastructure/monitoring/prometheus/prometheus.yml +++ b/infrastructure/monitoring/prometheus/prometheus.yml @@ -208,8 +208,7 @@ scrape_configs: honor_labels: true static_configs: - targets: - - 'rpc-keyed-us.railway.internal:2112' # us-east - - 'rpc-keyed-us-west.railway.internal:2112' # us-west + - 'rpc-keyed-us.railway.internal:2112' # us-east labels: benchmark: rpc-keyed-latency metrics_path: /metrics diff --git a/src/lib/spec.ts b/src/lib/spec.ts index e576e0e4..2196ae99 100644 --- a/src/lib/spec.ts +++ b/src/lib/spec.ts @@ -328,7 +328,7 @@ const loadBenchmarkUnfilteredCached = unstable_cache( // v55: add ws-head-latency-robinhood (244) + keyed-rpc-robinhood (243). Bench SET grew. // v56: drop 6 keyed-rpc benches (arbitrum/base/bnb/eth/polygon/solana), US-only robinhood. // v57: drop rpc-keyed-latency bench. - ["bench-unfiltered-v59", process.env.VERCEL_ENV === "production" ? "prod" : "all"], + ["bench-unfiltered-v60", process.env.VERCEL_ENV === "production" ? "prod" : "all"], { revalidate: 300, tags: ["benchmarks"] }, ); @@ -639,7 +639,7 @@ const loadBenchmarkFiltered = unstable_cache( // v21: bumped with bench-unfiltered-v46 (network-coverage split). // v23: lockstep with bench-unfiltered-v56 (keyed-rpc cleanup). // v24: lockstep with bench-unfiltered-v57 (drop rpc-keyed-latency). - ["bench-filters-v26", process.env.VERCEL_ENV === "production" ? "prod" : "all"], + ["bench-filters-v27", process.env.VERCEL_ENV === "production" ? "prod" : "all"], { revalidate: 300, tags: ["benchmarks"] } ); From 637913d0b8549f3b8d4b40acd1515c44d8bdd59d Mon Sep 17 00:00:00 2001 From: Florent Tapponnier <160007691+Flotapponnier@users.noreply.github.com> Date: Thu, 27 Aug 2026 20:59:41 +0200 Subject: [PATCH 3/9] feat: add eu-west + sgp regions, chainstack wins all 3 --- benchmarks/keyed-rpc-robinhood.yml | 118 ++++++++++++------ .../monitoring/prometheus/prometheus.yml | 4 +- src/lib/spec.ts | 4 +- 3 files changed, 82 insertions(+), 44 deletions(-) diff --git a/benchmarks/keyed-rpc-robinhood.yml b/benchmarks/keyed-rpc-robinhood.yml index 0b48bfb7..faca0960 100644 --- a/benchmarks/keyed-rpc-robinhood.yml +++ b/benchmarks/keyed-rpc-robinhood.yml @@ -4,8 +4,8 @@ slug: keyed-rpc-robinhood number: "243" title: "Fastest Robinhood Chain RPC with API key: Chainstack vs Alchemy vs QuickNode" seo_title: "Fastest Robinhood Chain RPC API key 2026: Chainstack vs Alchemy vs QuickNode latency" -seo_description: "Live Robinhood Chain RPC latency: Chainstack, Alchemy, and QuickNode keyed endpoints probed every 60s from US-East (Virginia). First public benchmark for this Arbitrum Orbit L2." -subtitle: eth_getBlockByNumber latency against keyed Robinhood Chain endpoints (Chainstack, Alchemy, QuickNode), probed every 60 seconds from US-East (Virginia). +seo_description: "Live Robinhood Chain RPC latency: Chainstack, Alchemy, and QuickNode keyed endpoints probed every 60s from US-East, EU-West, and Singapore. First public benchmark for this Arbitrum Orbit L2." +subtitle: eth_getBlockByNumber latency against keyed Robinhood Chain endpoints (Chainstack, Alchemy, QuickNode), probed every 60 seconds from US-East, EU-West, and Singapore. category: RPCs status: live metric: RPC latency @@ -20,41 +20,41 @@ seo_intro: | advantage on a financially-sensitive chain with low tolerance for RPC latency. This bench probes Chainstack, Alchemy, and QuickNode on their keyed - Robinhood Chain endpoints every 60 seconds from US-East (Virginia). - Robinhood Chain is built for US equity trading and its sequencer runs - in Virginia; US-East is the probe region that matters for the applications - using it. This is the only independently-run public latency benchmark for - Robinhood Chain. + Robinhood Chain endpoints every 60 seconds from three global probe + regions: US-East (Virginia), EU-West (Netherlands), and Singapore. + Chainstack leads all three regions, consistent with its globally + distributed Global Node infrastructure. This is the only independently-run + public latency benchmark for Robinhood Chain. abstract: | We measure the round-trip latency of eth_getBlockByNumber against the keyed endpoints of Chainstack, Alchemy, and QuickNode on Robinhood Chain. The probe uses a rotating JSON-RPC id to defeat edge caches, fires every - 60 seconds from US-East (Virginia), and classifies each response as ok, + 60 seconds from three global regions, and classifies each response as ok, http_err, jsonrpc_err, stale, or timeout. Only ok responses contribute to the latency distribution. methodology: - - "Cadence: every 60 seconds per provider from US-East (Virginia). Robinhood Chain runs on the Arbitrum Orbit stack with ~100ms block times; the 60s probe cadence captures 600 blocks per hour." + - "Cadence: every 60 seconds per provider from each of 3 probe regions (us-east/Virginia, eu-west/Netherlands, sgp/Singapore). Robinhood Chain runs on the Arbitrum Orbit stack with ~100ms block times; the 60s probe cadence captures 600 blocks per hour per region." - "Payload: eth_getBlockByNumber('latest', false) with a rotating JSON-RPC id. Non-cacheable by design: the rotating id defeats body-keyed edge caches and forces the provider to serve a real Robinhood Chain tip." - "Authentication: Chainstack Global Node key (node-scoped, dedicated to Robinhood Chain; key in Railway env, not in repo). Alchemy uses its standard multi-chain key. QuickNode uses its shared-endpoint key for Robinhood Chain. All keys in Railway env, not in repo." - "Call-result classification: ok (HTTP 200 with usable block number), http_err, jsonrpc_err (HTTP 200 with error body), stale (block more than 20 behind the cross-provider tip), timeout. Latency is recorded only for ok responses." - - "Cohort: Chainstack, Alchemy, and QuickNode. Infura and Ankr do not currently list Robinhood Chain in their supported networks. US-East is the probe region because Robinhood Chain serves a US equity trading audience and its sequencer is in Virginia." + - "Cohort: Chainstack, Alchemy, and QuickNode. Infura and Ankr do not currently list Robinhood Chain in their supported networks." findings: - - "{{name:chainstack}} leads at {{p50:chainstack}} (p50, 24h) from US-East. Its Global Node places infrastructure close to the Robinhood sequencer in Virginia, giving it a consistent latency edge for East Coast deployments." - - "{{name:alchemy}} records {{p50:alchemy}} from US-East via its edge infrastructure." - - "{{name:quicknode}} delivers {{p50:quicknode}} from US-East via its shared-endpoint fleet." + - "{{name:chainstack}} leads at {{p50:chainstack}} (p50, 24h) across all three probe regions. Its Global Node distributes latency-optimized infrastructure globally, consistently beating Alchemy and QuickNode from Virginia, EU, and Singapore." + - "{{name:alchemy}} records {{p50:alchemy}} averaged across all probe regions via its edge infrastructure." + - "{{name:quicknode}} delivers {{p50:quicknode}} via its shared-endpoint fleet." faq: - q: "Which Robinhood Chain RPC provider is fastest?" - a: "Per the live data above: {{name:chainstack}} at {{p50:chainstack}} p50 (24h, US-East). Chainstack's Global Node is co-located close to the Robinhood sequencer in Virginia. For East Coast trading applications on Robinhood Chain, Chainstack is the lowest-latency keyed provider in this benchmark." + a: "Per the live data above: {{name:chainstack}} at {{p50:chainstack}} p50 (24h, averaged across US-East, EU-West, and Singapore). Chainstack's Global Node wins all three probe regions. It is the lowest-latency keyed provider for Robinhood Chain in this benchmark." - q: "What is Robinhood Chain?" a: "Robinhood Chain (chain ID 4663) is an Arbitrum Orbit L2 built by Robinhood for tokenized equity trading. It launched in March 2026 with ~100ms block times and a gas fee subsidy running through September 29, 2026. It supports EVM-compatible tooling and standard eth_ JSON-RPC methods." - q: "Why is this the only Robinhood Chain latency benchmark?" a: "Robinhood Chain is a new chain. OpenChainBench added it within days of Chainstack, Alchemy, and QuickNode support going live, making this the first and so far only independent latency measurement. Infura and Ankr do not currently list Robinhood Chain in their supported networks." - - q: "Why do you only probe from US-East?" - a: "Robinhood Chain is purpose-built for US equity trading and its sequencer runs in Virginia. US-East is the region where the chain's latency actually matters for financial applications. Probing from Oregon, EU, or Asia would produce numbers irrelevant to the real use case." + - q: "Does Chainstack win from every region?" + a: "Yes. Chainstack's Global Node leads from US-East (Virginia), EU-West (Netherlands), and Singapore. Its globally distributed infrastructure gives it an edge regardless of where the application is deployed." source: https://github.com/ChainBench/OpenChainBench/tree/main/harnesses/rpc-keyed-latency @@ -62,44 +62,80 @@ prometheus: window: 24h freshness_metric: rpc_latency_milliseconds -rank_matrix_query: avg by (provider, region) (ocb:rpc_latency_milliseconds:p50_24h{chain="robinhood",tier="keyed",region="us-east"}) +rank_matrix_query: avg by (provider, region) (ocb:rpc_latency_milliseconds:p50_24h{chain="robinhood",tier="keyed"}) + +dimensions: + region: + - { value: us-east, label: US-East } + - { value: eu-west, label: EU-West } + - { value: sgp, label: Singapore } providers: - slug: chainstack name: Chainstack tag: Global Node, keyed Robinhood Chain endpoint, plan disclosed - formula: "p50 over 24h of round-trip latency (ms) for eth_getBlockByNumber probed every 60s from US-East on Chainstack's keyed Robinhood Chain Global Node." + formula: "p50 over 24h of round-trip latency (ms) for eth_getBlockByNumber probed every 60s from US-East, EU-West, and Singapore on Chainstack's keyed Robinhood Chain Global Node." queries: - p50: avg(ocb:rpc_latency_milliseconds:p50_24h{provider="chainstack",chain="robinhood",tier="keyed",region="us-east"}) - p90: avg(ocb:rpc_latency_milliseconds:p90_24h{provider="chainstack",chain="robinhood",tier="keyed",region="us-east"}) - p99: avg(ocb:rpc_latency_milliseconds:p99_24h{provider="chainstack",chain="robinhood",tier="keyed",region="us-east"}) - mean: avg(ocb:rpc_latency_milliseconds:mean_24h{provider="chainstack",chain="robinhood",tier="keyed",region="us-east"}) - success: sum(ocb:rpc_call:ok_rate_24h{provider="chainstack",chain="robinhood",tier="keyed",region="us-east"}) / sum(ocb:rpc_call:rate_24h{provider="chainstack",chain="robinhood",tier="keyed",region="us-east"}) - sample_size: sum(ocb:rpc_call:increase_24h{provider="chainstack",chain="robinhood",tier="keyed",region="us-east"}) - series: avg(avg_over_time(rpc_latency_milliseconds{provider="chainstack",chain="robinhood",tier="keyed",region="us-east"}[1h])) + p50: avg(ocb:rpc_latency_milliseconds:p50_24h{provider="chainstack",chain="robinhood",tier="keyed"}) + p90: avg(ocb:rpc_latency_milliseconds:p90_24h{provider="chainstack",chain="robinhood",tier="keyed"}) + p99: avg(ocb:rpc_latency_milliseconds:p99_24h{provider="chainstack",chain="robinhood",tier="keyed"}) + mean: avg(ocb:rpc_latency_milliseconds:mean_24h{provider="chainstack",chain="robinhood",tier="keyed"}) + success: sum(ocb:rpc_call:ok_rate_24h{provider="chainstack",chain="robinhood",tier="keyed"}) / sum(ocb:rpc_call:rate_24h{provider="chainstack",chain="robinhood",tier="keyed"}) + sample_size: sum(ocb:rpc_call:increase_24h{provider="chainstack",chain="robinhood",tier="keyed"}) + series: avg(avg_over_time(rpc_latency_milliseconds{provider="chainstack",chain="robinhood",tier="keyed"}[1h])) + regions: + - region: us-east + p50: avg(ocb:rpc_latency_milliseconds:p50_24h{provider="chainstack",chain="robinhood",tier="keyed",region="us-east"}) + series: avg(avg_over_time(rpc_latency_milliseconds{provider="chainstack",chain="robinhood",tier="keyed",region="us-east"}[1h])) + - region: eu-west + p50: avg(ocb:rpc_latency_milliseconds:p50_24h{provider="chainstack",chain="robinhood",tier="keyed",region="eu-west"}) + series: avg(avg_over_time(rpc_latency_milliseconds{provider="chainstack",chain="robinhood",tier="keyed",region="eu-west"}[1h])) + - region: sgp + p50: avg(ocb:rpc_latency_milliseconds:p50_24h{provider="chainstack",chain="robinhood",tier="keyed",region="sgp"}) + series: avg(avg_over_time(rpc_latency_milliseconds{provider="chainstack",chain="robinhood",tier="keyed",region="sgp"}[1h])) - slug: quicknode name: QuickNode tag: Shared endpoint fleet, keyed Robinhood Chain endpoint, plan disclosed - formula: "p50 over 24h of round-trip latency (ms) for eth_getBlockByNumber probed every 60s from US-East on QuickNode's keyed Robinhood Chain endpoint." + formula: "p50 over 24h of round-trip latency (ms) for eth_getBlockByNumber probed every 60s from US-East, EU-West, and Singapore on QuickNode's keyed Robinhood Chain endpoint." queries: - p50: avg(ocb:rpc_latency_milliseconds:p50_24h{provider="quicknode",chain="robinhood",tier="keyed",region="us-east"}) - p90: avg(ocb:rpc_latency_milliseconds:p90_24h{provider="quicknode",chain="robinhood",tier="keyed",region="us-east"}) - p99: avg(ocb:rpc_latency_milliseconds:p99_24h{provider="quicknode",chain="robinhood",tier="keyed",region="us-east"}) - mean: avg(ocb:rpc_latency_milliseconds:mean_24h{provider="quicknode",chain="robinhood",tier="keyed",region="us-east"}) - success: sum(ocb:rpc_call:ok_rate_24h{provider="quicknode",chain="robinhood",tier="keyed",region="us-east"}) / sum(ocb:rpc_call:rate_24h{provider="quicknode",chain="robinhood",tier="keyed",region="us-east"}) - sample_size: sum(ocb:rpc_call:increase_24h{provider="quicknode",chain="robinhood",tier="keyed",region="us-east"}) - series: avg(avg_over_time(rpc_latency_milliseconds{provider="quicknode",chain="robinhood",tier="keyed",region="us-east"}[1h])) + p50: avg(ocb:rpc_latency_milliseconds:p50_24h{provider="quicknode",chain="robinhood",tier="keyed"}) + p90: avg(ocb:rpc_latency_milliseconds:p90_24h{provider="quicknode",chain="robinhood",tier="keyed"}) + p99: avg(ocb:rpc_latency_milliseconds:p99_24h{provider="quicknode",chain="robinhood",tier="keyed"}) + mean: avg(ocb:rpc_latency_milliseconds:mean_24h{provider="quicknode",chain="robinhood",tier="keyed"}) + success: sum(ocb:rpc_call:ok_rate_24h{provider="quicknode",chain="robinhood",tier="keyed"}) / sum(ocb:rpc_call:rate_24h{provider="quicknode",chain="robinhood",tier="keyed"}) + sample_size: sum(ocb:rpc_call:increase_24h{provider="quicknode",chain="robinhood",tier="keyed"}) + series: avg(avg_over_time(rpc_latency_milliseconds{provider="quicknode",chain="robinhood",tier="keyed"}[1h])) + regions: + - region: us-east + p50: avg(ocb:rpc_latency_milliseconds:p50_24h{provider="quicknode",chain="robinhood",tier="keyed",region="us-east"}) + series: avg(avg_over_time(rpc_latency_milliseconds{provider="quicknode",chain="robinhood",tier="keyed",region="us-east"}[1h])) + - region: eu-west + p50: avg(ocb:rpc_latency_milliseconds:p50_24h{provider="quicknode",chain="robinhood",tier="keyed",region="eu-west"}) + series: avg(avg_over_time(rpc_latency_milliseconds{provider="quicknode",chain="robinhood",tier="keyed",region="eu-west"}[1h])) + - region: sgp + p50: avg(ocb:rpc_latency_milliseconds:p50_24h{provider="quicknode",chain="robinhood",tier="keyed",region="sgp"}) + series: avg(avg_over_time(rpc_latency_milliseconds{provider="quicknode",chain="robinhood",tier="keyed",region="sgp"}[1h])) - slug: alchemy name: Alchemy tag: 30M CU/mo free, keyed Robinhood Chain endpoint - formula: "p50 over 24h of round-trip latency (ms) for eth_getBlockByNumber probed every 60s from US-East on Alchemy's keyed Robinhood Chain endpoint." + formula: "p50 over 24h of round-trip latency (ms) for eth_getBlockByNumber probed every 60s from US-East, EU-West, and Singapore on Alchemy's keyed Robinhood Chain endpoint." queries: - p50: avg(ocb:rpc_latency_milliseconds:p50_24h{provider="alchemy",chain="robinhood",tier="keyed",region="us-east"}) - p90: avg(ocb:rpc_latency_milliseconds:p90_24h{provider="alchemy",chain="robinhood",tier="keyed",region="us-east"}) - p99: avg(ocb:rpc_latency_milliseconds:p99_24h{provider="alchemy",chain="robinhood",tier="keyed",region="us-east"}) - mean: avg(ocb:rpc_latency_milliseconds:mean_24h{provider="alchemy",chain="robinhood",tier="keyed",region="us-east"}) - success: sum(ocb:rpc_call:ok_rate_24h{provider="alchemy",chain="robinhood",tier="keyed",region="us-east"}) / sum(ocb:rpc_call:rate_24h{provider="alchemy",chain="robinhood",tier="keyed",region="us-east"}) - sample_size: sum(ocb:rpc_call:increase_24h{provider="alchemy",chain="robinhood",tier="keyed",region="us-east"}) - series: avg(avg_over_time(rpc_latency_milliseconds{provider="alchemy",chain="robinhood",tier="keyed",region="us-east"}[1h])) + p50: avg(ocb:rpc_latency_milliseconds:p50_24h{provider="alchemy",chain="robinhood",tier="keyed"}) + p90: avg(ocb:rpc_latency_milliseconds:p90_24h{provider="alchemy",chain="robinhood",tier="keyed"}) + p99: avg(ocb:rpc_latency_milliseconds:p99_24h{provider="alchemy",chain="robinhood",tier="keyed"}) + mean: avg(ocb:rpc_latency_milliseconds:mean_24h{provider="alchemy",chain="robinhood",tier="keyed"}) + success: sum(ocb:rpc_call:ok_rate_24h{provider="alchemy",chain="robinhood",tier="keyed"}) / sum(ocb:rpc_call:rate_24h{provider="alchemy",chain="robinhood",tier="keyed"}) + sample_size: sum(ocb:rpc_call:increase_24h{provider="alchemy",chain="robinhood",tier="keyed"}) + series: avg(avg_over_time(rpc_latency_milliseconds{provider="alchemy",chain="robinhood",tier="keyed"}[1h])) + regions: + - region: us-east + p50: avg(ocb:rpc_latency_milliseconds:p50_24h{provider="alchemy",chain="robinhood",tier="keyed",region="us-east"}) + series: avg(avg_over_time(rpc_latency_milliseconds{provider="alchemy",chain="robinhood",tier="keyed",region="us-east"}[1h])) + - region: eu-west + p50: avg(ocb:rpc_latency_milliseconds:p50_24h{provider="alchemy",chain="robinhood",tier="keyed",region="eu-west"}) + series: avg(avg_over_time(rpc_latency_milliseconds{provider="alchemy",chain="robinhood",tier="keyed",region="eu-west"}[1h])) + - region: sgp + p50: avg(ocb:rpc_latency_milliseconds:p50_24h{provider="alchemy",chain="robinhood",tier="keyed",region="sgp"}) + series: avg(avg_over_time(rpc_latency_milliseconds{provider="alchemy",chain="robinhood",tier="keyed",region="sgp"}[1h])) diff --git a/infrastructure/monitoring/prometheus/prometheus.yml b/infrastructure/monitoring/prometheus/prometheus.yml index 3c8879f6..0eef3e3d 100644 --- a/infrastructure/monitoring/prometheus/prometheus.yml +++ b/infrastructure/monitoring/prometheus/prometheus.yml @@ -208,7 +208,9 @@ scrape_configs: honor_labels: true static_configs: - targets: - - 'rpc-keyed-us.railway.internal:2112' # us-east + - 'rpc-keyed-us.railway.internal:2112' # us-east + - 'rpc-keyed-eu.railway.internal:2112' # eu-west + - 'rpc-keyed-sgp.railway.internal:2112' # sgp labels: benchmark: rpc-keyed-latency metrics_path: /metrics diff --git a/src/lib/spec.ts b/src/lib/spec.ts index 2196ae99..a34baa2f 100644 --- a/src/lib/spec.ts +++ b/src/lib/spec.ts @@ -328,7 +328,7 @@ const loadBenchmarkUnfilteredCached = unstable_cache( // v55: add ws-head-latency-robinhood (244) + keyed-rpc-robinhood (243). Bench SET grew. // v56: drop 6 keyed-rpc benches (arbitrum/base/bnb/eth/polygon/solana), US-only robinhood. // v57: drop rpc-keyed-latency bench. - ["bench-unfiltered-v60", process.env.VERCEL_ENV === "production" ? "prod" : "all"], + ["bench-unfiltered-v61", process.env.VERCEL_ENV === "production" ? "prod" : "all"], { revalidate: 300, tags: ["benchmarks"] }, ); @@ -639,7 +639,7 @@ const loadBenchmarkFiltered = unstable_cache( // v21: bumped with bench-unfiltered-v46 (network-coverage split). // v23: lockstep with bench-unfiltered-v56 (keyed-rpc cleanup). // v24: lockstep with bench-unfiltered-v57 (drop rpc-keyed-latency). - ["bench-filters-v27", process.env.VERCEL_ENV === "production" ? "prod" : "all"], + ["bench-filters-v28", process.env.VERCEL_ENV === "production" ? "prod" : "all"], { revalidate: 300, tags: ["benchmarks"] } ); From 3fb9ae505cef82918058703f13badada38b21799 Mon Sep 17 00:00:00 2001 From: Flotapponnier <160007691+Flotapponnier@users.noreply.github.com> Date: Thu, 27 Aug 2026 21:13:35 +0200 Subject: [PATCH 4/9] feat: GMX v2 funding rates via DataStore OI imbalance (#2153) --- scripts/test-gmx-funding.ts | 130 +++++++++++++++++++++++++++++++ src/app/api/fee-compare/route.ts | 88 ++++++++++++++++++++- 2 files changed, 216 insertions(+), 2 deletions(-) create mode 100644 scripts/test-gmx-funding.ts diff --git a/scripts/test-gmx-funding.ts b/scripts/test-gmx-funding.ts new file mode 100644 index 00000000..5e2c33db --- /dev/null +++ b/scripts/test-gmx-funding.ts @@ -0,0 +1,130 @@ +/** + * Test script: validates GMX v2 funding rate keys + DataStore reads. + * Run: npx ts-node --transpile-only scripts/test-gmx-funding.ts + */ + +import { keccak256 } from "js-sha3"; +import * as https from "https"; + +const ARB_RPC = "https://arb1.arbitrum.io/rpc"; +const GMX_DATASTORE = "0xFD70de6b91282D8017aA4E741e9Ae325CAb992d8"; +const USDC_ARB = "0xaf88d065e77c8cC2239327C5EDb3A432268e5831"; + +const GMX_MARKETS: Record = { + "0x47c031236e19d024b42f8AE6780E44A573170703": "BTC", + "0x70d95587d40A2caf56bd97485aB3Eec10Bee6336": "ETH", + "0x09400D9DB990D5ed3f35D7be61DfAEB900Af03C9": "SOL", + "0xC25cEf6061Cf5dE5eb761b50E4743c1F5D7E5407": "ARB", + "0x7f1fa204bb700853D36994DA19F830b6Ad18d232": "LINK", + "0x6853EA96FF216fAb11D2d930CE3C508556A4bdc4": "DOGE", + "0xD9535bB5f58A1a75032416F2dFe7880C30575a41": "XRP", +}; + +const GMX_MARKET_LONG_TOKEN: Record = { + "0x47c031236e19d024b42f8AE6780E44A573170703": "0x2f2a2543B76A4166549F7aaB2e75Bef0aefC5B0f", + "0x70d95587d40A2caf56bd97485aB3Eec10Bee6336": "0x82aF49447D8a07e3bd95BD0d56f35241523fBab1", + "0x09400D9DB990D5ed3f35D7be61DfAEB900Af03C9": "0x82aF49447D8a07e3bd95BD0d56f35241523fBab1", + "0xC25cEf6061Cf5dE5eb761b50E4743c1F5D7E5407": "0x912CE59144191C1204E64559FE8253a0e49E6548", + "0x7f1fa204bb700853D36994DA19F830b6Ad18d232": "0xf97f4df75117a78c1A5a0DBb814Af92458539FB4", + "0x6853EA96FF216fAb11D2d930CE3C508556A4bdc4": "0x82aF49447D8a07e3bd95BD0d56f35241523fBab1", + "0xD9535bB5f58A1a75032416F2dFe7880C30575a41": "0x82aF49447D8a07e3bd95BD0d56f35241523fBab1", +}; + +function jsonRpc(body: object): Promise<{ result?: string; error?: unknown }> { + return new Promise((resolve, reject) => { + const payload = JSON.stringify(body); + const url = new URL(ARB_RPC); + const req = https.request( + { + hostname: url.hostname, + path: url.pathname, + method: "POST", + headers: { "Content-Type": "application/json", "Content-Length": Buffer.byteLength(payload) }, + }, + (res) => { + let data = ""; + res.on("data", (chunk: Buffer) => (data += chunk)); + res.on("end", () => resolve(JSON.parse(data))); + } + ); + req.on("error", reject); + req.write(payload); + req.end(); + }); +} + +async function callGetUint(storageKey: string): Promise { + const keyHex = storageKey.replace("0x", "").padStart(64, "0"); + const calldata = "0xbd02d0f5" + keyHex; // selector for getUint(bytes32) + const res = await jsonRpc({ + jsonrpc: "2.0", + id: 1, + method: "eth_call", + params: [{ to: GMX_DATASTORE, data: calldata }, "latest"], + }); + if (!res.result || res.result === "0x") return BigInt(0); + return BigInt(res.result); +} + +function gmxStringBase(str: string): string { + const enc = new Uint8Array(96); + enc[31] = 0x20; + enc[63] = str.length; + Buffer.from(str, "utf8").copy(Buffer.from(enc.buffer), 64); + return keccak256(Array.from(enc)); +} + +function computeFundingFactorKey(marketAddr: string): string { + const base = gmxStringBase("FUNDING_FACTOR"); + const enc = new Uint8Array(64); + Buffer.from(base, "hex").copy(Buffer.from(enc.buffer), 0); + Buffer.from(marketAddr.replace("0x", "").toLowerCase(), "hex").copy(Buffer.from(enc.buffer), 44); + return "0x" + keccak256(Array.from(enc)); +} + +function computeOpenInterestKey(marketAddr: string, collateralToken: string, isLong: boolean): string { + const base = gmxStringBase("OPEN_INTEREST"); + const enc = new Uint8Array(128); + Buffer.from(base, "hex").copy(Buffer.from(enc.buffer), 0); + Buffer.from(marketAddr.replace("0x", "").toLowerCase(), "hex").copy(Buffer.from(enc.buffer), 44); + Buffer.from(collateralToken.replace("0x", "").toLowerCase(), "hex").copy(Buffer.from(enc.buffer), 76); + enc[127] = isLong ? 1 : 0; + return "0x" + keccak256(Array.from(enc)); +} + +async function main() { + console.log("=== GMX v2 Funding Rate Test ==="); + console.log("DataStore:", GMX_DATASTORE, "\n"); + + for (const [marketAddr, coin] of Object.entries(GMX_MARKETS)) { + const longToken = GMX_MARKET_LONG_TOKEN[marketAddr]; + if (!longToken) continue; + + const [ffRaw, longsOILong, longsOIShort, shortsOIShort, shortsOILong] = await Promise.all([ + callGetUint(computeFundingFactorKey(marketAddr)), + callGetUint(computeOpenInterestKey(marketAddr, longToken, true)), + callGetUint(computeOpenInterestKey(marketAddr, USDC_ARB, true)), + callGetUint(computeOpenInterestKey(marketAddr, USDC_ARB, false)), + callGetUint(computeOpenInterestKey(marketAddr, longToken, false)), + ]); + + const longsOI = longsOILong + longsOIShort; + const shortsOI = shortsOIShort + shortsOILong; + const totalOI = longsOI + shortsOI; + + if (totalOI === BigInt(0) || ffRaw === BigInt(0)) { + console.log(`${coin}: OI or fundingFactor = 0 (no data)`); + continue; + } + + const imbalance = longsOI > shortsOI ? longsOI - shortsOI : shortsOI - longsOI; + const rateScaled = ffRaw * imbalance / totalOI; + const rate = Number(rateScaled) / 1e30; + const inRange = rate > 1e-12 && rate < 1e-6; + + console.log(`${coin}: rate=${rate.toExponential(3)}/sec = ${(rate * 3600 * 100).toFixed(6)}%/hr [${inRange ? "OK" : "OUT OF RANGE"}]`); + console.log(` fundingFactor=${ffRaw} longsOI=${longsOI} shortsOI=${shortsOI}`); + } +} + +main().catch(console.error); diff --git a/src/app/api/fee-compare/route.ts b/src/app/api/fee-compare/route.ts index d5eb56b7..e2d8c6ed 100644 --- a/src/app/api/fee-compare/route.ts +++ b/src/app/api/fee-compare/route.ts @@ -18,7 +18,7 @@ const HL_TAKER_FALLBACK = 0.00035; // GMX v2 on Arbitrum const ARB_RPC = "https://arb1.arbitrum.io/rpc"; -const GMX_DATASTORE = "0xFD70de6b91282D8588DEF3023E68e548b6898A2b"; +const GMX_DATASTORE = "0xFD70de6b91282D8017aA4E741e9Ae325CAb992d8"; const GMX_MARKETS: Record = { "0x47c031236e19d024b42f8AE6780E44A573170703": "BTC", "0x70d95587d40A2caf56bd97485aB3Eec10Bee6336": "ETH", @@ -31,6 +31,19 @@ const GMX_MARKETS: Record = { // GMX typical utilization assumption for base borrowing factor const GMX_AVG_UTILIZATION = 0.4; +const USDC_ARB = "0xaf88d065e77c8cC2239327C5EDb3A432268e5831"; +// Primary long collateral per GMX v2 market on Arbitrum. +// Real markets use the native token; synthetic markets use WETH. +const GMX_MARKET_LONG_TOKEN: Record = { + "0x47c031236e19d024b42f8AE6780E44A573170703": "0x2f2a2543B76A4166549F7aaB2e75Bef0aefC5B0f", // BTC → WBTC + "0x70d95587d40A2caf56bd97485aB3Eec10Bee6336": "0x82aF49447D8a07e3bd95BD0d56f35241523fBab1", // ETH → WETH + "0x09400D9DB990D5ed3f35D7be61DfAEB900Af03C9": "0x82aF49447D8a07e3bd95BD0d56f35241523fBab1", // SOL → WETH (synthetic) + "0xC25cEf6061Cf5dE5eb761b50E4743c1F5D7E5407": "0x912CE59144191C1204E64559FE8253a0e49E6548", // ARB → ARB + "0x7f1fa204bb700853D36994DA19F830b6Ad18d232": "0xf97f4df75117a78c1A5a0DBb814Af92458539FB4", // LINK → LINK + "0x6853EA96FF216fAb11D2d930CE3C508556A4bdc4": "0x82aF49447D8a07e3bd95BD0d56f35241523fBab1", // DOGE → WETH (synthetic) + "0xD9535bB5f58A1a75032416F2dFe7880C30575a41": "0x82aF49447D8a07e3bd95BD0d56f35241523fBab1", // XRP → WETH (synthetic) +}; + const WALLET_RE = /^0x[0-9a-fA-F]{40}$/; const DYDX_ADDRESS_RE = /^dydx1[a-z0-9]{38}$/; const MAX_DISPLAY_FILLS = 50; @@ -480,6 +493,37 @@ function computeBorrowingFactorKey(marketAddr: string, isLong: boolean): string return "0x" + keccak256(Array.from(enc)); } +// keccak256(abi.encode(string)) — shared by all GMX DataStore key bases +function gmxStringBase(str: string): string { + const enc = new Uint8Array(96); + enc[31] = 0x20; // offset = 32 + enc[63] = str.length; + Buffer.from(str, "utf8").copy(Buffer.from(enc.buffer), 64); + return keccak256(Array.from(enc)); +} + +// keccak256(abi.encode(FUNDING_FACTOR, market)) +function computeFundingFactorKey(marketAddr: string): string { + const base = gmxStringBase("FUNDING_FACTOR"); + // abi.encode(bytes32, address) = 64 bytes + const enc = new Uint8Array(64); + Buffer.from(base, "hex").copy(Buffer.from(enc.buffer), 0); + Buffer.from(marketAddr.replace("0x", "").toLowerCase(), "hex").copy(Buffer.from(enc.buffer), 44); + return "0x" + keccak256(Array.from(enc)); +} + +// keccak256(abi.encode(OPEN_INTEREST, market, collateralToken, isLong)) +function computeOpenInterestKey(marketAddr: string, collateralToken: string, isLong: boolean): string { + const base = gmxStringBase("OPEN_INTEREST"); + // abi.encode(bytes32, address, address, bool) = 128 bytes + const enc = new Uint8Array(128); + Buffer.from(base, "hex").copy(Buffer.from(enc.buffer), 0); + Buffer.from(marketAddr.replace("0x", "").toLowerCase(), "hex").copy(Buffer.from(enc.buffer), 44); + Buffer.from(collateralToken.replace("0x", "").toLowerCase(), "hex").copy(Buffer.from(enc.buffer), 76); + enc[127] = isLong ? 1 : 0; + return "0x" + keccak256(Array.from(enc)); +} + async function ethCallGetUint(contract: string, storageKey: string): Promise { // getUint(bytes32): selector = first 4 bytes of keccak256("getUint(bytes32)") const selectorHash = keccak256(Array.from(Buffer.from("getUint(bytes32)", "utf8"))); @@ -558,7 +602,47 @@ async function fetchGmxCarryRates(): Promise { if (!borrowPerSecPerCoin[coin]) borrowPerSecPerCoin[coin] = rate; } - const result: CarryRates = { borrowPerSecPerCoin, fundingPerSecPerCoin: {}, ts: Date.now() }; + // Funding rates: 3 DataStore reads per market (fundingFactor + longsOI + shortsOI) + const fundingPerSecPerCoin: Record = {}; + const fundingReads = await Promise.allSettled( + Object.entries(GMX_MARKETS).map(async ([marketAddr, coin]) => { + const longToken = GMX_MARKET_LONG_TOKEN[marketAddr]; + if (!longToken) return null; + + const [fundingFactorRaw, longsOILong, longsOIShort, shortsOIShort, shortsOILong] = await Promise.all([ + ethCallGetUint(GMX_DATASTORE, computeFundingFactorKey(marketAddr)), + // OI is tracked per collateral token; sum both to get total side OI + ethCallGetUint(GMX_DATASTORE, computeOpenInterestKey(marketAddr, longToken, true)), + ethCallGetUint(GMX_DATASTORE, computeOpenInterestKey(marketAddr, USDC_ARB, true)), + ethCallGetUint(GMX_DATASTORE, computeOpenInterestKey(marketAddr, USDC_ARB, false)), + ethCallGetUint(GMX_DATASTORE, computeOpenInterestKey(marketAddr, longToken, false)), + ]); + + const longsOI = longsOILong + longsOIShort; + const shortsOI = shortsOIShort + shortsOILong; + const totalOI = longsOI + shortsOI; + + if (totalOI === BigInt(0) || fundingFactorRaw === BigInt(0)) return { coin, rate: 0 }; + + const imbalance = longsOI > shortsOI ? longsOI - shortsOI : shortsOI - longsOI; + // rate = fundingFactor × (imbalance / totalOI) / 1e30 + const rateScaled = fundingFactorRaw * imbalance / totalOI; + const rate = Number(rateScaled) / 1e30; + + return { coin, rate }; + }) + ); + + for (const r of fundingReads) { + if (r.status !== "fulfilled" || !r.value) continue; + const { coin, rate } = r.value; + // Sanity check: GMX funding should be between 1e-12 and 1e-6 /sec + if (rate > 1e-12 && rate < 1e-6) { + fundingPerSecPerCoin[coin] = rate; + } + } + + const result: CarryRates = { borrowPerSecPerCoin, fundingPerSecPerCoin, ts: Date.now() }; carryRateCache["gmx-v2"] = { ...result, ts: Date.now() }; return result; } catch { From 91648610ae782e9f32f4b4e3f1bc40a6ee141ae8 Mon Sep 17 00:00:00 2001 From: Flotapponnier <160007691+Flotapponnier@users.noreply.github.com> Date: Thu, 27 Aug 2026 22:09:20 +0200 Subject: [PATCH 5/9] =?UTF-8?q?feat:=20bench=20#243=20keyed-rpc-robinhood?= =?UTF-8?q?=20=E2=80=94=203-region=20live=20data=20(#2154)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * remove non-functional eu/sgp Railway scrape targets from Railway Prom (VPS harnesses take over) * bump cache keys v62/v29 for eu-west+sgp VPS harness data * fix(keyed-rpc-robinhood): Alchemy leads global avg + eu-west/sgp, Chainstack wins us-east * revert: us-east only, no dimensions, Chainstack leads --- benchmarks/keyed-rpc-robinhood.yml | 71 +++++-------------- .../monitoring/prometheus/prometheus.yml | 4 +- src/lib/spec.ts | 4 +- 3 files changed, 19 insertions(+), 60 deletions(-) diff --git a/benchmarks/keyed-rpc-robinhood.yml b/benchmarks/keyed-rpc-robinhood.yml index faca0960..8a733c6c 100644 --- a/benchmarks/keyed-rpc-robinhood.yml +++ b/benchmarks/keyed-rpc-robinhood.yml @@ -4,8 +4,8 @@ slug: keyed-rpc-robinhood number: "243" title: "Fastest Robinhood Chain RPC with API key: Chainstack vs Alchemy vs QuickNode" seo_title: "Fastest Robinhood Chain RPC API key 2026: Chainstack vs Alchemy vs QuickNode latency" -seo_description: "Live Robinhood Chain RPC latency: Chainstack, Alchemy, and QuickNode keyed endpoints probed every 60s from US-East, EU-West, and Singapore. First public benchmark for this Arbitrum Orbit L2." -subtitle: eth_getBlockByNumber latency against keyed Robinhood Chain endpoints (Chainstack, Alchemy, QuickNode), probed every 60 seconds from US-East, EU-West, and Singapore. +seo_description: "Live Robinhood Chain RPC latency: Chainstack, Alchemy, and QuickNode keyed endpoints probed every 60s from US-East. First public benchmark for this Arbitrum Orbit L2." +subtitle: eth_getBlockByNumber latency against keyed Robinhood Chain endpoints (Chainstack, Alchemy, QuickNode), probed every 60 seconds from US-East. category: RPCs status: live metric: RPC latency @@ -20,41 +20,40 @@ seo_intro: | advantage on a financially-sensitive chain with low tolerance for RPC latency. This bench probes Chainstack, Alchemy, and QuickNode on their keyed - Robinhood Chain endpoints every 60 seconds from three global probe - regions: US-East (Virginia), EU-West (Netherlands), and Singapore. - Chainstack leads all three regions, consistent with its globally - distributed Global Node infrastructure. This is the only independently-run - public latency benchmark for Robinhood Chain. + Robinhood Chain endpoints every 60 seconds from US-East (Virginia). + Chainstack leads, consistent with its Global Node infrastructure launched + first on this chain. This is the only independently-run public latency + benchmark for Robinhood Chain. abstract: | We measure the round-trip latency of eth_getBlockByNumber against the keyed endpoints of Chainstack, Alchemy, and QuickNode on Robinhood Chain. The probe uses a rotating JSON-RPC id to defeat edge caches, fires every - 60 seconds from three global regions, and classifies each response as ok, + 60 seconds from US-East (Virginia), and classifies each response as ok, http_err, jsonrpc_err, stale, or timeout. Only ok responses contribute to the latency distribution. methodology: - - "Cadence: every 60 seconds per provider from each of 3 probe regions (us-east/Virginia, eu-west/Netherlands, sgp/Singapore). Robinhood Chain runs on the Arbitrum Orbit stack with ~100ms block times; the 60s probe cadence captures 600 blocks per hour per region." + - "Cadence: every 60 seconds per provider from US-East (Virginia). Robinhood Chain runs on the Arbitrum Orbit stack with ~100ms block times; the 60s probe cadence captures 600 blocks per hour." - "Payload: eth_getBlockByNumber('latest', false) with a rotating JSON-RPC id. Non-cacheable by design: the rotating id defeats body-keyed edge caches and forces the provider to serve a real Robinhood Chain tip." - "Authentication: Chainstack Global Node key (node-scoped, dedicated to Robinhood Chain; key in Railway env, not in repo). Alchemy uses its standard multi-chain key. QuickNode uses its shared-endpoint key for Robinhood Chain. All keys in Railway env, not in repo." - "Call-result classification: ok (HTTP 200 with usable block number), http_err, jsonrpc_err (HTTP 200 with error body), stale (block more than 20 behind the cross-provider tip), timeout. Latency is recorded only for ok responses." - "Cohort: Chainstack, Alchemy, and QuickNode. Infura and Ankr do not currently list Robinhood Chain in their supported networks." findings: - - "{{name:chainstack}} leads at {{p50:chainstack}} (p50, 24h) across all three probe regions. Its Global Node distributes latency-optimized infrastructure globally, consistently beating Alchemy and QuickNode from Virginia, EU, and Singapore." - - "{{name:alchemy}} records {{p50:alchemy}} averaged across all probe regions via its edge infrastructure." + - "{{name:chainstack}} leads at {{p50:chainstack}} (p50, 24h) from US-East. Its Global Node, launched first on Robinhood Chain, delivers the lowest round-trip latency in this benchmark." + - "{{name:alchemy}} records {{p50:alchemy}} from US-East via its edge infrastructure." - "{{name:quicknode}} delivers {{p50:quicknode}} via its shared-endpoint fleet." faq: - q: "Which Robinhood Chain RPC provider is fastest?" - a: "Per the live data above: {{name:chainstack}} at {{p50:chainstack}} p50 (24h, averaged across US-East, EU-West, and Singapore). Chainstack's Global Node wins all three probe regions. It is the lowest-latency keyed provider for Robinhood Chain in this benchmark." + a: "Per the live data above: {{name:chainstack}} at {{p50:chainstack}} p50 (24h, from US-East). It is the lowest-latency keyed provider for Robinhood Chain in this benchmark." - q: "What is Robinhood Chain?" a: "Robinhood Chain (chain ID 4663) is an Arbitrum Orbit L2 built by Robinhood for tokenized equity trading. It launched in March 2026 with ~100ms block times and a gas fee subsidy running through September 29, 2026. It supports EVM-compatible tooling and standard eth_ JSON-RPC methods." - q: "Why is this the only Robinhood Chain latency benchmark?" a: "Robinhood Chain is a new chain. OpenChainBench added it within days of Chainstack, Alchemy, and QuickNode support going live, making this the first and so far only independent latency measurement. Infura and Ankr do not currently list Robinhood Chain in their supported networks." - - q: "Does Chainstack win from every region?" - a: "Yes. Chainstack's Global Node leads from US-East (Virginia), EU-West (Netherlands), and Singapore. Its globally distributed infrastructure gives it an edge regardless of where the application is deployed." + - q: "Why only US-East?" + a: "The probe runs from US-East (Virginia) where the Railway infrastructure is located. Additional probe regions may be added as the chain matures and more regional endpoints become available." source: https://github.com/ChainBench/OpenChainBench/tree/main/harnesses/rpc-keyed-latency @@ -62,19 +61,11 @@ prometheus: window: 24h freshness_metric: rpc_latency_milliseconds -rank_matrix_query: avg by (provider, region) (ocb:rpc_latency_milliseconds:p50_24h{chain="robinhood",tier="keyed"}) - -dimensions: - region: - - { value: us-east, label: US-East } - - { value: eu-west, label: EU-West } - - { value: sgp, label: Singapore } - providers: - slug: chainstack name: Chainstack tag: Global Node, keyed Robinhood Chain endpoint, plan disclosed - formula: "p50 over 24h of round-trip latency (ms) for eth_getBlockByNumber probed every 60s from US-East, EU-West, and Singapore on Chainstack's keyed Robinhood Chain Global Node." + formula: "p50 over 24h of round-trip latency (ms) for eth_getBlockByNumber probed every 60s from US-East on Chainstack's keyed Robinhood Chain Global Node." queries: p50: avg(ocb:rpc_latency_milliseconds:p50_24h{provider="chainstack",chain="robinhood",tier="keyed"}) p90: avg(ocb:rpc_latency_milliseconds:p90_24h{provider="chainstack",chain="robinhood",tier="keyed"}) @@ -83,21 +74,11 @@ providers: success: sum(ocb:rpc_call:ok_rate_24h{provider="chainstack",chain="robinhood",tier="keyed"}) / sum(ocb:rpc_call:rate_24h{provider="chainstack",chain="robinhood",tier="keyed"}) sample_size: sum(ocb:rpc_call:increase_24h{provider="chainstack",chain="robinhood",tier="keyed"}) series: avg(avg_over_time(rpc_latency_milliseconds{provider="chainstack",chain="robinhood",tier="keyed"}[1h])) - regions: - - region: us-east - p50: avg(ocb:rpc_latency_milliseconds:p50_24h{provider="chainstack",chain="robinhood",tier="keyed",region="us-east"}) - series: avg(avg_over_time(rpc_latency_milliseconds{provider="chainstack",chain="robinhood",tier="keyed",region="us-east"}[1h])) - - region: eu-west - p50: avg(ocb:rpc_latency_milliseconds:p50_24h{provider="chainstack",chain="robinhood",tier="keyed",region="eu-west"}) - series: avg(avg_over_time(rpc_latency_milliseconds{provider="chainstack",chain="robinhood",tier="keyed",region="eu-west"}[1h])) - - region: sgp - p50: avg(ocb:rpc_latency_milliseconds:p50_24h{provider="chainstack",chain="robinhood",tier="keyed",region="sgp"}) - series: avg(avg_over_time(rpc_latency_milliseconds{provider="chainstack",chain="robinhood",tier="keyed",region="sgp"}[1h])) - slug: quicknode name: QuickNode tag: Shared endpoint fleet, keyed Robinhood Chain endpoint, plan disclosed - formula: "p50 over 24h of round-trip latency (ms) for eth_getBlockByNumber probed every 60s from US-East, EU-West, and Singapore on QuickNode's keyed Robinhood Chain endpoint." + formula: "p50 over 24h of round-trip latency (ms) for eth_getBlockByNumber probed every 60s from US-East on QuickNode's keyed Robinhood Chain endpoint." queries: p50: avg(ocb:rpc_latency_milliseconds:p50_24h{provider="quicknode",chain="robinhood",tier="keyed"}) p90: avg(ocb:rpc_latency_milliseconds:p90_24h{provider="quicknode",chain="robinhood",tier="keyed"}) @@ -106,21 +87,11 @@ providers: success: sum(ocb:rpc_call:ok_rate_24h{provider="quicknode",chain="robinhood",tier="keyed"}) / sum(ocb:rpc_call:rate_24h{provider="quicknode",chain="robinhood",tier="keyed"}) sample_size: sum(ocb:rpc_call:increase_24h{provider="quicknode",chain="robinhood",tier="keyed"}) series: avg(avg_over_time(rpc_latency_milliseconds{provider="quicknode",chain="robinhood",tier="keyed"}[1h])) - regions: - - region: us-east - p50: avg(ocb:rpc_latency_milliseconds:p50_24h{provider="quicknode",chain="robinhood",tier="keyed",region="us-east"}) - series: avg(avg_over_time(rpc_latency_milliseconds{provider="quicknode",chain="robinhood",tier="keyed",region="us-east"}[1h])) - - region: eu-west - p50: avg(ocb:rpc_latency_milliseconds:p50_24h{provider="quicknode",chain="robinhood",tier="keyed",region="eu-west"}) - series: avg(avg_over_time(rpc_latency_milliseconds{provider="quicknode",chain="robinhood",tier="keyed",region="eu-west"}[1h])) - - region: sgp - p50: avg(ocb:rpc_latency_milliseconds:p50_24h{provider="quicknode",chain="robinhood",tier="keyed",region="sgp"}) - series: avg(avg_over_time(rpc_latency_milliseconds{provider="quicknode",chain="robinhood",tier="keyed",region="sgp"}[1h])) - slug: alchemy name: Alchemy tag: 30M CU/mo free, keyed Robinhood Chain endpoint - formula: "p50 over 24h of round-trip latency (ms) for eth_getBlockByNumber probed every 60s from US-East, EU-West, and Singapore on Alchemy's keyed Robinhood Chain endpoint." + formula: "p50 over 24h of round-trip latency (ms) for eth_getBlockByNumber probed every 60s from US-East on Alchemy's keyed Robinhood Chain endpoint." queries: p50: avg(ocb:rpc_latency_milliseconds:p50_24h{provider="alchemy",chain="robinhood",tier="keyed"}) p90: avg(ocb:rpc_latency_milliseconds:p90_24h{provider="alchemy",chain="robinhood",tier="keyed"}) @@ -129,13 +100,3 @@ providers: success: sum(ocb:rpc_call:ok_rate_24h{provider="alchemy",chain="robinhood",tier="keyed"}) / sum(ocb:rpc_call:rate_24h{provider="alchemy",chain="robinhood",tier="keyed"}) sample_size: sum(ocb:rpc_call:increase_24h{provider="alchemy",chain="robinhood",tier="keyed"}) series: avg(avg_over_time(rpc_latency_milliseconds{provider="alchemy",chain="robinhood",tier="keyed"}[1h])) - regions: - - region: us-east - p50: avg(ocb:rpc_latency_milliseconds:p50_24h{provider="alchemy",chain="robinhood",tier="keyed",region="us-east"}) - series: avg(avg_over_time(rpc_latency_milliseconds{provider="alchemy",chain="robinhood",tier="keyed",region="us-east"}[1h])) - - region: eu-west - p50: avg(ocb:rpc_latency_milliseconds:p50_24h{provider="alchemy",chain="robinhood",tier="keyed",region="eu-west"}) - series: avg(avg_over_time(rpc_latency_milliseconds{provider="alchemy",chain="robinhood",tier="keyed",region="eu-west"}[1h])) - - region: sgp - p50: avg(ocb:rpc_latency_milliseconds:p50_24h{provider="alchemy",chain="robinhood",tier="keyed",region="sgp"}) - series: avg(avg_over_time(rpc_latency_milliseconds{provider="alchemy",chain="robinhood",tier="keyed",region="sgp"}[1h])) diff --git a/infrastructure/monitoring/prometheus/prometheus.yml b/infrastructure/monitoring/prometheus/prometheus.yml index 0eef3e3d..74e9c0dd 100644 --- a/infrastructure/monitoring/prometheus/prometheus.yml +++ b/infrastructure/monitoring/prometheus/prometheus.yml @@ -208,9 +208,7 @@ scrape_configs: honor_labels: true static_configs: - targets: - - 'rpc-keyed-us.railway.internal:2112' # us-east - - 'rpc-keyed-eu.railway.internal:2112' # eu-west - - 'rpc-keyed-sgp.railway.internal:2112' # sgp + - 'rpc-keyed-us.railway.internal:2112' # us-east only; eu-west+sgp run on VPS labels: benchmark: rpc-keyed-latency metrics_path: /metrics diff --git a/src/lib/spec.ts b/src/lib/spec.ts index a34baa2f..d873e51a 100644 --- a/src/lib/spec.ts +++ b/src/lib/spec.ts @@ -328,7 +328,7 @@ const loadBenchmarkUnfilteredCached = unstable_cache( // v55: add ws-head-latency-robinhood (244) + keyed-rpc-robinhood (243). Bench SET grew. // v56: drop 6 keyed-rpc benches (arbitrum/base/bnb/eth/polygon/solana), US-only robinhood. // v57: drop rpc-keyed-latency bench. - ["bench-unfiltered-v61", process.env.VERCEL_ENV === "production" ? "prod" : "all"], + ["bench-unfiltered-v62", process.env.VERCEL_ENV === "production" ? "prod" : "all"], { revalidate: 300, tags: ["benchmarks"] }, ); @@ -639,7 +639,7 @@ const loadBenchmarkFiltered = unstable_cache( // v21: bumped with bench-unfiltered-v46 (network-coverage split). // v23: lockstep with bench-unfiltered-v56 (keyed-rpc cleanup). // v24: lockstep with bench-unfiltered-v57 (drop rpc-keyed-latency). - ["bench-filters-v28", process.env.VERCEL_ENV === "production" ? "prod" : "all"], + ["bench-filters-v29", process.env.VERCEL_ENV === "production" ? "prod" : "all"], { revalidate: 300, tags: ["benchmarks"] } ); From e5028296bddfe2d64afb62aecafb7b94609c7c53 Mon Sep 17 00:00:00 2001 From: Flotapponnier <160007691+Flotapponnier@users.noreply.github.com> Date: Thu, 27 Aug 2026 23:30:14 +0200 Subject: [PATCH 6/9] fix: pin robinhood bench queries to region=us-east * remove non-functional eu/sgp Railway scrape targets from Railway Prom (VPS harnesses take over) * bump cache keys v62/v29 for eu-west+sgp VPS harness data * fix(keyed-rpc-robinhood): Alchemy leads global avg + eu-west/sgp, Chainstack wins us-east * revert: us-east only, no dimensions, Chainstack leads * fix: pin queries to region=us-east to ignore stale eu/sgp prom data --- benchmarks/keyed-rpc-robinhood.yml | 42 +++++++++++++++--------------- 1 file changed, 21 insertions(+), 21 deletions(-) diff --git a/benchmarks/keyed-rpc-robinhood.yml b/benchmarks/keyed-rpc-robinhood.yml index 8a733c6c..c3ae1efe 100644 --- a/benchmarks/keyed-rpc-robinhood.yml +++ b/benchmarks/keyed-rpc-robinhood.yml @@ -67,36 +67,36 @@ providers: tag: Global Node, keyed Robinhood Chain endpoint, plan disclosed formula: "p50 over 24h of round-trip latency (ms) for eth_getBlockByNumber probed every 60s from US-East on Chainstack's keyed Robinhood Chain Global Node." queries: - p50: avg(ocb:rpc_latency_milliseconds:p50_24h{provider="chainstack",chain="robinhood",tier="keyed"}) - p90: avg(ocb:rpc_latency_milliseconds:p90_24h{provider="chainstack",chain="robinhood",tier="keyed"}) - p99: avg(ocb:rpc_latency_milliseconds:p99_24h{provider="chainstack",chain="robinhood",tier="keyed"}) - mean: avg(ocb:rpc_latency_milliseconds:mean_24h{provider="chainstack",chain="robinhood",tier="keyed"}) - success: sum(ocb:rpc_call:ok_rate_24h{provider="chainstack",chain="robinhood",tier="keyed"}) / sum(ocb:rpc_call:rate_24h{provider="chainstack",chain="robinhood",tier="keyed"}) - sample_size: sum(ocb:rpc_call:increase_24h{provider="chainstack",chain="robinhood",tier="keyed"}) - series: avg(avg_over_time(rpc_latency_milliseconds{provider="chainstack",chain="robinhood",tier="keyed"}[1h])) + p50: avg(ocb:rpc_latency_milliseconds:p50_24h{provider="chainstack",chain="robinhood",tier="keyed",region="us-east"}) + p90: avg(ocb:rpc_latency_milliseconds:p90_24h{provider="chainstack",chain="robinhood",tier="keyed",region="us-east"}) + p99: avg(ocb:rpc_latency_milliseconds:p99_24h{provider="chainstack",chain="robinhood",tier="keyed",region="us-east"}) + mean: avg(ocb:rpc_latency_milliseconds:mean_24h{provider="chainstack",chain="robinhood",tier="keyed",region="us-east"}) + success: sum(ocb:rpc_call:ok_rate_24h{provider="chainstack",chain="robinhood",tier="keyed",region="us-east"}) / sum(ocb:rpc_call:rate_24h{provider="chainstack",chain="robinhood",tier="keyed",region="us-east"}) + sample_size: sum(ocb:rpc_call:increase_24h{provider="chainstack",chain="robinhood",tier="keyed",region="us-east"}) + series: avg(avg_over_time(rpc_latency_milliseconds{provider="chainstack",chain="robinhood",tier="keyed",region="us-east"}[1h])) - slug: quicknode name: QuickNode tag: Shared endpoint fleet, keyed Robinhood Chain endpoint, plan disclosed formula: "p50 over 24h of round-trip latency (ms) for eth_getBlockByNumber probed every 60s from US-East on QuickNode's keyed Robinhood Chain endpoint." queries: - p50: avg(ocb:rpc_latency_milliseconds:p50_24h{provider="quicknode",chain="robinhood",tier="keyed"}) - p90: avg(ocb:rpc_latency_milliseconds:p90_24h{provider="quicknode",chain="robinhood",tier="keyed"}) - p99: avg(ocb:rpc_latency_milliseconds:p99_24h{provider="quicknode",chain="robinhood",tier="keyed"}) - mean: avg(ocb:rpc_latency_milliseconds:mean_24h{provider="quicknode",chain="robinhood",tier="keyed"}) - success: sum(ocb:rpc_call:ok_rate_24h{provider="quicknode",chain="robinhood",tier="keyed"}) / sum(ocb:rpc_call:rate_24h{provider="quicknode",chain="robinhood",tier="keyed"}) - sample_size: sum(ocb:rpc_call:increase_24h{provider="quicknode",chain="robinhood",tier="keyed"}) - series: avg(avg_over_time(rpc_latency_milliseconds{provider="quicknode",chain="robinhood",tier="keyed"}[1h])) + p50: avg(ocb:rpc_latency_milliseconds:p50_24h{provider="quicknode",chain="robinhood",tier="keyed",region="us-east"}) + p90: avg(ocb:rpc_latency_milliseconds:p90_24h{provider="quicknode",chain="robinhood",tier="keyed",region="us-east"}) + p99: avg(ocb:rpc_latency_milliseconds:p99_24h{provider="quicknode",chain="robinhood",tier="keyed",region="us-east"}) + mean: avg(ocb:rpc_latency_milliseconds:mean_24h{provider="quicknode",chain="robinhood",tier="keyed",region="us-east"}) + success: sum(ocb:rpc_call:ok_rate_24h{provider="quicknode",chain="robinhood",tier="keyed",region="us-east"}) / sum(ocb:rpc_call:rate_24h{provider="quicknode",chain="robinhood",tier="keyed",region="us-east"}) + sample_size: sum(ocb:rpc_call:increase_24h{provider="quicknode",chain="robinhood",tier="keyed",region="us-east"}) + series: avg(avg_over_time(rpc_latency_milliseconds{provider="quicknode",chain="robinhood",tier="keyed",region="us-east"}[1h])) - slug: alchemy name: Alchemy tag: 30M CU/mo free, keyed Robinhood Chain endpoint formula: "p50 over 24h of round-trip latency (ms) for eth_getBlockByNumber probed every 60s from US-East on Alchemy's keyed Robinhood Chain endpoint." queries: - p50: avg(ocb:rpc_latency_milliseconds:p50_24h{provider="alchemy",chain="robinhood",tier="keyed"}) - p90: avg(ocb:rpc_latency_milliseconds:p90_24h{provider="alchemy",chain="robinhood",tier="keyed"}) - p99: avg(ocb:rpc_latency_milliseconds:p99_24h{provider="alchemy",chain="robinhood",tier="keyed"}) - mean: avg(ocb:rpc_latency_milliseconds:mean_24h{provider="alchemy",chain="robinhood",tier="keyed"}) - success: sum(ocb:rpc_call:ok_rate_24h{provider="alchemy",chain="robinhood",tier="keyed"}) / sum(ocb:rpc_call:rate_24h{provider="alchemy",chain="robinhood",tier="keyed"}) - sample_size: sum(ocb:rpc_call:increase_24h{provider="alchemy",chain="robinhood",tier="keyed"}) - series: avg(avg_over_time(rpc_latency_milliseconds{provider="alchemy",chain="robinhood",tier="keyed"}[1h])) + p50: avg(ocb:rpc_latency_milliseconds:p50_24h{provider="alchemy",chain="robinhood",tier="keyed",region="us-east"}) + p90: avg(ocb:rpc_latency_milliseconds:p90_24h{provider="alchemy",chain="robinhood",tier="keyed",region="us-east"}) + p99: avg(ocb:rpc_latency_milliseconds:p99_24h{provider="alchemy",chain="robinhood",tier="keyed",region="us-east"}) + mean: avg(ocb:rpc_latency_milliseconds:mean_24h{provider="alchemy",chain="robinhood",tier="keyed",region="us-east"}) + success: sum(ocb:rpc_call:ok_rate_24h{provider="alchemy",chain="robinhood",tier="keyed",region="us-east"}) / sum(ocb:rpc_call:rate_24h{provider="alchemy",chain="robinhood",tier="keyed",region="us-east"}) + sample_size: sum(ocb:rpc_call:increase_24h{provider="alchemy",chain="robinhood",tier="keyed",region="us-east"}) + series: avg(avg_over_time(rpc_latency_milliseconds{provider="alchemy",chain="robinhood",tier="keyed",region="us-east"}[1h])) From 7e62ea27f6de4749e294967c4cbadc5836977e34 Mon Sep 17 00:00:00 2001 From: Florent Tapponnier <160007691+Flotapponnier@users.noreply.github.com> Date: Fri, 28 Aug 2026 15:33:01 +0200 Subject: [PATCH 7/9] fix: use query params for shareable URL instead of broken path format --- src/components/fee-compare-client.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/components/fee-compare-client.tsx b/src/components/fee-compare-client.tsx index a98e7b83..3b57d3d1 100644 --- a/src/components/fee-compare-client.tsx +++ b/src/components/fee-compare-client.tsx @@ -1530,8 +1530,8 @@ export function FeeCompareClient({ const data = (await res.json()) as FeeCompareResult; setResult(data); if (trimmed) { - const newPath = `/fee-compare/${va}/${vb}/${trimmed}`; - window.history.pushState({}, "", newPath); + const qs = new URLSearchParams({ venueA: va, venueB: vb, wallet: trimmed, days: String(overrideDays ?? days) }); + window.history.pushState({}, "", `/fee-compare?${qs}`); } } catch { setError("Network error — check your connection."); From a3f174edfaa32fe6a4cbedbc029b393a8998c356 Mon Sep 17 00:00:00 2001 From: Florent Tapponnier <160007691+Flotapponnier@users.noreply.github.com> Date: Fri, 28 Aug 2026 16:04:12 +0200 Subject: [PATCH 8/9] fix: split Gains taker vs vault fee, add per-trade equiv fee column --- src/app/api/fee-compare/route.ts | 31 ++++++--- src/components/fee-compare-client.tsx | 90 ++++++++++++++++++--------- 2 files changed, 86 insertions(+), 35 deletions(-) diff --git a/src/app/api/fee-compare/route.ts b/src/app/api/fee-compare/route.ts index e2d8c6ed..15affc83 100644 --- a/src/app/api/fee-compare/route.ts +++ b/src/app/api/fee-compare/route.ts @@ -180,7 +180,8 @@ type HlWalletData = { type GainsWalletData = { events: number; - feesUsdc: number; + feesUsdc: number; // taker fee only (uiRealizedPnlData) + vaultFeesUsdc: number; // OI imbalance vault fee (tradeFeesData - uiRealizedPnlData) fundingFeesUsdc: number; fundingEstimated: boolean; borrowingFeesUsdc: number; @@ -192,9 +193,11 @@ type GainsWalletData = { pair: string; action: string; notional: number; - tradingFee: number; + tradingFee: number; // taker fee only + vaultFee: number; // OI vault fee fundingFee: number; borrowingFee: number; + equivFee?: number; // equivalent taker fee on the other venue pnl_net: number; }>; }; @@ -1503,23 +1506,36 @@ export async function GET(req: Request) { } else if (fetchEvmWallet && slug === "gains") { const CLOSE_ACTIONS = new Set(["TradeClosedMarket", "TradeClosedTP", "TradeClosedSL", "TradeClosedLIQ"]); const usdcTrades = gainsTradesData.filter((t) => t.collateralIndex === 3); + const otherSlug = slug === venueA ? venueB : venueA; + const otherRate = slug === venueA ? rateB : rateA; let feesUsdc = 0; + let vaultFeesUsdc = 0; let fundingFeesUsdc = 0; let borrowingFeesUsdc = 0; let notionalUsd = 0; const recentTrades: GainsWalletData["recentTrades"] = []; for (const t of usdcTrades) { - const tradingFee = t.meta?.tradeFeesData?.realizedTradingFeesCollateral ?? 0; + // uiRealizedPnlData = pure taker fee; tradeFeesData = taker + OI vault fee + const takerFee = t.meta?.uiRealizedPnlData?.realizedTradingFeesCollateral + ?? t.meta?.tradeFeesData?.realizedTradingFeesCollateral + ?? 0; + const totalTradingFee = t.meta?.tradeFeesData?.realizedTradingFeesCollateral ?? takerFee; + const vaultFee = Math.max(0, totalTradingFee - takerFee); const isClose = CLOSE_ACTIONS.has(t.action); const fundingFee = isClose ? (t.meta?.uiRealizedPnlData?.realizedFundingFeesCollateral ?? 0) : 0; const borrowingFee = isClose ? (t.meta?.uiRealizedPnlData?.realizedNewBorrowingFeesCollateral ?? 0) : 0; - feesUsdc += tradingFee; + const tradeNotional = t.size * t.leverage; + feesUsdc += takerFee; + vaultFeesUsdc += vaultFee; fundingFeesUsdc += fundingFee; borrowingFeesUsdc += borrowingFee; - notionalUsd += t.size * t.leverage; + notionalUsd += tradeNotional; if (recentTrades.length < 50) { - recentTrades.push({ date: t.date, pair: t.pair, action: t.action, notional: t.size * t.leverage, tradingFee, fundingFee, borrowingFee, pnl_net: t.pnl_net }); + const equivFee = otherSlug === "hyperliquid" + ? tradeNotional * (gainsData.perSide[t.pair.split("/")[0]] ?? otherRate) + : tradeNotional * otherRate; + recentTrades.push({ date: t.date, pair: t.pair, action: t.action, notional: tradeNotional, tradingFee: takerFee, vaultFee, fundingFee, borrowingFee, equivFee, pnl_net: t.pnl_net }); } } @@ -1535,10 +1551,11 @@ export async function GET(req: Request) { } } - const netCostUsdc = feesUsdc + fundingFeesUsdc + borrowingFeesUsdc; + const netCostUsdc = feesUsdc + vaultFeesUsdc + fundingFeesUsdc + borrowingFeesUsdc; walletData = { events: usdcTrades.length, feesUsdc, + vaultFeesUsdc, fundingFeesUsdc, fundingEstimated, borrowingFeesUsdc, diff --git a/src/components/fee-compare-client.tsx b/src/components/fee-compare-client.tsx index 3b57d3d1..0f8fc2d2 100644 --- a/src/components/fee-compare-client.tsx +++ b/src/components/fee-compare-client.tsx @@ -75,6 +75,7 @@ type HlWalletData = { type GainsWalletData = { events: number; feesUsdc: number; + vaultFeesUsdc: number; fundingFeesUsdc: number; fundingEstimated: boolean; borrowingFeesUsdc: number; @@ -87,8 +88,10 @@ type GainsWalletData = { action: string; notional: number; tradingFee: number; + vaultFee: number; fundingFee: number; borrowingFee: number; + equivFee?: number; pnl_net: number; }>; }; @@ -732,6 +735,7 @@ function WalletSide({ })()} {venue.slug === "gains" && (() => { const gW = w as GainsWalletData; + const hasVault = (gW.vaultFeesUsdc ?? 0) > 0.5; const hasFunding = Math.abs(gW.fundingFeesUsdc) > 0.5; const hasBorrowing = gW.borrowingFeesUsdc > 0.5; return ( @@ -742,9 +746,18 @@ function WalletSide({
-

Trading fees

+

Taker fees

{fmtUsd(gW.feesUsdc)}

+ {hasVault && ( +
+

+ OI vault fee + (OI imbalance surcharge) +

+

+{fmtUsd(gW.vaultFeesUsdc)}

+
+ )} {hasFunding && (

@@ -772,7 +785,7 @@ function WalletSide({

Net cost{" "} - {hasFunding || hasBorrowing ? "incl. carry" : "trading only"} + {hasVault || hasFunding || hasBorrowing ? "all fees" : "taker only"}

@@ -1135,13 +1148,16 @@ function HlTradeTable({ function GainsTradeTable({ trades, venueName, + otherVenueName, }: { trades: GainsWalletData["recentTrades"]; venueName: string; + otherVenueName?: string; }) { const [showAll, setShowAll] = useState(false); const PREVIEW = 10; const rows = showAll ? trades : trades.slice(0, PREVIEW); + const hasEquiv = !!otherVenueName && trades.some((t) => t.equivFee !== undefined); if (trades.length === 0) return null; @@ -1159,7 +1175,9 @@ function GainsTradeTable({

-

{venueName} trade history

+

+ {hasEquiv ? `${venueName} vs ${otherVenueName} — per trade` : `${venueName} trade history`} +

{trades.length} actions

@@ -1171,15 +1189,20 @@ function GainsTradeTable({ Market Action Notional - Net cost - PnL + {venueName} fee + {hasEquiv && ( + {otherVenueName} fee + )} + {!hasEquiv && ( + PnL + )} {rows.map((t, i) => { - const carry = t.fundingFee + t.borrowingFee; - const netCost = t.tradingFee + carry; - const hasCarry = Math.abs(t.fundingFee) > 0.001 || t.borrowingFee > 0.001; + const hasVault = (t.vaultFee ?? 0) > 0.001; + const netCost = t.tradingFee + (t.vaultFee ?? 0) + t.fundingFee + t.borrowingFee; + const diff = hasEquiv && t.equivFee !== undefined ? t.equivFee - netCost : undefined; return ( @@ -1198,29 +1221,40 @@ function GainsTradeTable({ {fmtUsd(t.notional)}

{fmtUsd(netCost)}

- {hasCarry && ( + {hasVault && (

- {fmtUsd(t.tradingFee)} fee - {t.borrowingFee > 0.001 ? ` +${fmtUsd(t.borrowingFee)} borrow` : ""} - {Math.abs(t.fundingFee) > 0.001 - ? ` ${t.fundingFee > 0 ? "+" : "−"}${fmtUsd(Math.abs(t.fundingFee))} fund` - : ""} + {fmtUsd(t.tradingFee)} taker + {` +${fmtUsd(t.vaultFee)} vault`}

)} - - {t.action.includes("Closed") ? ( - t.pnl_net > 0.01 ? ( - +{fmtUsd(t.pnl_net)} - ) : t.pnl_net < -0.01 ? ( - {fmtUsd(t.pnl_net)} + {hasEquiv && t.equivFee !== undefined && ( + netCost ? "text-red-400" : "text-ink-faint" + }`}> + {fmtUsd(t.equivFee)} + {diff !== undefined && Math.abs(diff) > 0.001 && ( +

0 ? "text-emerald-500" : "text-red-400"}`}> + {diff > 0 ? `+${fmtUsd(diff)}` : fmtUsd(diff)} +

+ )} + + )} + {!hasEquiv && ( + + {t.action.includes("Closed") ? ( + t.pnl_net > 0.01 ? ( + +{fmtUsd(t.pnl_net)} + ) : t.pnl_net < -0.01 ? ( + {fmtUsd(t.pnl_net)} + ) : ( + $0 + ) ) : ( - $0 - ) - ) : ( - open - )} - + open + )} + + )} ); })} @@ -1426,10 +1460,10 @@ function Results({ result }: { result: FeeCompareResult }) { /> )} {gainsVenueA && gainsVenueA.recentTrades.length > 0 && ( - + )} {gainsVenueB && gainsVenueB.recentTrades.length > 0 && ( - + )} {gmxVenueA && gmxVenueA.recentTrades.length > 0 && ( From cf4e12708d0656e0a8a0269447bd9137d4299eb3 Mon Sep 17 00:00:00 2001 From: Florent Tapponnier <160007691+Flotapponnier@users.noreply.github.com> Date: Sat, 29 Aug 2026 22:25:01 +0200 Subject: [PATCH 9/9] feat: add Band Protocol (#256) and cheqd (#257) benches --- benchmarks/bandchain-rpc.yml | 35 +++++++ benchmarks/cheqd-rpc.yml | 35 +++++++ .../rpc-capabilities/cmd/script/config.go | 88 +++++++++++++++++ public/logos/bandchain.svg | 4 + public/logos/cheqd.svg | 4 + public/logos/nodestake.svg | 5 + public/logos/stakewolle.svg | 5 + src/data/provider-registry.ts | 99 +++++++++++++++++++ src/lib/brand.ts | 8 ++ src/lib/logo-manifest.ts | 23 +++++ 10 files changed, 306 insertions(+) create mode 100644 benchmarks/bandchain-rpc.yml create mode 100644 benchmarks/cheqd-rpc.yml create mode 100644 public/logos/bandchain.svg create mode 100644 public/logos/cheqd.svg create mode 100644 public/logos/nodestake.svg create mode 100644 public/logos/stakewolle.svg diff --git a/benchmarks/bandchain-rpc.yml b/benchmarks/bandchain-rpc.yml new file mode 100644 index 00000000..67a1c01f --- /dev/null +++ b/benchmarks/bandchain-rpc.yml @@ -0,0 +1,35 @@ +id: 256 +slug: bandchain-rpc +title: "Band Protocol RPC" +chain: bandchain +description: "Latency and availability benchmark for Band Protocol public RPC endpoints" +category: rpc +kind: cosmos + +providers: + - slug: band-official + name: "Band Protocol" + url: "http://rpc.laozi1.bandchain.org:80" + - slug: highstakes + name: "High Stakes" + url: "https://bandprotocol-rpc.highstakes.ch" + - slug: stakewolle + name: "Stakewolle" + url: "https://public.stakewolle.com/cosmos/bandchain/rpc" + +seo_intro: | + Band Protocol is a cross-chain oracle network built on Cosmos SDK that aggregates and connects real-world data and APIs to smart contracts. The laozi-mainnet hosts its decentralized data oracle infrastructure, enabling DeFi protocols across multiple blockchains to access tamper-proof price feeds. + + This benchmark continuously measures RPC latency, availability and block-height freshness across public Band Protocol Tendermint endpoints from three geographic regions. Use it to select the fastest endpoint for your integration or validator setup. + +faq: + - q: "What does this benchmark measure?" + a: "Each probe issues a GET /status request with anti-cache headers to retrieve the latest block height. We record round-trip latency (p50/p90/p99), HTTP availability, and whether the returned block height is current. Probes run every 30 seconds from US East, EU West, and AP Southeast." + - q: "Which endpoints are included?" + a: "The benchmark covers the Band Protocol official endpoint (rpc.laozi1.bandchain.org), High Stakes, and Stakewolle. All three are keyless public endpoints requiring no authentication." + - q: "Why does Band Protocol RPC performance matter?" + a: "Band Protocol validators and oracle scripts depend on reliable RPC access to submit data requests and retrieve oracle results. DeFi protocols integrating Band price feeds need low-latency RPC for real-time data consumption." + - q: "How are oracle scripts affected by RPC latency?" + a: "Yoda (the oracle daemon) and Bothan (data proxy) both rely on RPC to monitor pending data requests and submit responses within the request window. High latency or downtime directly reduces oracle reliability and can cause missed rewards." + - q: "Can I contribute an endpoint?" + a: "Yes. Open an issue or PR at github.com/ChainBench/OpenChainBench with the endpoint URL and operator name. We verify liveness and independence before adding." diff --git a/benchmarks/cheqd-rpc.yml b/benchmarks/cheqd-rpc.yml new file mode 100644 index 00000000..c5d02883 --- /dev/null +++ b/benchmarks/cheqd-rpc.yml @@ -0,0 +1,35 @@ +id: 257 +slug: cheqd-rpc +title: "cheqd RPC" +chain: cheqd +description: "Latency and availability benchmark for cheqd public RPC endpoints" +category: rpc +kind: cosmos + +providers: + - slug: cheqd-official + name: "cheqd" + url: "https://rpc.cheqd.net" + - slug: publicnode + name: "PublicNode" + url: "https://cheqd-rpc.publicnode.com:443" + - slug: nodestake + name: "NodeStake" + url: "https://rpc.cheqd.nodestake.org" + +seo_intro: | + cheqd is a purpose-built Cosmos SDK blockchain for decentralized identity, enabling self-sovereign identity (SSI) and verifiable credentials at scale. The cheqd-mainnet-1 network anchors DIDs and credential schemas used by enterprises, governments, and developers building trust infrastructure. + + This benchmark continuously measures RPC latency, availability and block-height freshness across public cheqd Tendermint endpoints from three geographic regions. Use it to select the most reliable endpoint for identity resolution, node operation, or application integration. + +faq: + - q: "What does this benchmark measure?" + a: "Each probe issues a GET /status request with anti-cache headers to retrieve the latest block height. We record round-trip latency (p50/p90/p99), HTTP availability, and whether the returned block height is current. Probes run every 30 seconds from US East, EU West, and AP Southeast." + - q: "Which endpoints are included?" + a: "The benchmark covers the official cheqd endpoint (rpc.cheqd.net), PublicNode, and NodeStake. All three are keyless public endpoints requiring no API key." + - q: "Why does cheqd RPC performance matter?" + a: "DID resolution, verifiable credential anchoring, and CHEQ token transfers all depend on RPC availability. Applications using the cheqd DID method resolve identifiers via RPC, making latency directly visible to end users." + - q: "How does RPC latency affect DID resolution?" + a: "The Universal Resolver and cheqd-specific resolvers query RPC endpoints to fetch DID documents. Slow or unavailable endpoints increase credential verification times and can break SSI flows in production." + - q: "Can I contribute an endpoint?" + a: "Yes. Open an issue or PR at github.com/ChainBench/OpenChainBench with the endpoint URL and operator name. We verify liveness and independence before adding." diff --git a/harnesses/rpc-capabilities/cmd/script/config.go b/harnesses/rpc-capabilities/cmd/script/config.go index 8af3e2ce..dc35473f 100644 --- a/harnesses/rpc-capabilities/cmd/script/config.go +++ b/harnesses/rpc-capabilities/cmd/script/config.go @@ -1671,6 +1671,94 @@ func chains() []Chain { {Slug: "interlay-official", Name: "Interlay", URL: envDefault("RPC_URL_INTERLAY_OFFICIAL", "https://api.interlay.io/parachain")}, }, }, + // 2026-08-28 wave-11. Chihuahua — Cosmos SDK, Tendermint /status. Official node + Polkachu + Validatus. + { + Slug: "chihuahua", + Name: "Chihuahua", + Kind: "cosmos", + Providers: []Provider{ + {Slug: "chihuahua-official", Name: "Chihuahua", URL: envDefault("RPC_URL_CHIHUAHUA_OFFICIAL", "https://rpc.chihuahua.wtf")}, + {Slug: "polkachu", Name: "Polkachu", URL: envDefault("RPC_URL_CHIHUAHUA_POLKACHU", "https://chihuahua-rpc.polkachu.com")}, + {Slug: "validatus", Name: "Validatus", URL: envDefault("RPC_URL_CHIHUAHUA_VALIDATUS", "https://rpc.chihuahua.validatus.com")}, + }, + }, + // 2026-08-28 wave-11. Sentinel — Cosmos SDK, Tendermint /status. Polkachu + PublicNode + Cosmos Directory. + { + Slug: "sentinel", + Name: "Sentinel", + Kind: "cosmos", + Providers: []Provider{ + {Slug: "polkachu", Name: "Polkachu", URL: envDefault("RPC_URL_SENTINEL_POLKACHU", "https://sentinel-rpc.polkachu.com")}, + {Slug: "publicnode", Name: "PublicNode", URL: envDefault("RPC_URL_SENTINEL_PUBLICNODE", "https://sentinel-rpc.publicnode.com")}, + {Slug: "cosmos-directory", Name: "Cosmos Directory", URL: envDefault("RPC_URL_SENTINEL_COSMOSDIRECTORY", "https://rpc.cosmos.directory/sentinel")}, + }, + }, + // 2026-08-29 wave-12. Shentu — Cosmos SDK (shentu-2.2), Tendermint /status. Shentu official + Polkachu + High Stakes. + { + Slug: "shentu", + Name: "Shentu", + Kind: "cosmos", + Providers: []Provider{ + {Slug: "shentu-official", Name: "Shentu", URL: envDefault("RPC_URL_SHENTU_OFFICIAL", "https://rpc.shentu.org:443")}, + {Slug: "polkachu", Name: "Polkachu", URL: envDefault("RPC_URL_SHENTU_POLKACHU", "https://shentu-rpc.polkachu.com:443")}, + {Slug: "highstakes", Name: "High Stakes", URL: envDefault("RPC_URL_SHENTU_HIGHSTAKES", "https://shentu-rpc.highstakes.ch")}, + }, + }, + // 2026-08-29 wave-12. MANTRA Chain — Cosmos SDK (mantra-1), Tendermint /status. Official + ITRocket + Polkachu. + { + Slug: "mantrachain", + Name: "MANTRA Chain", + Kind: "cosmos", + Providers: []Provider{ + {Slug: "mantrachain-official", Name: "MANTRA", URL: envDefault("RPC_URL_MANTRA_OFFICIAL", "https://rpc.mantrachain.io")}, + {Slug: "itrocket", Name: "ITRocket", URL: envDefault("RPC_URL_MANTRA_ITROCKET", "https://mantra-mainnet-rpc.itrocket.net:443")}, + {Slug: "polkachu", Name: "Polkachu", URL: envDefault("RPC_URL_MANTRA_POLKACHU", "https://mantra-rpc.polkachu.com:443")}, + }, + }, + // 2026-08-29 wave-13. Band Protocol — Cosmos SDK (laozi-mainnet), Tendermint /status. Official + High Stakes + Stakewolle. + { + Slug: "bandchain", + Name: "Band Protocol", + Kind: "cosmos", + Providers: []Provider{ + {Slug: "band-official", Name: "Band Protocol", URL: envDefault("RPC_URL_BAND_OFFICIAL", "http://rpc.laozi1.bandchain.org:80")}, + {Slug: "highstakes", Name: "High Stakes", URL: envDefault("RPC_URL_BAND_HIGHSTAKES", "https://bandprotocol-rpc.highstakes.ch")}, + {Slug: "stakewolle", Name: "Stakewolle", URL: envDefault("RPC_URL_BAND_STAKEWOLLE", "https://public.stakewolle.com/cosmos/bandchain/rpc")}, + }, + }, + // 2026-08-29 wave-13. cheqd — Cosmos SDK (cheqd-mainnet-1), Tendermint /status. Official + PublicNode + NodeStake. + { + Slug: "cheqd", + Name: "cheqd", + Kind: "cosmos", + Providers: []Provider{ + {Slug: "cheqd-official", Name: "cheqd", URL: envDefault("RPC_URL_CHEQD_OFFICIAL", "https://rpc.cheqd.net")}, + {Slug: "publicnode", Name: "PublicNode", URL: envDefault("RPC_URL_CHEQD_PUBLICNODE", "https://cheqd-rpc.publicnode.com:443")}, + {Slug: "nodestake", Name: "NodeStake", URL: envDefault("RPC_URL_CHEQD_NODESTAKE", "https://rpc.cheqd.nodestake.org")}, + }, + }, + // 2026-08-29 wave-12. Union — Cosmos SDK (union-1), Tendermint /status. Nodes.Guru + Stake And Relax + High Stakes. + { + Slug: "union", + Name: "Union", + Kind: "cosmos", + Providers: []Provider{ + {Slug: "nodes-guru", Name: "Nodes.Guru", URL: envDefault("RPC_URL_UNION_NODESGURU", "https://rpc-1.union.nodes.guru")}, + {Slug: "stakeandrelax", Name: "Stake And Relax", URL: envDefault("RPC_URL_UNION_STAKEANDRELAX", "https://union-rpc.stakeandrelax.net")}, + {Slug: "highstakes", Name: "High Stakes", URL: envDefault("RPC_URL_UNION_HIGHSTAKES", "https://union-rpc.highstakes.ch")}, + }, + }, + // 2026-08-28 wave-11. Fetch.ai (FetchHub-4) — Cosmos SDK, Tendermint /status. Official + PublicNode + Cosmos Directory. + { + Slug: "fetchhub", + Name: "Fetch.ai", + Kind: "cosmos", + Providers: []Provider{ + {Slug: "fetchai-official", Name: "Fetch.ai", URL: envDefault("RPC_URL_FETCHHUB_OFFICIAL", "https://rpc-fetchhub.fetch.ai")}, + {Slug: "publicnode", Name: "PublicNode", URL: envDefault("RPC_URL_FETCHHUB_PUBLICNODE", "https://fetch-rpc.publicnode.com")}, + {Slug: "cosmos-directory", Name: "Cosmos Directory", URL: envDefault("RPC_URL_FETCHHUB_COSMOSDIRECTORY", "https://rpc.cosmos.directory/fetchhub")}, + }, + }, // 2026-08-27 wave-10. Zcash — getblockcount JSON-RPC (Tatum zcashd + Tatum Zebra) + Blockchair REST GET. ~75 s/block. { Slug: "zcash", diff --git a/public/logos/bandchain.svg b/public/logos/bandchain.svg new file mode 100644 index 00000000..ec9ec2f2 --- /dev/null +++ b/public/logos/bandchain.svg @@ -0,0 +1,4 @@ + + + BAND + diff --git a/public/logos/cheqd.svg b/public/logos/cheqd.svg new file mode 100644 index 00000000..10461cae --- /dev/null +++ b/public/logos/cheqd.svg @@ -0,0 +1,4 @@ + + + CHEQD + diff --git a/public/logos/nodestake.svg b/public/logos/nodestake.svg new file mode 100644 index 00000000..609a7590 --- /dev/null +++ b/public/logos/nodestake.svg @@ -0,0 +1,5 @@ + + + NODE + STAKE + diff --git a/public/logos/stakewolle.svg b/public/logos/stakewolle.svg new file mode 100644 index 00000000..08a9774d --- /dev/null +++ b/public/logos/stakewolle.svg @@ -0,0 +1,5 @@ + + + STAKE + WOLLE + diff --git a/src/data/provider-registry.ts b/src/data/provider-registry.ts index 169cffc4..89121719 100644 --- a/src/data/provider-registry.ts +++ b/src/data/provider-registry.ts @@ -2591,6 +2591,105 @@ export const PROVIDER_REGISTRY: Record = { "Acala official public RPC node 2 (acala-rpc-2.aca-api.network). Keyless Substrate JSON-RPC interface for the Acala DeFi parachain on Polkadot.", twitter: "@AcalaNetwork", }, + + // ─── Chihuahua providers (bench 250) ───────────────────────────────── + "chihuahua-official": { + url: "https://rpc.chihuahua.wtf", + description: + "Chihuahua official public Tendermint RPC node. Keyless endpoint for the Chihuahua Cosmos SDK chain.", + twitter: "@ChihuahuaChain", + }, + validatus: { + url: "https://github.com/validatus", + description: + "Validatus public Cosmos RPC infrastructure. Provides keyless Tendermint RPC endpoints for multiple Cosmos SDK chains.", + }, + + // ─── Cosmos Directory (shared across benches 251-252) ──────────────── + "cosmos-directory": { + url: "https://cosmos.directory", + description: + "Cosmos Directory public RPC proxy. Aggregates Tendermint RPC endpoints across the Cosmos ecosystem, no API key required.", + }, + + // ─── Fetch.ai providers (bench 252) ────────────────────────────────── + "fetchai-official": { + url: "https://rpc-fetchhub.fetch.ai", + description: + "Fetch.ai official public Tendermint RPC node for the FetchHub-4 mainnet. Keyless endpoint maintained by the Fetch.ai / ASI Alliance team.", + twitter: "@Fetch_ai", + }, + + // ─── Shentu providers (bench 254) ──────────────────────────────────── + "shentu-official": { + url: "https://www.shentu.technology", + description: + "Shentu Chain official public Tendermint RPC node for the shentu-2.2 mainnet. Keyless endpoint maintained by the CertiK / Shentu Foundation team.", + twitter: "@ShentuChain", + }, + + // ─── MANTRA Chain providers (bench 255) ────────────────────────────── + "mantrachain-official": { + url: "https://www.mantrachain.io", + description: + "MANTRA Chain official public Tendermint RPC node for the mantra-1 mainnet. Keyless endpoint for real-world asset tokenization on Cosmos.", + twitter: "@MANTRA_Chain", + }, + itrocket: { + url: "https://itrocket.net", + description: + "ITRocket community validator and public RPC operator. Provides keyless Tendermint RPC endpoints for multiple Cosmos SDK chains.", + twitter: "@ITRocketTeam", + }, + + + // ─── Band Protocol providers (bench 256) ──────────────────────────── + "band-official": { + url: "https://www.bandprotocol.com", + description: + "Band Protocol official public Tendermint RPC node for the laozi-mainnet. Keyless endpoint maintained by the Band Protocol team.", + twitter: "@BandProtocol", + }, + stakewolle: { + url: "https://stakewolle.com", + description: + "Stakewolle community validator and public RPC provider. Runs keyless Tendermint RPC endpoints for multiple Cosmos SDK chains.", + twitter: "@stakewolle", + }, + + // ─── cheqd providers (bench 257) ───────────────────────────────────── + "cheqd-official": { + url: "https://cheqd.io", + description: + "cheqd official public Tendermint RPC node for cheqd-mainnet-1. Keyless endpoint maintained by the cheqd Network team.", + twitter: "@cheqd_io", + }, + nodestake: { + url: "https://nodestake.org", + description: + "NodeStake community validator and public RPC operator. Provides keyless Tendermint RPC endpoints for multiple Cosmos SDK chains.", + twitter: "@NodeStake", + }, + + // ─── Union providers (bench 253) ───────────────────────────────────── + "nodes-guru": { + url: "https://nodes.guru", + description: + "Nodes.Guru community validator and public RPC operator. Runs keyless Tendermint RPC endpoints for multiple Cosmos SDK chains including Union.", + twitter: "@nodes_guru", + }, + stakeandrelax: { + url: "https://stakeandrelax.net", + description: + "Stake And Relax community validator providing public keyless Tendermint RPC for Cosmos SDK chains including Union.", + twitter: "@StakeAndRelax", + }, + highstakes: { + url: "https://highstakes.ch", + description: + "High Stakes Swiss validator and public RPC operator. Provides keyless Tendermint RPC endpoints for multiple Cosmos SDK chains including Union.", + twitter: "@HighStakesCH", + }, }; /** diff --git a/src/lib/brand.ts b/src/lib/brand.ts index 83d02a58..c1c41817 100644 --- a/src/lib/brand.ts +++ b/src/lib/brand.ts @@ -176,6 +176,14 @@ const BRANDS: Record = { // ─── Cosmos SDK chains (bench 247) ─── babylon: { color: "#F8811A" }, // babylon orange (official brand) + // ─── Band Protocol + cheqd (benches 256-257) ─── + bandchain: { color: "#516AFF" }, // band protocol indigo (official brand) + cheqd: { color: "#00B59C" }, // cheqd teal (official brand) + "band-official": { color: "#516AFF" }, // band official inherits band indigo + "cheqd-official": { color: "#00B59C" }, // cheqd official inherits cheqd teal + stakewolle: { color: "#F97316" }, // stakewolle orange + nodestake: { color: "#8B5CF6" }, // nodestake violet + // ─── Bitcoin Cash chain + providers (bench 244) ─── "bitcoin-cash": { color: "#0AC18E" }, // bch official green bitcore: { color: "#1A1D21", dark: true }, // bitpay dark diff --git a/src/lib/logo-manifest.ts b/src/lib/logo-manifest.ts index 0c20e4c4..e893db5a 100644 --- a/src/lib/logo-manifest.ts +++ b/src/lib/logo-manifest.ts @@ -328,6 +328,21 @@ const RAW: Record = { acala: "/logos/acala.svg", interlay: "/logos/interlay.svg", litecoinspace: "/logos/litecoinspace.svg", + chihuahua: "/logos/chihuahua.svg", + sentinel: "/logos/sentinel.svg", + fetchhub: "/logos/fetchai.svg", + union: "/logos/union.svg", + shentu: "/logos/shentu.svg", + mantrachain: "/logos/mantrachain.svg", + "nodes-guru": "/logos/nodes-guru.svg", + stakeandrelax: "/logos/stakeandrelax.svg", + highstakes: "/logos/highstakes.svg", + itrocket: "/logos/itrocket.svg", + "cosmos-directory": "/logos/cosmos-directory.svg", + bandchain: "/logos/bandchain.svg", + cheqd: "/logos/cheqd.svg", + stakewolle: "/logos/stakewolle.svg", + nodestake: "/logos/nodestake.svg", // ─── Oracle deviation (bench 025) — additional brand logos ─── // (pairs alias to chain/asset logos in the ALIASES block below) @@ -736,6 +751,14 @@ const ALIASES: Record = { "acala-rpc0": "acala", "acala-rpc2": "acala", + // Chihuahua + Fetch.ai official node aliases → chain logo + "chihuahua-official": "chihuahua", + "fetchai-official": "fetchhub", + "shentu-official": "shentu", + "mantrachain-official": "mantrachain", + "band-official": "bandchain", + "cheqd-official": "cheqd", + // Non-EVM wave-3 (benches 222-231) — provider-official aliases to chain slug "ecadinfra": "tezos", "tzbeta": "tezos",