From 1978cfc4cfc461837d61cfc071880a7c6f959ede Mon Sep 17 00:00:00 2001 From: Flotapponnier <160007691+Flotapponnier@users.noreply.github.com> Date: Sat, 29 Aug 2026 15:40:29 +0200 Subject: [PATCH 01/10] =?UTF-8?q?merge:=20dev=20=E2=86=92=20main=20(Gains?= =?UTF-8?q?=20carry=20fix=20+=20vault=20fee=20split)=20(#2191)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * fix(harnesses): cap LZ/CCIP/Axelar cardinality to bench-tracked chains only (#2187) * fix(cache): strip heavy fields from aggregate blob to pass 2MB limit (#2188) Hub pages only need card data. Drop seriesByRegion24h, metricPanels, editorial text (seoIntro/faq/findings/methodology/disclaimer/abstract/ perChainExplainer) from the blob aggregate slim. Cuts serialized size from ~4.3 MB to under 2 MB so unstable_cache actually persists the entry instead of silently dropping it on every render. * fix: include still-open Gains positions in carry projection (#2189) * fix(cache): strip heavy fields from aggregate blob to pass 2MB limit Hub pages only need card data. Drop seriesByRegion24h, metricPanels, editorial text (seoIntro/faq/findings/methodology/disclaimer/abstract/ perChainExplainer) from the blob aggregate slim. Cuts serialized size from ~4.3 MB to under 2 MB so unstable_cache actually persists the entry instead of silently dropping it on every render. * fix: include still-open Gains positions in carry projection * fix: update Gains action names v5→v6, handle position size increases (#2190) * fix(cache): strip heavy fields from aggregate blob to pass 2MB limit Hub pages only need card data. Drop seriesByRegion24h, metricPanels, editorial text (seoIntro/faq/findings/methodology/disclaimer/abstract/ perChainExplainer) from the blob aggregate slim. Cuts serialized size from ~4.3 MB to under 2 MB so unstable_cache actually persists the entry instead of silently dropping it on every render. * fix: include still-open Gains positions in carry projection * fix: update Gains open action names to v6 API, handle TradePosSizeIncrease --- .../axelar-gmp-latency/cmd/script/chains.go | 21 +++++++++++ .../axelar-gmp-latency/cmd/script/main.go | 6 ++-- .../cmd/script/chains.go | 26 ++++++++++++++ .../chainlink-ccip-latency/cmd/script/main.go | 4 +++ .../cmd/script/chains.go | 29 +++++++++++++++ .../cmd/script/main.go | 4 +++ src/app/api/fee-compare/route.ts | 15 ++++++-- src/lib/aggregate-blob.ts | 35 +++++++++++++++++-- 8 files changed, 133 insertions(+), 7 deletions(-) diff --git a/harnesses/axelar-gmp-latency/cmd/script/chains.go b/harnesses/axelar-gmp-latency/cmd/script/chains.go index 2c83c54f4..2b62f1769 100644 --- a/harnesses/axelar-gmp-latency/cmd/script/chains.go +++ b/harnesses/axelar-gmp-latency/cmd/script/chains.go @@ -14,6 +14,27 @@ import "strings" // - Cosmos chains (osmosis, injective, sei, celestia, kava) are // Axelar-exclusive coverage vs Wormhole/LayerZero/CCIP/Hyperlane // +// axelarTrackedChains is the set of OCB canonical slugs we record metrics for. +// Only messages where BOTH source AND destination are in this set are +// observed, capping cardinality at len²×buckets. +// Derived from the bench YAML provider slugs. +var axelarTrackedChains = map[string]bool{ + "ethereum": true, + "polygon": true, + "base": true, + "moonbeam": true, + "osmosis": true, + "arbitrum": true, + "avalanche": true, + "bnb": true, + "celo": true, + "injective": true, + "linea": true, + "mantle": true, + "optimism": true, + "scroll": true, +} + // Unknown names fall through to `chain-` so we never drop data. var axelarChainSlug = map[string]string{ // EVM L1s diff --git a/harnesses/axelar-gmp-latency/cmd/script/main.go b/harnesses/axelar-gmp-latency/cmd/script/main.go index a3652e996..55ff6b41a 100644 --- a/harnesses/axelar-gmp-latency/cmd/script/main.go +++ b/harnesses/axelar-gmp-latency/cmd/script/main.go @@ -173,8 +173,10 @@ func poll(ctx context.Context, client *http.Client, seen *lruSet) error { totalMs := float64(m.TimeSpent.Total) * 1000 if totalMs > 0 && totalMs <= maxLatencyMs { dst := canonicalizeAxelarChain(m.Call.ReturnValues.DestinationChain) - axelarE2ELatencyMs.WithLabelValues(src, dst).Observe(totalMs) - axelarSeenTotal.WithLabelValues(src, dst).Inc() + if axelarTrackedChains[src] && axelarTrackedChains[dst] { + axelarE2ELatencyMs.WithLabelValues(src, dst).Observe(totalMs) + axelarSeenTotal.WithLabelValues(src, dst).Inc() + } } seen.add(m.ID) diff --git a/harnesses/chainlink-ccip-latency/cmd/script/chains.go b/harnesses/chainlink-ccip-latency/cmd/script/chains.go index e551d3c46..bb3a17fed 100644 --- a/harnesses/chainlink-ccip-latency/cmd/script/chains.go +++ b/harnesses/chainlink-ccip-latency/cmd/script/chains.go @@ -12,6 +12,32 @@ package main // makes the mapping legible and the audit trail obvious when CCIP // adds a new chain we didn't anticipate. // +// ccipTrackedChains is the set of OCB canonical slugs we record metrics for. +// Only messages where BOTH source AND destination are in this set are +// observed, capping cardinality at len²×buckets. +// Derived from the bench YAML provider slugs. +var ccipTrackedChains = map[string]bool{ + "ethereum": true, + "bnb": true, + "polygon": true, + "avalanche": true, + "arbitrum": true, + "base": true, + "robinhood": true, + "berachain": true, + "celo": true, + "ink": true, + "linea": true, + "mantle": true, + "monad": true, + "moonbeam": true, + "optimism": true, + "scroll": true, + "solana": true, + "unichain": true, + "world-chain": true, +} + // Only mainnet entries are mapped; testnet rows are dropped in main.go // via the `environment != "mainnet"` guard so we never emit test-chain // latency. diff --git a/harnesses/chainlink-ccip-latency/cmd/script/main.go b/harnesses/chainlink-ccip-latency/cmd/script/main.go index 19b6e3c8d..c4a0cece9 100644 --- a/harnesses/chainlink-ccip-latency/cmd/script/main.go +++ b/harnesses/chainlink-ccip-latency/cmd/script/main.go @@ -189,6 +189,10 @@ func poll(ctx context.Context, client *http.Client, seen *lruSet) error { seen.add(m.MessageID) continue } + if !ccipTrackedChains[srcSlug] || !ccipTrackedChains[dstSlug] { + seen.add(m.MessageID) + continue + } ccipLatencyMs.WithLabelValues(srcSlug, dstSlug).Observe(deltaMs) ccipSeenTotal.WithLabelValues(srcSlug, dstSlug).Inc() seen.add(m.MessageID) diff --git a/harnesses/layerzero-message-latency/cmd/script/chains.go b/harnesses/layerzero-message-latency/cmd/script/chains.go index 2c8887410..ded442922 100644 --- a/harnesses/layerzero-message-latency/cmd/script/chains.go +++ b/harnesses/layerzero-message-latency/cmd/script/chains.go @@ -10,6 +10,35 @@ package main // - LayerZero exposes a bunch of exotic chains (orderly, flare, ape, // robinhood, hyperliquid) that map to our slugs where they exist. // +// lzTrackedChains is the set of OCB canonical slugs we record metrics for. +// Only messages where BOTH source AND destination are in this set are +// observed. This caps cardinality at len²×buckets instead of the full +// N×N cross-product of all chains LayerZero supports. +// Derived from the bench YAML provider slugs — add here when adding a +// new chain to the bench. +var lzTrackedChains = map[string]bool{ + "ethereum": true, + "solana": true, + "bnb": true, + "arbitrum": true, + "base": true, + "optimism": true, + "polygon": true, + "avalanche": true, + "robinhood": true, + "monad": true, + "berachain": true, + "celo": true, + "injective": true, + "ink": true, + "linea": true, + "mantle": true, + "moonbeam": true, + "scroll": true, + "sui": true, + "unichain": true, +} + // Unknown names fall through to a synthetic `chain-` slug in // main.go so we never drop data silently. var lzChainSlug = map[string]string{ diff --git a/harnesses/layerzero-message-latency/cmd/script/main.go b/harnesses/layerzero-message-latency/cmd/script/main.go index 5f7a4f6d9..3ccfc0d07 100644 --- a/harnesses/layerzero-message-latency/cmd/script/main.go +++ b/harnesses/layerzero-message-latency/cmd/script/main.go @@ -206,6 +206,10 @@ func poll(ctx context.Context, client *http.Client, seen *lruSet) error { continue } dstSlug := chainSlug(m.Pathway.Receiver.Chain) + if !lzTrackedChains[srcSlug] || !lzTrackedChains[dstSlug] { + seen.add(m.GUID) + continue + } lzLatencyMs.WithLabelValues(srcSlug, dstSlug).Observe(deltaMs) lzSeenTotal.WithLabelValues(srcSlug, dstSlug).Inc() seen.add(m.GUID) diff --git a/src/app/api/fee-compare/route.ts b/src/app/api/fee-compare/route.ts index cd724592e..b4f9e2e12 100644 --- a/src/app/api/fee-compare/route.ts +++ b/src/app/api/fee-compare/route.ts @@ -1110,7 +1110,10 @@ function augmentWithHlOpenPositions( } function reconstructGainsPositions(trades: GainsApiTrade[], cutoffMs: number): PositionSlice[] { - const OPEN_ACTIONS = new Set(["MarketOpened", "LimitOrderExecuted"]); + // v5 names: MarketOpened, LimitOrderExecuted — v6 names: TradeOpenedMarket, TradeOpenedLimit + const OPEN_ACTIONS = new Set(["MarketOpened", "LimitOrderExecuted", "TradeOpenedMarket", "TradeOpenedLimit"]); + // TradePosSizeIncrease updates the position size; use latest size as notional + const INCREASE_ACTIONS = new Set(["TradePosSizeIncrease"]); const CLOSE_ACTIONS = new Set(["TradeClosedMarket", "TradeClosedTP", "TradeClosedSL", "TradeClosedLIQ"]); const byId = new Map(); @@ -1118,19 +1121,25 @@ function reconstructGainsPositions(trades: GainsApiTrade[], cutoffMs: number): P if (!byId.has(t.id)) byId.set(t.id, {}); const e = byId.get(t.id)!; if (OPEN_ACTIONS.has(t.action)) e.open = t; + else if (INCREASE_ACTIONS.has(t.action) && e.open) { + e.open = { ...e.open, size: t.size, leverage: t.leverage }; + } else if (CLOSE_ACTIONS.has(t.action)) e.close = t; } + const now = Date.now(); const slices: PositionSlice[] = []; for (const { open, close } of byId.values()) { - if (!open || !close) continue; + if (!open) continue; const openMs = new Date(open.date).getTime(); if (openMs < cutoffMs) continue; + // Still-open positions use now as close time (same as reconstructHlPositions) + const closeMs = close ? new Date(close.date).getTime() : now; slices.push({ coin: open.pair.split("/")[0], notionalUsd: open.size * open.leverage, openMs, - closeMs: new Date(close.date).getTime(), + closeMs, isLong: open.buy !== false, }); } diff --git a/src/lib/aggregate-blob.ts b/src/lib/aggregate-blob.ts index 0c2ff32e5..f920fb1fd 100644 --- a/src/lib/aggregate-blob.ts +++ b/src/lib/aggregate-blob.ts @@ -26,6 +26,37 @@ import type { Benchmark } from "@/types/benchmark"; import { loadSpecsUncached } from "@/lib/materialize/load"; import { overlayEditorial, slimBenchmarkForCache } from "@/lib/spec"; +// Aggressive slim for the aggregate blob. Hub pages (homepage, categories, +// chains, products) only need card data — they never render editorial text +// or metric panels. Stripping these fields drops the serialized aggregate +// from ~4.3 MB to well under the 2 MB unstable_cache ceiling. +// +// Fields stripped beyond slimBenchmarkForCache (which already removes +// 7d/30d series): +// - extras.seriesByRegion24h (only used on bench detail pages) +// - metricPanels (only used on bench detail pages) +// - seoIntro, faq, disclaimer (editorial, bench detail only) +// - perChainExplainer (bench detail + worker's sitemap.json handles sitemap) +// - findings, methodology (bench detail only; required fields → []) +// - abstract (bench detail only; required field → "") +function slimForBlobAggregate(b: Benchmark): Benchmark { + const base = slimBenchmarkForCache(b); + // eslint-disable-next-line @typescript-eslint/no-unused-vars + const { seriesByRegion24h: _sbr, ...slimExtras } = base.extras; + return { + ...base, + extras: slimExtras, + metricPanels: undefined, + seoIntro: undefined, + faq: undefined, + perChainExplainer: undefined, + disclaimer: undefined, + findings: [], + methodology: [], + abstract: "", + }; +} + // On any Vercel deployment (production or preview), use the self-hosted // CDN proxy (/api/aggregate on openchainbench.com) so Vercel functions // pay ~1 ms (edge cache hit) instead of ~12 s fetching the 7.5 MB blob @@ -113,7 +144,7 @@ async function fetchAndProject(): Promise { for (const bench of raw.benches) { const spec = specBySlug.get(bench.slug); if (!spec) continue; // Bench in blob no longer has a spec — skip. - projected.push(slimBenchmarkForCache(overlayEditorial(bench, spec))); + projected.push(slimForBlobAggregate(overlayEditorial(bench, spec))); } return projected.sort((a, b) => (a.number ?? "").localeCompare(b.number ?? ""), @@ -130,6 +161,6 @@ async function fetchAndProject(): Promise { */ export const loadAggregateFromBlob = unstable_cache( fetchAndProject, - ["aggregate-blob-v2"], + ["aggregate-blob-v3"], { revalidate: 60, tags: ["bench-aggregate", "benchmarks"] }, ); From cf6906b552c7cefe76d577ced80039d109886bbb Mon Sep 17 00:00:00 2001 From: Flotapponnier <160007691+Flotapponnier@users.noreply.github.com> Date: Sat, 29 Aug 2026 18:02:33 +0200 Subject: [PATCH 02/10] feat(rpc): Union bench #253 (#2195) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * fix(harnesses): cap LZ/CCIP/Axelar cardinality to bench-tracked chains only (#2187) * fix(cache): strip heavy fields from aggregate blob to pass 2MB limit (#2188) Hub pages only need card data. Drop seriesByRegion24h, metricPanels, editorial text (seoIntro/faq/findings/methodology/disclaimer/abstract/ perChainExplainer) from the blob aggregate slim. Cuts serialized size from ~4.3 MB to under 2 MB so unstable_cache actually persists the entry instead of silently dropping it on every render. * fix: include still-open Gains positions in carry projection (#2189) * fix(cache): strip heavy fields from aggregate blob to pass 2MB limit Hub pages only need card data. Drop seriesByRegion24h, metricPanels, editorial text (seoIntro/faq/findings/methodology/disclaimer/abstract/ perChainExplainer) from the blob aggregate slim. Cuts serialized size from ~4.3 MB to under 2 MB so unstable_cache actually persists the entry instead of silently dropping it on every render. * fix: include still-open Gains positions in carry projection * fix: update Gains action names v5→v6, handle position size increases (#2190) * fix(cache): strip heavy fields from aggregate blob to pass 2MB limit Hub pages only need card data. Drop seriesByRegion24h, metricPanels, editorial text (seoIntro/faq/findings/methodology/disclaimer/abstract/ perChainExplainer) from the blob aggregate slim. Cuts serialized size from ~4.3 MB to under 2 MB so unstable_cache actually persists the entry instead of silently dropping it on every render. * fix: include still-open Gains positions in carry projection * fix: update Gains open action names to v6 API, handle TradePosSizeIncrease * fix: Gains carry projection (v6 action names + still-open positions) * fix(cache): strip heavy fields from aggregate blob to pass 2MB limit Hub pages only need card data. Drop seriesByRegion24h, metricPanels, editorial text (seoIntro/faq/findings/methodology/disclaimer/abstract/ perChainExplainer) from the blob aggregate slim. Cuts serialized size from ~4.3 MB to under 2 MB so unstable_cache actually persists the entry instead of silently dropping it on every render. * fix: include still-open Gains positions in carry projection * fix: update Gains open action names to v6 API, handle TradePosSizeIncrease * fix: widen coin name column for long tickers (xyz:NVDA) * fix(cache): strip heavy fields from aggregate blob to pass 2MB limit Hub pages only need card data. Drop seriesByRegion24h, metricPanels, editorial text (seoIntro/faq/findings/methodology/disclaimer/abstract/ perChainExplainer) from the blob aggregate slim. Cuts serialized size from ~4.3 MB to under 2 MB so unstable_cache actually persists the entry instead of silently dropping it on every render. * fix: include still-open Gains positions in carry projection * fix: update Gains open action names to v6 API, handle TradePosSizeIncrease * fix: widen coin name column in top markets to fit xyz:NVDA * feat(rpc): add Union bench #253 (Nodes.Guru, Stake And Relax, High Stakes) (#2194) --- benchmarks/union-rpc.yml | 138 ++++++++++++++++++ .../rpc-capabilities/cmd/script/config.go | 11 ++ public/logos/union.svg | 4 + src/components/fee-compare-client.tsx | 2 +- src/data/provider-registry.ts | 20 +++ src/lib/brand.ts | 3 +- src/lib/logo-manifest.ts | 1 + 7 files changed, 177 insertions(+), 2 deletions(-) create mode 100644 benchmarks/union-rpc.yml create mode 100644 public/logos/union.svg diff --git a/benchmarks/union-rpc.yml b/benchmarks/union-rpc.yml new file mode 100644 index 000000000..0373e4819 --- /dev/null +++ b/benchmarks/union-rpc.yml @@ -0,0 +1,138 @@ +# OpenChainBench. Bench No 253 + +slug: union-rpc +number: "253" +title: Fastest free Union RPC, live no-key endpoint latency +seo_title: "Fastest free Union RPC 2026" +seo_description: "{{best_name}} leads free Union RPC at {{best_p50}} (block height p50, 24h). 3 providers measured every 60s from 3 regions." +subtitle: HTTP round-trip latency for Tendermint /status queries against every available public Union (union-1) endpoint, audited every 60 seconds from 3 regions. + +category: RPCs +status: live +metric: RPC latency +unit: ms +higher_is_better: false + +seo_intro: | + Union is a trust-minimised, zero-knowledge cross-chain protocol built on a custom Cosmos SDK chain (chain ID union-1). It enables secure interoperability between Cosmos, EVM, and other ecosystems via ZK proof-based consensus verification — no external validators or multisigs required. The chain uses the standard Tendermint RPC interface; block height is fetched via the /status endpoint. Public keyless RPC nodes are provided by Nodes.Guru, Stake And Relax, and High Stakes. Every provider was live-verified with consecutive block-height probes at launch. + +abstract: | + Per-chain member of the RPC latency cluster, extended to Union (union-1). + We measure the round-trip latency of a Tendermint /status query against + every available public Union endpoint that sustains continuous probing: + 3 providers at launch, every 60 seconds, from us-east, eu-west and + Singapore. Each provider is probed via a GET /status request + from which the block number is extracted. + The harness classifies every response (ok / http_err / jsonrpc_err / stale / timeout) + with a Cosmos-scaled staleness gap (20 blocks, around 80 s at 4 s/block). + The cross-chain view lives on the parent rpc-capabilities benchmark; + this page is the Union-scoped answer with per-region breakdowns as + a first-class dimension. + +methodology: + - "Cadence: every 60 seconds per provider, from each of 3 probe regions (us-east Virginia, eu-west Amsterdam, sgp Singapore). Headline p50/p90/p99 aggregate across all 3 regions via Prometheus avg(quantile_over_time(...)); per-region breakdowns are first-class on this page via the region tabs." + - "Payload: GET /status. The result.sync_info.latest_block_height field (string-encoded integer) is extracted as the current block height." + - "Latency: client-side round-trip delta in milliseconds, exposed as both a gauge and a histogram (buckets 50 ms to 10 s), so percentiles are computed via Prometheus quantile_over_time over the last 24 hours." + - "Call-result classification: ok (parsable block height returned), http_err, jsonrpc_err, stale (block more than 20 behind the cross-provider tip), timeout." + - "This page is part of the per-chain RPC cluster derived from the cross-chain rpc-capabilities benchmark; the identical harness, cadence and exclusion rules apply on every chain." + - "Chain scope: every query on this page is pinned to chain=union. Provider coverage at launch: 3 endpoints (Nodes.Guru, Stake And Relax, High Stakes)." + +findings: + - "{{best_name}} leads free Union RPC at {{best_p50}} (Tendermint /status p50, 24h) across 3 measured providers." + +faq: + - q: "What is the fastest free Union RPC right now?" + a: "{{best_name}} currently leads at {{best_p50}} (Union block height p50 over the last 24h), measured against 3 providers probed every 60 seconds from us-east, eu-west and Singapore. The leaderboard re-sorts continuously against fresh Prometheus samples." + - q: "Which Union RPC endpoints work without an API key?" + a: "3 endpoints sustain continuous keyless probing at launch: Nodes.Guru (rpc-1.union.nodes.guru), Stake And Relax (union-rpc.stakeandrelax.net), and High Stakes (union-rpc.highstakes.ch). Every listed endpoint was live-verified before inclusion." + - q: "What is Union and why does its RPC latency matter?" + a: "Union is a ZK-based cross-chain interoperability protocol with a native Cosmos SDK chain (union-1). It connects blockchains without trusted intermediaries by verifying consensus proofs on-chain. Developers building cross-chain applications, bridges, or omnichain protocols on Union need reliable low-latency RPC access to query transactions, proofs, and chain state." + - q: "Does the fastest Union RPC change by region?" + a: "Often. Community validators like Nodes.Guru and High Stakes host in different datacentres. The region tabs re-scope every number to a single probe origin so you can pick the best endpoint for your user base." + - q: "What makes Union different from other cross-chain protocols?" + a: "Union replaces trusted validator sets and multisigs with ZK consensus proofs, making cross-chain message passing verifiable on-chain. This means no external trust assumptions — the security of the bridge reduces to the security of the underlying chains and ZK proof system." + +source: https://github.com/ChainBench/OpenChainBench/tree/main/harnesses/rpc-capabilities + +prometheus: + window: 24h + freshness_metric: rpc_latency_milliseconds + +rank_matrix_query: avg by (provider, region) (ocb:rpc_latency_milliseconds:p50_24h{chain="union"}) + +dimensions: + region: + - { value: all, label: All regions } + - { value: us-east, label: US-East } + - { value: eu-west, label: EU-West } + - { value: sgp, label: Singapore } + +providers: + - slug: nodes-guru + name: Nodes.Guru + tag: Nodes.Guru public Union RPC, no API key required + formula: "50th percentile over 24h of client-side round-trip latency (ms) for a GET /status sent every 60s from 3 regions to rpc-1.union.nodes.guru." + queries: + p50: avg(ocb:rpc_latency_milliseconds:p50_24h{provider="nodes-guru", chain="union"}) + p90: avg(ocb:rpc_latency_milliseconds:p90_24h{provider="nodes-guru", chain="union"}) + p99: avg(ocb:rpc_latency_milliseconds:p99_24h{provider="nodes-guru", chain="union"}) + mean: avg(ocb:rpc_latency_milliseconds:mean_24h{provider="nodes-guru", chain="union"}) + success: sum(ocb:rpc_call:ok_rate_24h{provider="nodes-guru", chain="union"}) / sum(ocb:rpc_call:rate_24h{provider="nodes-guru", chain="union"}) + sample_size: sum(ocb:rpc_call:increase_24h{provider="nodes-guru", chain="union"}) + series: avg(avg_over_time(rpc_latency_milliseconds{provider="nodes-guru", chain="union"}[1h])) + regions: + - region: us-east + p50: avg(ocb:rpc_latency_milliseconds:p50_24h{provider="nodes-guru", chain="union", region="us-east"}) + series: avg_over_time(rpc_latency_milliseconds{provider="nodes-guru", chain="union", region="us-east"}[1h]) + - region: eu-west + p50: avg(ocb:rpc_latency_milliseconds:p50_24h{provider="nodes-guru", chain="union", region="eu-west"}) + series: avg_over_time(rpc_latency_milliseconds{provider="nodes-guru", chain="union", region="eu-west"}[1h]) + - region: ap-southeast + p50: avg(ocb:rpc_latency_milliseconds:p50_24h{provider="nodes-guru", chain="union", region="sgp"}) + series: avg_over_time(rpc_latency_milliseconds{provider="nodes-guru", chain="union", region="sgp"}[1h]) + + - slug: stakeandrelax + name: Stake And Relax + tag: Stake And Relax public Union RPC, no API key required + formula: "50th percentile over 24h of client-side round-trip latency (ms) for a GET /status sent every 60s from 3 regions to union-rpc.stakeandrelax.net." + queries: + p50: avg(ocb:rpc_latency_milliseconds:p50_24h{provider="stakeandrelax", chain="union"}) + p90: avg(ocb:rpc_latency_milliseconds:p90_24h{provider="stakeandrelax", chain="union"}) + p99: avg(ocb:rpc_latency_milliseconds:p99_24h{provider="stakeandrelax", chain="union"}) + mean: avg(ocb:rpc_latency_milliseconds:mean_24h{provider="stakeandrelax", chain="union"}) + success: sum(ocb:rpc_call:ok_rate_24h{provider="stakeandrelax", chain="union"}) / sum(ocb:rpc_call:rate_24h{provider="stakeandrelax", chain="union"}) + sample_size: sum(ocb:rpc_call:increase_24h{provider="stakeandrelax", chain="union"}) + series: avg(avg_over_time(rpc_latency_milliseconds{provider="stakeandrelax", chain="union"}[1h])) + regions: + - region: us-east + p50: avg(ocb:rpc_latency_milliseconds:p50_24h{provider="stakeandrelax", chain="union", region="us-east"}) + series: avg_over_time(rpc_latency_milliseconds{provider="stakeandrelax", chain="union", region="us-east"}[1h]) + - region: eu-west + p50: avg(ocb:rpc_latency_milliseconds:p50_24h{provider="stakeandrelax", chain="union", region="eu-west"}) + series: avg_over_time(rpc_latency_milliseconds{provider="stakeandrelax", chain="union", region="eu-west"}[1h]) + - region: ap-southeast + p50: avg(ocb:rpc_latency_milliseconds:p50_24h{provider="stakeandrelax", chain="union", region="sgp"}) + series: avg_over_time(rpc_latency_milliseconds{provider="stakeandrelax", chain="union", region="sgp"}[1h]) + + - slug: highstakes + name: High Stakes + tag: High Stakes public Union RPC, no API key required + formula: "50th percentile over 24h of client-side round-trip latency (ms) for a GET /status sent every 60s from 3 regions to union-rpc.highstakes.ch." + queries: + p50: avg(ocb:rpc_latency_milliseconds:p50_24h{provider="highstakes", chain="union"}) + p90: avg(ocb:rpc_latency_milliseconds:p90_24h{provider="highstakes", chain="union"}) + p99: avg(ocb:rpc_latency_milliseconds:p99_24h{provider="highstakes", chain="union"}) + mean: avg(ocb:rpc_latency_milliseconds:mean_24h{provider="highstakes", chain="union"}) + success: sum(ocb:rpc_call:ok_rate_24h{provider="highstakes", chain="union"}) / sum(ocb:rpc_call:rate_24h{provider="highstakes", chain="union"}) + sample_size: sum(ocb:rpc_call:increase_24h{provider="highstakes", chain="union"}) + series: avg(avg_over_time(rpc_latency_milliseconds{provider="highstakes", chain="union"}[1h])) + regions: + - region: us-east + p50: avg(ocb:rpc_latency_milliseconds:p50_24h{provider="highstakes", chain="union", region="us-east"}) + series: avg_over_time(rpc_latency_milliseconds{provider="highstakes", chain="union", region="us-east"}[1h]) + - region: eu-west + p50: avg(ocb:rpc_latency_milliseconds:p50_24h{provider="highstakes", chain="union", region="eu-west"}) + series: avg_over_time(rpc_latency_milliseconds{provider="highstakes", chain="union", region="eu-west"}[1h]) + - region: ap-southeast + p50: avg(ocb:rpc_latency_milliseconds:p50_24h{provider="highstakes", chain="union", region="sgp"}) + series: avg_over_time(rpc_latency_milliseconds{provider="highstakes", chain="union", region="sgp"}[1h]) diff --git a/harnesses/rpc-capabilities/cmd/script/config.go b/harnesses/rpc-capabilities/cmd/script/config.go index c985e4479..186460674 100644 --- a/harnesses/rpc-capabilities/cmd/script/config.go +++ b/harnesses/rpc-capabilities/cmd/script/config.go @@ -1693,6 +1693,17 @@ func chains() []Chain { {Slug: "cosmos-directory", Name: "Cosmos Directory", URL: envDefault("RPC_URL_SENTINEL_COSMOSDIRECTORY", "https://rpc.cosmos.directory/sentinel")}, }, }, + // 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", diff --git a/public/logos/union.svg b/public/logos/union.svg new file mode 100644 index 000000000..a1957ae25 --- /dev/null +++ b/public/logos/union.svg @@ -0,0 +1,4 @@ + + + UNO + diff --git a/src/components/fee-compare-client.tsx b/src/components/fee-compare-client.tsx index 6e931a1c0..a7ada7a71 100644 --- a/src/components/fee-compare-client.tsx +++ b/src/components/fee-compare-client.tsx @@ -956,7 +956,7 @@ function HlTopCoinsCard({ key={c.coin} className="flex items-center gap-3 px-5 py-3 hover:bg-ink/2 transition-colors" > - + {c.coin} {c.fills} fills diff --git a/src/data/provider-registry.ts b/src/data/provider-registry.ts index a3e40769c..9046f4ba6 100644 --- a/src/data/provider-registry.ts +++ b/src/data/provider-registry.ts @@ -2619,6 +2619,26 @@ export const PROVIDER_REGISTRY: Record = { "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", }, + + // ─── 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 8fb6c311a..453dc405d 100644 --- a/src/lib/brand.ts +++ b/src/lib/brand.ts @@ -173,11 +173,12 @@ const BRANDS: Record = { acala: { color: "#E40C5B" }, // acala red/pink (official brand) interlay: { color: "#1A3BDB" }, // interlay blue (official brand) - // ─── Cosmos SDK chains (benches 247, 250-252) ─── + // ─── Cosmos SDK chains (benches 247, 250-253) ─── babylon: { color: "#F8811A" }, // babylon orange (official brand) chihuahua: { color: "#E05F2A" }, // chihuahua orange-red (official brand) sentinel: { color: "#00C2FF" }, // sentinel cyan (official brand) fetchhub: { color: "#3B2D8E" }, // fetch.ai deep purple (official brand) + union: { color: "#6366F1" }, // union indigo (brand kit) "cosmos-directory": { color: "#1B1B2F", dark: true }, // cosmos directory dark navy // ─── Bitcoin Cash chain + providers (bench 244) ─── diff --git a/src/lib/logo-manifest.ts b/src/lib/logo-manifest.ts index 3fca81c47..d221da4df 100644 --- a/src/lib/logo-manifest.ts +++ b/src/lib/logo-manifest.ts @@ -331,6 +331,7 @@ const RAW: Record = { chihuahua: "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/logos/chihuahua.svg", sentinel: "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/logos/sentinel.svg", fetchhub: "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/logos/fetchai.svg", + union: "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/logos/union.svg", "cosmos-directory": "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/logos/cosmos-directory.svg", // ─── Oracle deviation (bench 025) — additional brand logos ─── From 4a47227a2f0f2c3c6c696a8aea2b8649ae7ad034 Mon Sep 17 00:00:00 2001 From: Florent Tapponnier <160007691+Flotapponnier@users.noreply.github.com> Date: Sat, 29 Aug 2026 18:13:26 +0200 Subject: [PATCH 03/10] fix(union-rpc): remove em dashes from YAML --- benchmarks/union-rpc.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/benchmarks/union-rpc.yml b/benchmarks/union-rpc.yml index 0373e4819..c075123a4 100644 --- a/benchmarks/union-rpc.yml +++ b/benchmarks/union-rpc.yml @@ -14,7 +14,7 @@ unit: ms higher_is_better: false seo_intro: | - Union is a trust-minimised, zero-knowledge cross-chain protocol built on a custom Cosmos SDK chain (chain ID union-1). It enables secure interoperability between Cosmos, EVM, and other ecosystems via ZK proof-based consensus verification — no external validators or multisigs required. The chain uses the standard Tendermint RPC interface; block height is fetched via the /status endpoint. Public keyless RPC nodes are provided by Nodes.Guru, Stake And Relax, and High Stakes. Every provider was live-verified with consecutive block-height probes at launch. + Union is a trust-minimised, zero-knowledge cross-chain protocol built on a custom Cosmos SDK chain (chain ID union-1). It enables secure interoperability between Cosmos, EVM, and other ecosystems via ZK proof-based consensus verification, with no external validators or multisigs required. The chain uses the standard Tendermint RPC interface; block height is fetched via the /status endpoint. Public keyless RPC nodes are provided by Nodes.Guru, Stake And Relax, and High Stakes. Every provider was live-verified with consecutive block-height probes at launch. abstract: | Per-chain member of the RPC latency cluster, extended to Union (union-1). @@ -50,7 +50,7 @@ faq: - q: "Does the fastest Union RPC change by region?" a: "Often. Community validators like Nodes.Guru and High Stakes host in different datacentres. The region tabs re-scope every number to a single probe origin so you can pick the best endpoint for your user base." - q: "What makes Union different from other cross-chain protocols?" - a: "Union replaces trusted validator sets and multisigs with ZK consensus proofs, making cross-chain message passing verifiable on-chain. This means no external trust assumptions — the security of the bridge reduces to the security of the underlying chains and ZK proof system." + a: "Union replaces trusted validator sets and multisigs with ZK consensus proofs, making cross-chain message passing verifiable on-chain. This means no external trust assumptions: the security of the bridge reduces to the security of the underlying chains and ZK proof system." source: https://github.com/ChainBench/OpenChainBench/tree/main/harnesses/rpc-capabilities From 407e002c2526afe7f5cb5b1ebbfa6069b83fc144 Mon Sep 17 00:00:00 2001 From: Florent Tapponnier <160007691+Flotapponnier@users.noreply.github.com> Date: Sat, 29 Aug 2026 18:36:33 +0200 Subject: [PATCH 04/10] feat(union): add provider SVG logos and brand colors --- public/logos/highstakes.svg | 5 +++++ public/logos/nodes-guru.svg | 5 +++++ public/logos/stakeandrelax.svg | 5 +++++ src/lib/brand.ts | 5 ++++- src/lib/logo-manifest.ts | 3 +++ 5 files changed, 22 insertions(+), 1 deletion(-) create mode 100644 public/logos/highstakes.svg create mode 100644 public/logos/nodes-guru.svg create mode 100644 public/logos/stakeandrelax.svg diff --git a/public/logos/highstakes.svg b/public/logos/highstakes.svg new file mode 100644 index 000000000..220f0fad4 --- /dev/null +++ b/public/logos/highstakes.svg @@ -0,0 +1,5 @@ + + + HIGH + STAKES + diff --git a/public/logos/nodes-guru.svg b/public/logos/nodes-guru.svg new file mode 100644 index 000000000..c55d6fd0c --- /dev/null +++ b/public/logos/nodes-guru.svg @@ -0,0 +1,5 @@ + + + NODES + GURU + diff --git a/public/logos/stakeandrelax.svg b/public/logos/stakeandrelax.svg new file mode 100644 index 000000000..3664a52dd --- /dev/null +++ b/public/logos/stakeandrelax.svg @@ -0,0 +1,5 @@ + + + STAKE + RELAX + diff --git a/src/lib/brand.ts b/src/lib/brand.ts index 453dc405d..bb62f3166 100644 --- a/src/lib/brand.ts +++ b/src/lib/brand.ts @@ -178,7 +178,10 @@ const BRANDS: Record = { chihuahua: { color: "#E05F2A" }, // chihuahua orange-red (official brand) sentinel: { color: "#00C2FF" }, // sentinel cyan (official brand) fetchhub: { color: "#3B2D8E" }, // fetch.ai deep purple (official brand) - union: { color: "#6366F1" }, // union indigo (brand kit) + union: { color: "#6366F1" }, // union indigo (brand kit) + "nodes-guru": { color: "#F59E0B" }, // nodes.guru amber + stakeandrelax: { color: "#10B981" }, // stake and relax emerald + highstakes: { color: "#3B82F6" }, // high stakes blue "cosmos-directory": { color: "#1B1B2F", dark: true }, // cosmos directory dark navy // ─── Bitcoin Cash chain + providers (bench 244) ─── diff --git a/src/lib/logo-manifest.ts b/src/lib/logo-manifest.ts index d221da4df..6aa6d2b7a 100644 --- a/src/lib/logo-manifest.ts +++ b/src/lib/logo-manifest.ts @@ -332,6 +332,9 @@ const RAW: Record = { sentinel: "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/logos/sentinel.svg", fetchhub: "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/logos/fetchai.svg", union: "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/logos/union.svg", + "nodes-guru": "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/logos/nodes-guru.svg", + stakeandrelax: "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/logos/stakeandrelax.svg", + highstakes: "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/logos/highstakes.svg", "cosmos-directory": "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/logos/cosmos-directory.svg", // ─── Oracle deviation (bench 025) — additional brand logos ─── From bcd05470ec3d1009a27b4f5dab50e807bec3a3ac Mon Sep 17 00:00:00 2001 From: Flotapponnier <160007691+Flotapponnier@users.noreply.github.com> Date: Sat, 29 Aug 2026 19:19:43 +0200 Subject: [PATCH 05/10] feat: Shentu #254 + MANTRA Chain #255 benches MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * fix(harnesses): cap LZ/CCIP/Axelar cardinality to bench-tracked chains only (#2187) * fix(cache): strip heavy fields from aggregate blob to pass 2MB limit (#2188) Hub pages only need card data. Drop seriesByRegion24h, metricPanels, editorial text (seoIntro/faq/findings/methodology/disclaimer/abstract/ perChainExplainer) from the blob aggregate slim. Cuts serialized size from ~4.3 MB to under 2 MB so unstable_cache actually persists the entry instead of silently dropping it on every render. * fix: include still-open Gains positions in carry projection (#2189) * fix(cache): strip heavy fields from aggregate blob to pass 2MB limit Hub pages only need card data. Drop seriesByRegion24h, metricPanels, editorial text (seoIntro/faq/findings/methodology/disclaimer/abstract/ perChainExplainer) from the blob aggregate slim. Cuts serialized size from ~4.3 MB to under 2 MB so unstable_cache actually persists the entry instead of silently dropping it on every render. * fix: include still-open Gains positions in carry projection * fix: update Gains action names v5→v6, handle position size increases (#2190) * fix(cache): strip heavy fields from aggregate blob to pass 2MB limit Hub pages only need card data. Drop seriesByRegion24h, metricPanels, editorial text (seoIntro/faq/findings/methodology/disclaimer/abstract/ perChainExplainer) from the blob aggregate slim. Cuts serialized size from ~4.3 MB to under 2 MB so unstable_cache actually persists the entry instead of silently dropping it on every render. * fix: include still-open Gains positions in carry projection * fix: update Gains open action names to v6 API, handle TradePosSizeIncrease * fix: Gains carry projection (v6 action names + still-open positions) * fix(cache): strip heavy fields from aggregate blob to pass 2MB limit Hub pages only need card data. Drop seriesByRegion24h, metricPanels, editorial text (seoIntro/faq/findings/methodology/disclaimer/abstract/ perChainExplainer) from the blob aggregate slim. Cuts serialized size from ~4.3 MB to under 2 MB so unstable_cache actually persists the entry instead of silently dropping it on every render. * fix: include still-open Gains positions in carry projection * fix: update Gains open action names to v6 API, handle TradePosSizeIncrease * fix: widen coin name column for long tickers (xyz:NVDA) * fix(cache): strip heavy fields from aggregate blob to pass 2MB limit Hub pages only need card data. Drop seriesByRegion24h, metricPanels, editorial text (seoIntro/faq/findings/methodology/disclaimer/abstract/ perChainExplainer) from the blob aggregate slim. Cuts serialized size from ~4.3 MB to under 2 MB so unstable_cache actually persists the entry instead of silently dropping it on every render. * fix: include still-open Gains positions in carry projection * fix: update Gains open action names to v6 API, handle TradePosSizeIncrease * fix: widen coin name column in top markets to fit xyz:NVDA * feat(rpc): add Union bench #253 (Nodes.Guru, Stake And Relax, High Stakes) (#2194) * fix(union-rpc): remove em dashes from YAML (#2196) * feat(rpc): Shentu #254 + MANTRA Chain #255 (#2199) * feat(rpc): add Shentu bench #254 and MANTRA Chain bench #255 * fix: remove accidental dev-portal submodule --- benchmarks/mantrachain-rpc.yml | 138 ++++++++++++++++++ benchmarks/shentu-rpc.yml | 138 ++++++++++++++++++ .../rpc-capabilities/cmd/script/config.go | 22 +++ public/logos/itrocket.svg | 5 + public/logos/mantrachain.svg | 4 + public/logos/shentu-official.svg | 4 + public/logos/shentu.svg | 4 + src/data/provider-registry.ts | 23 +++ src/lib/brand.ts | 15 +- src/lib/logo-manifest.ts | 5 + 10 files changed, 352 insertions(+), 6 deletions(-) create mode 100644 benchmarks/mantrachain-rpc.yml create mode 100644 benchmarks/shentu-rpc.yml create mode 100644 public/logos/itrocket.svg create mode 100644 public/logos/mantrachain.svg create mode 100644 public/logos/shentu-official.svg create mode 100644 public/logos/shentu.svg diff --git a/benchmarks/mantrachain-rpc.yml b/benchmarks/mantrachain-rpc.yml new file mode 100644 index 000000000..e68ac84f9 --- /dev/null +++ b/benchmarks/mantrachain-rpc.yml @@ -0,0 +1,138 @@ +# OpenChainBench. Bench No 255 + +slug: mantrachain-rpc +number: "255" +title: Fastest free MANTRA RPC, live no-key endpoint latency +seo_title: "Fastest free MANTRA Chain RPC 2026" +seo_description: "{{best_name}} leads free MANTRA RPC at {{best_p50}} (block height p50, 24h). 3 providers measured every 60s from 3 regions." +subtitle: HTTP round-trip latency for Tendermint /status queries against every available public MANTRA Chain (mantra-1) endpoint, audited every 60 seconds from 3 regions. + +category: RPCs +status: live +metric: RPC latency +unit: ms +higher_is_better: false + +seo_intro: | + MANTRA Chain is a Cosmos SDK blockchain (chain ID mantra-1) purpose-built for real-world asset (RWA) tokenization. It is a permissioned, regulatory-compliant Layer 1 focused on bringing tokenized financial assets on-chain, including real estate, bonds, and commodities. The chain uses the standard Tendermint RPC interface; block height is fetched via the /status endpoint. Public keyless RPC nodes are provided by the MANTRA official team, ITRocket, and Polkachu. Every provider was live-verified with consecutive block-height probes at launch. + +abstract: | + Per-chain member of the RPC latency cluster, extended to MANTRA Chain (mantra-1). + We measure the round-trip latency of a Tendermint /status query against + every available public MANTRA endpoint that sustains continuous probing: + 3 providers at launch, every 60 seconds, from us-east, eu-west and + Singapore. Each provider is probed via a GET /status request + from which the block number is extracted. + The harness classifies every response (ok / http_err / jsonrpc_err / stale / timeout) + with a Cosmos-scaled staleness gap (20 blocks, around 120 s at 6 s/block). + The cross-chain view lives on the parent rpc-capabilities benchmark; + this page is the MANTRA-scoped answer with per-region breakdowns as + a first-class dimension. + +methodology: + - "Cadence: every 60 seconds per provider, from each of 3 probe regions (us-east Virginia, eu-west Amsterdam, sgp Singapore). Headline p50/p90/p99 aggregate across all 3 regions via Prometheus avg(quantile_over_time(...)); per-region breakdowns are first-class on this page via the region tabs." + - "Payload: GET /status. The result.sync_info.latest_block_height field (string-encoded integer) is extracted as the current block height." + - "Latency: client-side round-trip delta in milliseconds, exposed as both a gauge and a histogram (buckets 50 ms to 10 s), so percentiles are computed via Prometheus quantile_over_time over the last 24 hours." + - "Call-result classification: ok (parsable block height returned), http_err, jsonrpc_err, stale (block more than 20 behind the cross-provider tip), timeout." + - "This page is part of the per-chain RPC cluster derived from the cross-chain rpc-capabilities benchmark; the identical harness, cadence and exclusion rules apply on every chain." + - "Chain scope: every query on this page is pinned to chain=mantrachain. Provider coverage at launch: 3 endpoints (MANTRA official, ITRocket, Polkachu)." + +findings: + - "{{best_name}} leads free MANTRA Chain RPC at {{best_p50}} (Tendermint /status p50, 24h) across 3 measured providers." + +faq: + - q: "What is the fastest free MANTRA Chain RPC right now?" + a: "{{best_name}} currently leads at {{best_p50}} (MANTRA block height p50 over the last 24h), measured against 3 providers probed every 60 seconds from us-east, eu-west and Singapore. The leaderboard re-sorts continuously against fresh Prometheus samples." + - q: "Which MANTRA Chain RPC endpoints work without an API key?" + a: "3 endpoints sustain continuous keyless probing at launch: MANTRA official (rpc.mantrachain.io), ITRocket (mantra-mainnet-rpc.itrocket.net), and Polkachu (mantra-rpc.polkachu.com). Every listed endpoint was live-verified before inclusion." + - q: "What is MANTRA Chain and why does its RPC latency matter?" + a: "MANTRA Chain is a Cosmos SDK Layer 1 built for real-world asset tokenization under regulatory frameworks. It enables compliant issuance and trading of tokenized financial assets such as real estate, bonds, and commodities. Developers building RWA applications, compliance tooling, or DeFi protocols on MANTRA need reliable low-latency RPC access to query asset state, transactions, and governance." + - q: "Does the fastest MANTRA RPC change by region?" + a: "Often. The official MANTRA node and community validators are hosted across different regions. The region tabs re-scope every number to a single probe origin so you can pick the best endpoint for your user base." + - q: "What is the OM token on MANTRA Chain?" + a: "OM is the native staking and governance token of MANTRA Chain. It is used for validator staking, on-chain governance, and fee payment. The mantra-1 mainnet launched in 2024 with a focus on regulated RWA markets in the Middle East and Asia." + +source: https://github.com/ChainBench/OpenChainBench/tree/main/harnesses/rpc-capabilities + +prometheus: + window: 24h + freshness_metric: rpc_latency_milliseconds + +rank_matrix_query: avg by (provider, region) (ocb:rpc_latency_milliseconds:p50_24h{chain="mantrachain"}) + +dimensions: + region: + - { value: all, label: All regions } + - { value: us-east, label: US-East } + - { value: eu-west, label: EU-West } + - { value: sgp, label: Singapore } + +providers: + - slug: mantrachain-official + name: MANTRA + tag: MANTRA Chain official public RPC node, no API key required + formula: "50th percentile over 24h of client-side round-trip latency (ms) for a GET /status sent every 60s from 3 regions to rpc.mantrachain.io." + queries: + p50: avg(ocb:rpc_latency_milliseconds:p50_24h{provider="mantrachain-official", chain="mantrachain"}) + p90: avg(ocb:rpc_latency_milliseconds:p90_24h{provider="mantrachain-official", chain="mantrachain"}) + p99: avg(ocb:rpc_latency_milliseconds:p99_24h{provider="mantrachain-official", chain="mantrachain"}) + mean: avg(ocb:rpc_latency_milliseconds:mean_24h{provider="mantrachain-official", chain="mantrachain"}) + success: sum(ocb:rpc_call:ok_rate_24h{provider="mantrachain-official", chain="mantrachain"}) / sum(ocb:rpc_call:rate_24h{provider="mantrachain-official", chain="mantrachain"}) + sample_size: sum(ocb:rpc_call:increase_24h{provider="mantrachain-official", chain="mantrachain"}) + series: avg(avg_over_time(rpc_latency_milliseconds{provider="mantrachain-official", chain="mantrachain"}[1h])) + regions: + - region: us-east + p50: avg(ocb:rpc_latency_milliseconds:p50_24h{provider="mantrachain-official", chain="mantrachain", region="us-east"}) + series: avg_over_time(rpc_latency_milliseconds{provider="mantrachain-official", chain="mantrachain", region="us-east"}[1h]) + - region: eu-west + p50: avg(ocb:rpc_latency_milliseconds:p50_24h{provider="mantrachain-official", chain="mantrachain", region="eu-west"}) + series: avg_over_time(rpc_latency_milliseconds{provider="mantrachain-official", chain="mantrachain", region="eu-west"}[1h]) + - region: ap-southeast + p50: avg(ocb:rpc_latency_milliseconds:p50_24h{provider="mantrachain-official", chain="mantrachain", region="sgp"}) + series: avg_over_time(rpc_latency_milliseconds{provider="mantrachain-official", chain="mantrachain", region="sgp"}[1h]) + + - slug: itrocket + name: ITRocket + tag: ITRocket public MANTRA Chain RPC, no API key required + formula: "50th percentile over 24h of client-side round-trip latency (ms) for a GET /status sent every 60s from 3 regions to mantra-mainnet-rpc.itrocket.net." + queries: + p50: avg(ocb:rpc_latency_milliseconds:p50_24h{provider="itrocket", chain="mantrachain"}) + p90: avg(ocb:rpc_latency_milliseconds:p90_24h{provider="itrocket", chain="mantrachain"}) + p99: avg(ocb:rpc_latency_milliseconds:p99_24h{provider="itrocket", chain="mantrachain"}) + mean: avg(ocb:rpc_latency_milliseconds:mean_24h{provider="itrocket", chain="mantrachain"}) + success: sum(ocb:rpc_call:ok_rate_24h{provider="itrocket", chain="mantrachain"}) / sum(ocb:rpc_call:rate_24h{provider="itrocket", chain="mantrachain"}) + sample_size: sum(ocb:rpc_call:increase_24h{provider="itrocket", chain="mantrachain"}) + series: avg(avg_over_time(rpc_latency_milliseconds{provider="itrocket", chain="mantrachain"}[1h])) + regions: + - region: us-east + p50: avg(ocb:rpc_latency_milliseconds:p50_24h{provider="itrocket", chain="mantrachain", region="us-east"}) + series: avg_over_time(rpc_latency_milliseconds{provider="itrocket", chain="mantrachain", region="us-east"}[1h]) + - region: eu-west + p50: avg(ocb:rpc_latency_milliseconds:p50_24h{provider="itrocket", chain="mantrachain", region="eu-west"}) + series: avg_over_time(rpc_latency_milliseconds{provider="itrocket", chain="mantrachain", region="eu-west"}[1h]) + - region: ap-southeast + p50: avg(ocb:rpc_latency_milliseconds:p50_24h{provider="itrocket", chain="mantrachain", region="sgp"}) + series: avg_over_time(rpc_latency_milliseconds{provider="itrocket", chain="mantrachain", region="sgp"}[1h]) + + - slug: polkachu + name: Polkachu + tag: Polkachu public MANTRA Chain RPC, no API key required + formula: "50th percentile over 24h of client-side round-trip latency (ms) for a GET /status sent every 60s from 3 regions to mantra-rpc.polkachu.com." + queries: + p50: avg(ocb:rpc_latency_milliseconds:p50_24h{provider="polkachu", chain="mantrachain"}) + p90: avg(ocb:rpc_latency_milliseconds:p90_24h{provider="polkachu", chain="mantrachain"}) + p99: avg(ocb:rpc_latency_milliseconds:p99_24h{provider="polkachu", chain="mantrachain"}) + mean: avg(ocb:rpc_latency_milliseconds:mean_24h{provider="polkachu", chain="mantrachain"}) + success: sum(ocb:rpc_call:ok_rate_24h{provider="polkachu", chain="mantrachain"}) / sum(ocb:rpc_call:rate_24h{provider="polkachu", chain="mantrachain"}) + sample_size: sum(ocb:rpc_call:increase_24h{provider="polkachu", chain="mantrachain"}) + series: avg(avg_over_time(rpc_latency_milliseconds{provider="polkachu", chain="mantrachain"}[1h])) + regions: + - region: us-east + p50: avg(ocb:rpc_latency_milliseconds:p50_24h{provider="polkachu", chain="mantrachain", region="us-east"}) + series: avg_over_time(rpc_latency_milliseconds{provider="polkachu", chain="mantrachain", region="us-east"}[1h]) + - region: eu-west + p50: avg(ocb:rpc_latency_milliseconds:p50_24h{provider="polkachu", chain="mantrachain", region="eu-west"}) + series: avg_over_time(rpc_latency_milliseconds{provider="polkachu", chain="mantrachain", region="eu-west"}[1h]) + - region: ap-southeast + p50: avg(ocb:rpc_latency_milliseconds:p50_24h{provider="polkachu", chain="mantrachain", region="sgp"}) + series: avg_over_time(rpc_latency_milliseconds{provider="polkachu", chain="mantrachain", region="sgp"}[1h]) diff --git a/benchmarks/shentu-rpc.yml b/benchmarks/shentu-rpc.yml new file mode 100644 index 000000000..49d538b63 --- /dev/null +++ b/benchmarks/shentu-rpc.yml @@ -0,0 +1,138 @@ +# OpenChainBench. Bench No 254 + +slug: shentu-rpc +number: "254" +title: Fastest free Shentu RPC, live no-key endpoint latency +seo_title: "Fastest free Shentu RPC 2026" +seo_description: "{{best_name}} leads free Shentu RPC at {{best_p50}} (block height p50, 24h). 3 providers measured every 60s from 3 regions." +subtitle: HTTP round-trip latency for Tendermint /status queries against every available public Shentu (shentu-2.2) endpoint, audited every 60 seconds from 3 regions. + +category: RPCs +status: live +metric: RPC latency +unit: ms +higher_is_better: false + +seo_intro: | + Shentu is a Cosmos SDK blockchain (chain ID shentu-2.2) focused on blockchain security. It provides a decentralized security oracle, on-chain bug bounty platform (CertiK Shield), and formal verification tools for smart contracts. The chain uses the standard Tendermint RPC interface; block height is fetched via the /status endpoint. Public keyless RPC nodes are provided by the Shentu official team, Polkachu, and High Stakes. Every provider was live-verified with consecutive block-height probes at launch. + +abstract: | + Per-chain member of the RPC latency cluster, extended to Shentu (shentu-2.2). + We measure the round-trip latency of a Tendermint /status query against + every available public Shentu endpoint that sustains continuous probing: + 3 providers at launch, every 60 seconds, from us-east, eu-west and + Singapore. Each provider is probed via a GET /status request + from which the block number is extracted. + The harness classifies every response (ok / http_err / jsonrpc_err / stale / timeout) + with a Cosmos-scaled staleness gap (20 blocks, around 120 s at 6 s/block). + The cross-chain view lives on the parent rpc-capabilities benchmark; + this page is the Shentu-scoped answer with per-region breakdowns as + a first-class dimension. + +methodology: + - "Cadence: every 60 seconds per provider, from each of 3 probe regions (us-east Virginia, eu-west Amsterdam, sgp Singapore). Headline p50/p90/p99 aggregate across all 3 regions via Prometheus avg(quantile_over_time(...)); per-region breakdowns are first-class on this page via the region tabs." + - "Payload: GET /status. The result.sync_info.latest_block_height field (string-encoded integer) is extracted as the current block height." + - "Latency: client-side round-trip delta in milliseconds, exposed as both a gauge and a histogram (buckets 50 ms to 10 s), so percentiles are computed via Prometheus quantile_over_time over the last 24 hours." + - "Call-result classification: ok (parsable block height returned), http_err, jsonrpc_err, stale (block more than 20 behind the cross-provider tip), timeout." + - "This page is part of the per-chain RPC cluster derived from the cross-chain rpc-capabilities benchmark; the identical harness, cadence and exclusion rules apply on every chain." + - "Chain scope: every query on this page is pinned to chain=shentu. Provider coverage at launch: 3 endpoints (Shentu official, Polkachu, High Stakes)." + +findings: + - "{{best_name}} leads free Shentu RPC at {{best_p50}} (Tendermint /status p50, 24h) across 3 measured providers." + +faq: + - q: "What is the fastest free Shentu RPC right now?" + a: "{{best_name}} currently leads at {{best_p50}} (Shentu block height p50 over the last 24h), measured against 3 providers probed every 60 seconds from us-east, eu-west and Singapore. The leaderboard re-sorts continuously against fresh Prometheus samples." + - q: "Which Shentu RPC endpoints work without an API key?" + a: "3 endpoints sustain continuous keyless probing at launch: Shentu official (rpc.shentu.org), Polkachu (shentu-rpc.polkachu.com), and High Stakes (shentu-rpc.highstakes.ch). Every listed endpoint was live-verified before inclusion." + - q: "What is Shentu and why does its RPC latency matter?" + a: "Shentu is a Cosmos SDK blockchain built by CertiK, focused on blockchain security infrastructure. It powers the CertiK Shield decentralized reimbursement platform and a security oracle that scores smart contracts on-chain. Developers integrating with CertiK Shield, querying security scores, or building on the Shentu ecosystem need reliable low-latency RPC access." + - q: "Does the fastest Shentu RPC change by region?" + a: "Yes. The official Shentu node and community validators are hosted in different regions. The region tabs re-scope every number to a single probe origin so you can pick the best endpoint for your user base." + - q: "What is the CTK token on Shentu?" + a: "CTK (CertiK) is the native staking and governance token of the Shentu chain (denominated as uctk on-chain). It is used to stake in the CertiK Shield protection pool, pay for security oracle queries, and participate in on-chain governance." + +source: https://github.com/ChainBench/OpenChainBench/tree/main/harnesses/rpc-capabilities + +prometheus: + window: 24h + freshness_metric: rpc_latency_milliseconds + +rank_matrix_query: avg by (provider, region) (ocb:rpc_latency_milliseconds:p50_24h{chain="shentu"}) + +dimensions: + region: + - { value: all, label: All regions } + - { value: us-east, label: US-East } + - { value: eu-west, label: EU-West } + - { value: sgp, label: Singapore } + +providers: + - slug: shentu-official + name: Shentu + tag: Shentu official public RPC node, no API key required + formula: "50th percentile over 24h of client-side round-trip latency (ms) for a GET /status sent every 60s from 3 regions to rpc.shentu.org." + queries: + p50: avg(ocb:rpc_latency_milliseconds:p50_24h{provider="shentu-official", chain="shentu"}) + p90: avg(ocb:rpc_latency_milliseconds:p90_24h{provider="shentu-official", chain="shentu"}) + p99: avg(ocb:rpc_latency_milliseconds:p99_24h{provider="shentu-official", chain="shentu"}) + mean: avg(ocb:rpc_latency_milliseconds:mean_24h{provider="shentu-official", chain="shentu"}) + success: sum(ocb:rpc_call:ok_rate_24h{provider="shentu-official", chain="shentu"}) / sum(ocb:rpc_call:rate_24h{provider="shentu-official", chain="shentu"}) + sample_size: sum(ocb:rpc_call:increase_24h{provider="shentu-official", chain="shentu"}) + series: avg(avg_over_time(rpc_latency_milliseconds{provider="shentu-official", chain="shentu"}[1h])) + regions: + - region: us-east + p50: avg(ocb:rpc_latency_milliseconds:p50_24h{provider="shentu-official", chain="shentu", region="us-east"}) + series: avg_over_time(rpc_latency_milliseconds{provider="shentu-official", chain="shentu", region="us-east"}[1h]) + - region: eu-west + p50: avg(ocb:rpc_latency_milliseconds:p50_24h{provider="shentu-official", chain="shentu", region="eu-west"}) + series: avg_over_time(rpc_latency_milliseconds{provider="shentu-official", chain="shentu", region="eu-west"}[1h]) + - region: ap-southeast + p50: avg(ocb:rpc_latency_milliseconds:p50_24h{provider="shentu-official", chain="shentu", region="sgp"}) + series: avg_over_time(rpc_latency_milliseconds{provider="shentu-official", chain="shentu", region="sgp"}[1h]) + + - slug: polkachu + name: Polkachu + tag: Polkachu public Shentu RPC, no API key required + formula: "50th percentile over 24h of client-side round-trip latency (ms) for a GET /status sent every 60s from 3 regions to shentu-rpc.polkachu.com." + queries: + p50: avg(ocb:rpc_latency_milliseconds:p50_24h{provider="polkachu", chain="shentu"}) + p90: avg(ocb:rpc_latency_milliseconds:p90_24h{provider="polkachu", chain="shentu"}) + p99: avg(ocb:rpc_latency_milliseconds:p99_24h{provider="polkachu", chain="shentu"}) + mean: avg(ocb:rpc_latency_milliseconds:mean_24h{provider="polkachu", chain="shentu"}) + success: sum(ocb:rpc_call:ok_rate_24h{provider="polkachu", chain="shentu"}) / sum(ocb:rpc_call:rate_24h{provider="polkachu", chain="shentu"}) + sample_size: sum(ocb:rpc_call:increase_24h{provider="polkachu", chain="shentu"}) + series: avg(avg_over_time(rpc_latency_milliseconds{provider="polkachu", chain="shentu"}[1h])) + regions: + - region: us-east + p50: avg(ocb:rpc_latency_milliseconds:p50_24h{provider="polkachu", chain="shentu", region="us-east"}) + series: avg_over_time(rpc_latency_milliseconds{provider="polkachu", chain="shentu", region="us-east"}[1h]) + - region: eu-west + p50: avg(ocb:rpc_latency_milliseconds:p50_24h{provider="polkachu", chain="shentu", region="eu-west"}) + series: avg_over_time(rpc_latency_milliseconds{provider="polkachu", chain="shentu", region="eu-west"}[1h]) + - region: ap-southeast + p50: avg(ocb:rpc_latency_milliseconds:p50_24h{provider="polkachu", chain="shentu", region="sgp"}) + series: avg_over_time(rpc_latency_milliseconds{provider="polkachu", chain="shentu", region="sgp"}[1h]) + + - slug: highstakes + name: High Stakes + tag: High Stakes public Shentu RPC, no API key required + formula: "50th percentile over 24h of client-side round-trip latency (ms) for a GET /status sent every 60s from 3 regions to shentu-rpc.highstakes.ch." + queries: + p50: avg(ocb:rpc_latency_milliseconds:p50_24h{provider="highstakes", chain="shentu"}) + p90: avg(ocb:rpc_latency_milliseconds:p90_24h{provider="highstakes", chain="shentu"}) + p99: avg(ocb:rpc_latency_milliseconds:p99_24h{provider="highstakes", chain="shentu"}) + mean: avg(ocb:rpc_latency_milliseconds:mean_24h{provider="highstakes", chain="shentu"}) + success: sum(ocb:rpc_call:ok_rate_24h{provider="highstakes", chain="shentu"}) / sum(ocb:rpc_call:rate_24h{provider="highstakes", chain="shentu"}) + sample_size: sum(ocb:rpc_call:increase_24h{provider="highstakes", chain="shentu"}) + series: avg(avg_over_time(rpc_latency_milliseconds{provider="highstakes", chain="shentu"}[1h])) + regions: + - region: us-east + p50: avg(ocb:rpc_latency_milliseconds:p50_24h{provider="highstakes", chain="shentu", region="us-east"}) + series: avg_over_time(rpc_latency_milliseconds{provider="highstakes", chain="shentu", region="us-east"}[1h]) + - region: eu-west + p50: avg(ocb:rpc_latency_milliseconds:p50_24h{provider="highstakes", chain="shentu", region="eu-west"}) + series: avg_over_time(rpc_latency_milliseconds{provider="highstakes", chain="shentu", region="eu-west"}[1h]) + - region: ap-southeast + p50: avg(ocb:rpc_latency_milliseconds:p50_24h{provider="highstakes", chain="shentu", region="sgp"}) + series: avg_over_time(rpc_latency_milliseconds{provider="highstakes", chain="shentu", region="sgp"}[1h]) diff --git a/harnesses/rpc-capabilities/cmd/script/config.go b/harnesses/rpc-capabilities/cmd/script/config.go index 186460674..580bbb0eb 100644 --- a/harnesses/rpc-capabilities/cmd/script/config.go +++ b/harnesses/rpc-capabilities/cmd/script/config.go @@ -1693,6 +1693,28 @@ func chains() []Chain { {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-12. Union — Cosmos SDK (union-1), Tendermint /status. Nodes.Guru + Stake And Relax + High Stakes. { Slug: "union", diff --git a/public/logos/itrocket.svg b/public/logos/itrocket.svg new file mode 100644 index 000000000..775cfe671 --- /dev/null +++ b/public/logos/itrocket.svg @@ -0,0 +1,5 @@ + + + ITROCKET + 🚀 + diff --git a/public/logos/mantrachain.svg b/public/logos/mantrachain.svg new file mode 100644 index 000000000..10bcec3c9 --- /dev/null +++ b/public/logos/mantrachain.svg @@ -0,0 +1,4 @@ + + + OM + diff --git a/public/logos/shentu-official.svg b/public/logos/shentu-official.svg new file mode 100644 index 000000000..4ca2baba1 --- /dev/null +++ b/public/logos/shentu-official.svg @@ -0,0 +1,4 @@ + + + CTK + diff --git a/public/logos/shentu.svg b/public/logos/shentu.svg new file mode 100644 index 000000000..4ca2baba1 --- /dev/null +++ b/public/logos/shentu.svg @@ -0,0 +1,4 @@ + + + CTK + diff --git a/src/data/provider-registry.ts b/src/data/provider-registry.ts index 9046f4ba6..8756c7e9e 100644 --- a/src/data/provider-registry.ts +++ b/src/data/provider-registry.ts @@ -2620,6 +2620,29 @@ export const PROVIDER_REGISTRY: Record = { 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", + }, + + // ─── Union providers (bench 253) ───────────────────────────────────── "nodes-guru": { url: "https://nodes.guru", diff --git a/src/lib/brand.ts b/src/lib/brand.ts index bb62f3166..0d2d6e050 100644 --- a/src/lib/brand.ts +++ b/src/lib/brand.ts @@ -173,15 +173,18 @@ const BRANDS: Record = { acala: { color: "#E40C5B" }, // acala red/pink (official brand) interlay: { color: "#1A3BDB" }, // interlay blue (official brand) - // ─── Cosmos SDK chains (benches 247, 250-253) ─── - babylon: { color: "#F8811A" }, // babylon orange (official brand) - chihuahua: { color: "#E05F2A" }, // chihuahua orange-red (official brand) - sentinel: { color: "#00C2FF" }, // sentinel cyan (official brand) - fetchhub: { color: "#3B2D8E" }, // fetch.ai deep purple (official brand) - union: { color: "#6366F1" }, // union indigo (brand kit) + // ─── Cosmos SDK chains (benches 247, 250-255) ─── + babylon: { color: "#F8811A" }, // babylon orange (official brand) + chihuahua: { color: "#E05F2A" }, // chihuahua orange-red (official brand) + sentinel: { color: "#00C2FF" }, // sentinel cyan (official brand) + fetchhub: { color: "#3B2D8E" }, // fetch.ai deep purple (official brand) + union: { color: "#6366F1" }, // union indigo (brand kit) + shentu: { color: "#1A6DFF" }, // shentu blue (certik brand) + mantrachain: { color: "#E8A020" }, // mantra gold (om token brand) "nodes-guru": { color: "#F59E0B" }, // nodes.guru amber stakeandrelax: { color: "#10B981" }, // stake and relax emerald highstakes: { color: "#3B82F6" }, // high stakes blue + itrocket: { color: "#E53E3E" }, // itrocket red "cosmos-directory": { color: "#1B1B2F", dark: true }, // cosmos directory dark navy // ─── Bitcoin Cash chain + providers (bench 244) ─── diff --git a/src/lib/logo-manifest.ts b/src/lib/logo-manifest.ts index 6aa6d2b7a..b5250d683 100644 --- a/src/lib/logo-manifest.ts +++ b/src/lib/logo-manifest.ts @@ -332,9 +332,12 @@ const RAW: Record = { sentinel: "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/logos/sentinel.svg", fetchhub: "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/logos/fetchai.svg", union: "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/logos/union.svg", + shentu: "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/logos/shentu.svg", + mantrachain: "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/logos/mantrachain.svg", "nodes-guru": "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/logos/nodes-guru.svg", stakeandrelax: "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/logos/stakeandrelax.svg", highstakes: "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/logos/highstakes.svg", + itrocket: "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/logos/itrocket.svg", "cosmos-directory": "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/logos/cosmos-directory.svg", // ─── Oracle deviation (bench 025) — additional brand logos ─── @@ -747,6 +750,8 @@ const ALIASES: Record = { // Chihuahua + Fetch.ai official node aliases → chain logo "chihuahua-official": "chihuahua", "fetchai-official": "fetchhub", + "shentu-official": "shentu", + "mantrachain-official": "mantrachain", // Non-EVM wave-3 (benches 222-231) — provider-official aliases to chain slug "ecadinfra": "tezos", From d7d5fc7465521332e52c9150c75ca3c688bc8873 Mon Sep 17 00:00:00 2001 From: Flotapponnier <160007691+Flotapponnier@users.noreply.github.com> Date: Sat, 29 Aug 2026 22:53:32 +0200 Subject: [PATCH 06/10] feat: Band Protocol #256 + cheqd #257 benches MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * fix(harnesses): cap LZ/CCIP/Axelar cardinality to bench-tracked chains only (#2187) * fix(cache): strip heavy fields from aggregate blob to pass 2MB limit (#2188) Hub pages only need card data. Drop seriesByRegion24h, metricPanels, editorial text (seoIntro/faq/findings/methodology/disclaimer/abstract/ perChainExplainer) from the blob aggregate slim. Cuts serialized size from ~4.3 MB to under 2 MB so unstable_cache actually persists the entry instead of silently dropping it on every render. * fix: include still-open Gains positions in carry projection (#2189) * fix(cache): strip heavy fields from aggregate blob to pass 2MB limit Hub pages only need card data. Drop seriesByRegion24h, metricPanels, editorial text (seoIntro/faq/findings/methodology/disclaimer/abstract/ perChainExplainer) from the blob aggregate slim. Cuts serialized size from ~4.3 MB to under 2 MB so unstable_cache actually persists the entry instead of silently dropping it on every render. * fix: include still-open Gains positions in carry projection * fix: update Gains action names v5→v6, handle position size increases (#2190) * fix(cache): strip heavy fields from aggregate blob to pass 2MB limit Hub pages only need card data. Drop seriesByRegion24h, metricPanels, editorial text (seoIntro/faq/findings/methodology/disclaimer/abstract/ perChainExplainer) from the blob aggregate slim. Cuts serialized size from ~4.3 MB to under 2 MB so unstable_cache actually persists the entry instead of silently dropping it on every render. * fix: include still-open Gains positions in carry projection * fix: update Gains open action names to v6 API, handle TradePosSizeIncrease * fix: Gains carry projection (v6 action names + still-open positions) * fix(cache): strip heavy fields from aggregate blob to pass 2MB limit Hub pages only need card data. Drop seriesByRegion24h, metricPanels, editorial text (seoIntro/faq/findings/methodology/disclaimer/abstract/ perChainExplainer) from the blob aggregate slim. Cuts serialized size from ~4.3 MB to under 2 MB so unstable_cache actually persists the entry instead of silently dropping it on every render. * fix: include still-open Gains positions in carry projection * fix: update Gains open action names to v6 API, handle TradePosSizeIncrease * fix: widen coin name column for long tickers (xyz:NVDA) * fix(cache): strip heavy fields from aggregate blob to pass 2MB limit Hub pages only need card data. Drop seriesByRegion24h, metricPanels, editorial text (seoIntro/faq/findings/methodology/disclaimer/abstract/ perChainExplainer) from the blob aggregate slim. Cuts serialized size from ~4.3 MB to under 2 MB so unstable_cache actually persists the entry instead of silently dropping it on every render. * fix: include still-open Gains positions in carry projection * fix: update Gains open action names to v6 API, handle TradePosSizeIncrease * fix: widen coin name column in top markets to fit xyz:NVDA * feat(rpc): add Union bench #253 (Nodes.Guru, Stake And Relax, High Stakes) (#2194) * fix(union-rpc): remove em dashes from YAML (#2196) * feat(rpc): Shentu #254 + MANTRA Chain #255 (#2199) * feat(rpc): add Shentu bench #254 and MANTRA Chain bench #255 * fix: remove accidental dev-portal submodule * feat: Band Protocol #256 + cheqd #257 benches --- benchmarks/bandchain-rpc.yml | 35 +++++++++++++++++++ benchmarks/cheqd-rpc.yml | 35 +++++++++++++++++++ .../rpc-capabilities/cmd/script/config.go | 22 ++++++++++++ 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 | 29 +++++++++++++++ src/lib/brand.ts | 8 +++++ src/lib/logo-manifest.ts | 6 ++++ 10 files changed, 153 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 000000000..67a1c01ff --- /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 000000000..c5d028836 --- /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 580bbb0eb..dc35473f2 100644 --- a/harnesses/rpc-capabilities/cmd/script/config.go +++ b/harnesses/rpc-capabilities/cmd/script/config.go @@ -1715,6 +1715,28 @@ func chains() []Chain { {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", diff --git a/public/logos/bandchain.svg b/public/logos/bandchain.svg new file mode 100644 index 000000000..ec9ec2f2b --- /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 000000000..10461cae2 --- /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 000000000..609a75903 --- /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 000000000..08a9774dc --- /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 8756c7e9e..dd0f2bd4e 100644 --- a/src/data/provider-registry.ts +++ b/src/data/provider-registry.ts @@ -2643,6 +2643,35 @@ export const PROVIDER_REGISTRY: Record = { }, + // ─── 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", diff --git a/src/lib/brand.ts b/src/lib/brand.ts index 0d2d6e050..7ce6a49e1 100644 --- a/src/lib/brand.ts +++ b/src/lib/brand.ts @@ -187,6 +187,14 @@ const BRANDS: Record = { itrocket: { color: "#E53E3E" }, // itrocket red "cosmos-directory": { color: "#1B1B2F", dark: true }, // cosmos directory dark navy + // ─── 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 b5250d683..e893db5ab 100644 --- a/src/lib/logo-manifest.ts +++ b/src/lib/logo-manifest.ts @@ -339,6 +339,10 @@ const RAW: Record = { highstakes: "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/logos/highstakes.svg", itrocket: "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/logos/itrocket.svg", "cosmos-directory": "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/logos/cosmos-directory.svg", + bandchain: "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/logos/bandchain.svg", + cheqd: "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/logos/cheqd.svg", + stakewolle: "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/logos/stakewolle.svg", + nodestake: "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/logos/nodestake.svg", // ─── Oracle deviation (bench 025) — additional brand logos ─── // (pairs alias to chain/asset logos in the ALIASES block below) @@ -752,6 +756,8 @@ const ALIASES: Record = { "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", From 6ca6a2f62ef8fe2316e71e89bff730542343fd67 Mon Sep 17 00:00:00 2001 From: Flotapponnier <160007691+Flotapponnier@users.noreply.github.com> Date: Sat, 29 Aug 2026 23:04:48 +0200 Subject: [PATCH 07/10] fix: bench-blob revalidate 300s to fix perp ISR conflict (#2206) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * merge: dev → main (Gains carry fix + vault fee split) (#2191) * fix(harnesses): cap LZ/CCIP/Axelar cardinality to bench-tracked chains only (#2187) * fix(cache): strip heavy fields from aggregate blob to pass 2MB limit (#2188) Hub pages only need card data. Drop seriesByRegion24h, metricPanels, editorial text (seoIntro/faq/findings/methodology/disclaimer/abstract/ perChainExplainer) from the blob aggregate slim. Cuts serialized size from ~4.3 MB to under 2 MB so unstable_cache actually persists the entry instead of silently dropping it on every render. * fix: include still-open Gains positions in carry projection (#2189) * fix(cache): strip heavy fields from aggregate blob to pass 2MB limit Hub pages only need card data. Drop seriesByRegion24h, metricPanels, editorial text (seoIntro/faq/findings/methodology/disclaimer/abstract/ perChainExplainer) from the blob aggregate slim. Cuts serialized size from ~4.3 MB to under 2 MB so unstable_cache actually persists the entry instead of silently dropping it on every render. * fix: include still-open Gains positions in carry projection * fix: update Gains action names v5→v6, handle position size increases (#2190) * fix(cache): strip heavy fields from aggregate blob to pass 2MB limit Hub pages only need card data. Drop seriesByRegion24h, metricPanels, editorial text (seoIntro/faq/findings/methodology/disclaimer/abstract/ perChainExplainer) from the blob aggregate slim. Cuts serialized size from ~4.3 MB to under 2 MB so unstable_cache actually persists the entry instead of silently dropping it on every render. * fix: include still-open Gains positions in carry projection * fix: update Gains open action names to v6 API, handle TradePosSizeIncrease * feat(rpc): Union bench #253 (#2195) * fix(harnesses): cap LZ/CCIP/Axelar cardinality to bench-tracked chains only (#2187) * fix(cache): strip heavy fields from aggregate blob to pass 2MB limit (#2188) Hub pages only need card data. Drop seriesByRegion24h, metricPanels, editorial text (seoIntro/faq/findings/methodology/disclaimer/abstract/ perChainExplainer) from the blob aggregate slim. Cuts serialized size from ~4.3 MB to under 2 MB so unstable_cache actually persists the entry instead of silently dropping it on every render. * fix: include still-open Gains positions in carry projection (#2189) * fix(cache): strip heavy fields from aggregate blob to pass 2MB limit Hub pages only need card data. Drop seriesByRegion24h, metricPanels, editorial text (seoIntro/faq/findings/methodology/disclaimer/abstract/ perChainExplainer) from the blob aggregate slim. Cuts serialized size from ~4.3 MB to under 2 MB so unstable_cache actually persists the entry instead of silently dropping it on every render. * fix: include still-open Gains positions in carry projection * fix: update Gains action names v5→v6, handle position size increases (#2190) * fix(cache): strip heavy fields from aggregate blob to pass 2MB limit Hub pages only need card data. Drop seriesByRegion24h, metricPanels, editorial text (seoIntro/faq/findings/methodology/disclaimer/abstract/ perChainExplainer) from the blob aggregate slim. Cuts serialized size from ~4.3 MB to under 2 MB so unstable_cache actually persists the entry instead of silently dropping it on every render. * fix: include still-open Gains positions in carry projection * fix: update Gains open action names to v6 API, handle TradePosSizeIncrease * fix: Gains carry projection (v6 action names + still-open positions) * fix(cache): strip heavy fields from aggregate blob to pass 2MB limit Hub pages only need card data. Drop seriesByRegion24h, metricPanels, editorial text (seoIntro/faq/findings/methodology/disclaimer/abstract/ perChainExplainer) from the blob aggregate slim. Cuts serialized size from ~4.3 MB to under 2 MB so unstable_cache actually persists the entry instead of silently dropping it on every render. * fix: include still-open Gains positions in carry projection * fix: update Gains open action names to v6 API, handle TradePosSizeIncrease * fix: widen coin name column for long tickers (xyz:NVDA) * fix(cache): strip heavy fields from aggregate blob to pass 2MB limit Hub pages only need card data. Drop seriesByRegion24h, metricPanels, editorial text (seoIntro/faq/findings/methodology/disclaimer/abstract/ perChainExplainer) from the blob aggregate slim. Cuts serialized size from ~4.3 MB to under 2 MB so unstable_cache actually persists the entry instead of silently dropping it on every render. * fix: include still-open Gains positions in carry projection * fix: update Gains open action names to v6 API, handle TradePosSizeIncrease * fix: widen coin name column in top markets to fit xyz:NVDA * feat(rpc): add Union bench #253 (Nodes.Guru, Stake And Relax, High Stakes) (#2194) * fix(union-rpc): remove em dashes from YAML * feat(union): add provider SVG logos and brand colors * feat: Shentu #254 + MANTRA Chain #255 benches * fix(harnesses): cap LZ/CCIP/Axelar cardinality to bench-tracked chains only (#2187) * fix(cache): strip heavy fields from aggregate blob to pass 2MB limit (#2188) Hub pages only need card data. Drop seriesByRegion24h, metricPanels, editorial text (seoIntro/faq/findings/methodology/disclaimer/abstract/ perChainExplainer) from the blob aggregate slim. Cuts serialized size from ~4.3 MB to under 2 MB so unstable_cache actually persists the entry instead of silently dropping it on every render. * fix: include still-open Gains positions in carry projection (#2189) * fix(cache): strip heavy fields from aggregate blob to pass 2MB limit Hub pages only need card data. Drop seriesByRegion24h, metricPanels, editorial text (seoIntro/faq/findings/methodology/disclaimer/abstract/ perChainExplainer) from the blob aggregate slim. Cuts serialized size from ~4.3 MB to under 2 MB so unstable_cache actually persists the entry instead of silently dropping it on every render. * fix: include still-open Gains positions in carry projection * fix: update Gains action names v5→v6, handle position size increases (#2190) * fix(cache): strip heavy fields from aggregate blob to pass 2MB limit Hub pages only need card data. Drop seriesByRegion24h, metricPanels, editorial text (seoIntro/faq/findings/methodology/disclaimer/abstract/ perChainExplainer) from the blob aggregate slim. Cuts serialized size from ~4.3 MB to under 2 MB so unstable_cache actually persists the entry instead of silently dropping it on every render. * fix: include still-open Gains positions in carry projection * fix: update Gains open action names to v6 API, handle TradePosSizeIncrease * fix: Gains carry projection (v6 action names + still-open positions) * fix(cache): strip heavy fields from aggregate blob to pass 2MB limit Hub pages only need card data. Drop seriesByRegion24h, metricPanels, editorial text (seoIntro/faq/findings/methodology/disclaimer/abstract/ perChainExplainer) from the blob aggregate slim. Cuts serialized size from ~4.3 MB to under 2 MB so unstable_cache actually persists the entry instead of silently dropping it on every render. * fix: include still-open Gains positions in carry projection * fix: update Gains open action names to v6 API, handle TradePosSizeIncrease * fix: widen coin name column for long tickers (xyz:NVDA) * fix(cache): strip heavy fields from aggregate blob to pass 2MB limit Hub pages only need card data. Drop seriesByRegion24h, metricPanels, editorial text (seoIntro/faq/findings/methodology/disclaimer/abstract/ perChainExplainer) from the blob aggregate slim. Cuts serialized size from ~4.3 MB to under 2 MB so unstable_cache actually persists the entry instead of silently dropping it on every render. * fix: include still-open Gains positions in carry projection * fix: update Gains open action names to v6 API, handle TradePosSizeIncrease * fix: widen coin name column in top markets to fit xyz:NVDA * feat(rpc): add Union bench #253 (Nodes.Guru, Stake And Relax, High Stakes) (#2194) * fix(union-rpc): remove em dashes from YAML (#2196) * feat(rpc): Shentu #254 + MANTRA Chain #255 (#2199) * feat(rpc): add Shentu bench #254 and MANTRA Chain bench #255 * fix: remove accidental dev-portal submodule * fix: bench-blob revalidate 300s to fix perp ISR conflict --- src/lib/bench-blob.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lib/bench-blob.ts b/src/lib/bench-blob.ts index 07f96cde4..45e825e55 100644 --- a/src/lib/bench-blob.ts +++ b/src/lib/bench-blob.ts @@ -54,7 +54,7 @@ async function fetchJson(url: string): Promise { try { const res = await fetch(url, { signal: AbortSignal.timeout(FETCH_TIMEOUT_MS), - cache: "no-store", + next: { revalidate: 300 }, }); if (!res.ok) return null; return await res.json(); From e8aaddde9f4e7c428b6ec0e874416891cf169ee7 Mon Sep 17 00:00:00 2001 From: Florent Tapponnier <160007691+Flotapponnier@users.noreply.github.com> Date: Sun, 30 Aug 2026 00:49:39 +0200 Subject: [PATCH 08/10] fix(fee-compare): total position value + inline vault fee explanation --- src/components/fee-compare-client.tsx | 23 ++++++++++++++--------- 1 file changed, 14 insertions(+), 9 deletions(-) diff --git a/src/components/fee-compare-client.tsx b/src/components/fee-compare-client.tsx index a7ada7a71..b95130f37 100644 --- a/src/components/fee-compare-client.tsx +++ b/src/components/fee-compare-client.tsx @@ -741,24 +741,29 @@ function WalletSide({ return (
-

Volume

+

Total position value

{fmtUsd(volume)}

+

+ Sum of (collateral × leverage) across all trades in the period. +

Taker fees

{fmtUsd(gW.feesUsdc)}

{hasVault && ( -
-

- OI vault fee{" "} - OI imbalance surcharge -

-

- +{fmtUsd(gW.vaultFeesUsdc)} + <> +

+

Vault fee

+

+ +{fmtUsd(gW.vaultFeesUsdc)} +

+
+

+ Gains charges an extra fee to LPs when your trade increases the long/short imbalance. Hyperliquid does not have this — it uses funding rates instead.

-
+ )} {hasFunding && (
From c3bb3a6df5b20036ca7c872bffaa7c214bcfa8da Mon Sep 17 00:00:00 2001 From: Flotapponnier <160007691+Flotapponnier@users.noreply.github.com> Date: Sun, 30 Aug 2026 01:07:58 +0200 Subject: [PATCH 09/10] =?UTF-8?q?fix(fee-compare):=20remove=20vault=20fee?= =?UTF-8?q?=20=E2=80=94=20carry=20fees=20settled=20per-action,=20not=20OI?= =?UTF-8?q?=20surcharge=20(#2209)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/app/api/fee-compare/route.ts | 27 +++++++++++---------------- src/components/fee-compare-client.tsx | 21 ++------------------- 2 files changed, 13 insertions(+), 35 deletions(-) diff --git a/src/app/api/fee-compare/route.ts b/src/app/api/fee-compare/route.ts index b4f9e2e12..d6d533019 100644 --- a/src/app/api/fee-compare/route.ts +++ b/src/app/api/fee-compare/route.ts @@ -132,6 +132,7 @@ type GainsApiTrade = { realizedTradingFeesCollateral?: number; realizedFundingFeesCollateral?: number; realizedNewBorrowingFeesCollateral?: number; + realizedOldBorrowingFeesCollateral?: number; }; }; }; @@ -180,8 +181,7 @@ type HlWalletData = { type GainsWalletData = { events: number; - feesUsdc: number; // pure taker fee only (uiRealizedPnlData) - vaultFeesUsdc: number; // OI imbalance vault surcharge (tradeFeesData - uiRealizedPnlData) + feesUsdc: number; fundingFeesUsdc: number; fundingEstimated: boolean; borrowingFeesUsdc: number; @@ -193,8 +193,7 @@ type GainsWalletData = { pair: string; action: string; notional: number; - tradingFee: number; // taker fee only - vaultFee: number; // OI vault surcharge + tradingFee: number; fundingFee: number; borrowingFee: number; equivFee?: number; // equivalent fee on the other venue @@ -1525,23 +1524,20 @@ export async function GET(req: Request) { 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) { - // uiRealizedPnlData = pure taker fee; tradeFeesData = taker + OI vault surcharge + // Gains settles carry (funding + borrowing) on every action, not just closes. + // uiRealizedPnlData breaks down taker, funding, and borrowing separately — use it for all. 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; + const fundingFee = t.meta?.uiRealizedPnlData?.realizedFundingFeesCollateral ?? 0; + const borrowingFee = (t.meta?.uiRealizedPnlData?.realizedNewBorrowingFeesCollateral ?? 0) + + (t.meta?.uiRealizedPnlData?.realizedOldBorrowingFeesCollateral ?? 0); feesUsdc += takerFee; - vaultFeesUsdc += vaultFee; fundingFeesUsdc += fundingFee; borrowingFeesUsdc += borrowingFee; const tradeNotional = t.size * t.leverage; @@ -1550,7 +1546,7 @@ export async function GET(req: Request) { 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 }); + recentTrades.push({ date: t.date, pair: t.pair, action: t.action, notional: tradeNotional, tradingFee: takerFee, fundingFee, borrowingFee, equivFee, pnl_net: t.pnl_net }); } } @@ -1566,17 +1562,16 @@ export async function GET(req: Request) { } } - const netCostUsdc = feesUsdc + vaultFeesUsdc + fundingFeesUsdc + borrowingFeesUsdc; + const netCostUsdc = feesUsdc + fundingFeesUsdc + borrowingFeesUsdc; walletData = { events: usdcTrades.length, feesUsdc, - vaultFeesUsdc, fundingFeesUsdc, fundingEstimated, borrowingFeesUsdc, netCostUsdc, positionSizeUsdc: notionalUsd, - avgFeeRateBps: notionalUsd > 0 ? (feesUsdc / notionalUsd) * 10000 : 0, + avgFeeRateBps: notionalUsd > 0 ? (netCostUsdc / notionalUsd) * 10000 : 0, recentTrades, } satisfies GainsWalletData; } else if (fetchEvmWallet && slug === "gmx-v2" && gmxWalletData) { diff --git a/src/components/fee-compare-client.tsx b/src/components/fee-compare-client.tsx index b95130f37..be2404676 100644 --- a/src/components/fee-compare-client.tsx +++ b/src/components/fee-compare-client.tsx @@ -75,7 +75,6 @@ type HlWalletData = { type GainsWalletData = { events: number; feesUsdc: number; - vaultFeesUsdc: number; fundingFeesUsdc: number; fundingEstimated: boolean; borrowingFeesUsdc: number; @@ -88,7 +87,6 @@ type GainsWalletData = { action: string; notional: number; tradingFee: number; - vaultFee: number; fundingFee: number; borrowingFee: number; equivFee?: number; @@ -735,7 +733,6 @@ 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 ( @@ -752,19 +749,6 @@ function WalletSide({

Taker fees

{fmtUsd(gW.feesUsdc)}

- {hasVault && ( - <> -
-

Vault fee

-

- +{fmtUsd(gW.vaultFeesUsdc)} -

-
-

- Gains charges an extra fee to LPs when your trade increases the long/short imbalance. Hyperliquid does not have this — it uses funding rates instead. -

- - )} {hasFunding && (

@@ -1202,8 +1186,7 @@ function GainsTradeTable({ {rows.map((t, i) => { - const hasVault = (t.vaultFee ?? 0) > 0.001; - const netCost = t.tradingFee + (t.vaultFee ?? 0) + t.fundingFee + t.borrowingFee; + const netCost = t.tradingFee + t.fundingFee + t.borrowingFee; const diff = hasEquiv && t.equivFee !== undefined ? t.equivFee - netCost : undefined; return ( @@ -1225,7 +1208,7 @@ function GainsTradeTable({

{fmtUsd(netCost)}

{fmtUsd(t.tradingFee)} taker - {hasVault ? ` +${fmtUsd(t.vaultFee)} vault` : ""} + {t.borrowingFee > 0.001 ? ` +${fmtUsd(t.borrowingFee)} borrow` : ""} {Math.abs(t.fundingFee) > 0.001 ? ` ${t.fundingFee > 0 ? "+" : "−"}${fmtUsd(Math.abs(t.fundingFee))} fund` From 28a2f3c67251227f19de0716364bbf13487d90ff Mon Sep 17 00:00:00 2001 From: Florent Tapponnier <160007691+Flotapponnier@users.noreply.github.com> Date: Sun, 30 Aug 2026 14:39:44 +0200 Subject: [PATCH 10/10] =?UTF-8?q?feat(rpc):=20ICON=20bench=20#258=20?= =?UTF-8?q?=E2=80=94=20new=20icon=20probe=20kind,=203=20keyless=20provider?= =?UTF-8?q?s?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- benchmarks/icon-rpc.yml | 35 +++++++++++ .../rpc-capabilities/cmd/script/config.go | 11 ++++ .../rpc-capabilities/cmd/script/probe.go | 59 ++++++++++++++++++- public/logos/icon.svg | 4 ++ public/logos/iconblockchain.svg | 5 ++ src/data/provider-registry.ts | 19 ++++++ src/lib/brand.ts | 6 ++ src/lib/logo-manifest.ts | 4 ++ 8 files changed, 142 insertions(+), 1 deletion(-) create mode 100644 benchmarks/icon-rpc.yml create mode 100644 public/logos/icon.svg create mode 100644 public/logos/iconblockchain.svg diff --git a/benchmarks/icon-rpc.yml b/benchmarks/icon-rpc.yml new file mode 100644 index 000000000..f45ec814c --- /dev/null +++ b/benchmarks/icon-rpc.yml @@ -0,0 +1,35 @@ +id: 258 +slug: icon-rpc +title: "Fastest free ICON RPC, live no-key endpoint latency" +chain: icon +description: "Latency and availability benchmark for ICON public RPC endpoints" +category: rpc +kind: icon + +providers: + - slug: icon-solidwallet + name: "ICON Foundation" + url: "https://ctz.solidwallet.io" + - slug: icon-community + name: "ICON Community" + url: "https://api.icon.community" + - slug: iconblockchain + name: "iconblockchain.xyz" + url: "https://api.iconblockchain.xyz" + +seo_intro: | + ICON is a South Korean L1 blockchain focused on interoperability and enterprise adoption, using a Delegated Proof of Contribution (DPoC) consensus with ~2-second block finality. ICON nodes expose a JSON-RPC 2.0 API: POST /api/v3 with method icx_getLastBlock returns the latest block height. Free public endpoints are available keyless from the ICON Foundation (ctz.solidwallet.io), ICON Community (api.icon.community), and iconblockchain.xyz. + + This benchmark continuously measures RPC latency, availability and block-height freshness across these public ICON endpoints from three geographic regions. Every provider was live-verified with consecutive keyless icx_getLastBlock probes at launch. + +faq: + - q: "What does this benchmark measure?" + a: "Each probe issues a POST /api/v3 icx_getLastBlock request 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 60 seconds from US East, EU West, and AP Southeast." + - q: "Which endpoints are included?" + a: "The benchmark covers the ICON Foundation endpoint (ctz.solidwallet.io), ICON Community (api.icon.community), and iconblockchain.xyz. All three are keyless public endpoints requiring no API key." + - q: "Why does ICON RPC performance matter?" + a: "ICON powers ICX transfers, BTP cross-chain messages, and DApps across the ICON ecosystem. Low-latency RPC access is critical for wallets, DEX aggregators integrating ICON, and validators monitoring chain health." + - q: "How does ICON's ~2-second block time affect staleness detection?" + a: "With blocks every ~2 seconds, a gap of 150 blocks represents roughly 5 minutes of drift — the same threshold we use to classify other fast-finality chains as stale. A provider returning a block more than 150 behind the cross-provider tip is marked stale." + - 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 dc35473f2..b61dc0d04 100644 --- a/harnesses/rpc-capabilities/cmd/script/config.go +++ b/harnesses/rpc-capabilities/cmd/script/config.go @@ -1837,6 +1837,17 @@ func chains() []Chain { {Slug: "waves-exchange", Name: "Waves Exchange Node", URL: envDefault("RPC_URL_WAVES_EXCHANGE", "https://nodes.waves.exchange")}, }, }, + // 2026-08-29 wave-13. ICON blockchain — JSON-RPC icx_getLastBlock /api/v3, ~2 s/block. 3 keyless providers. + { + Slug: "icon", + Name: "ICON", + Kind: "icon", + Providers: []Provider{ + {Slug: "icon-solidwallet", Name: "ICON Foundation", URL: envDefault("RPC_URL_ICON_SOLIDWALLET", "https://ctz.solidwallet.io")}, + {Slug: "icon-community", Name: "ICON Community", URL: envDefault("RPC_URL_ICON_COMMUNITY", "https://api.icon.community")}, + {Slug: "iconblockchain", Name: "iconblockchain.xyz", URL: envDefault("RPC_URL_ICON_ICONBLOCKCHAIN", "https://api.iconblockchain.xyz")}, + }, + }, // 2026-08-18 wave-8. WAX gaming blockchain (Antelope) — REST GET /v1/chain/get_info, ~0.5 s/block. 3 keyless providers. { Slug: "wax", diff --git a/harnesses/rpc-capabilities/cmd/script/probe.go b/harnesses/rpc-capabilities/cmd/script/probe.go index 6a2569e29..77a2560cc 100644 --- a/harnesses/rpc-capabilities/cmd/script/probe.go +++ b/harnesses/rpc-capabilities/cmd/script/probe.go @@ -104,6 +104,9 @@ const ( // veChainStaleBlockGap: VeChain produces one block every ~10 s, // so 30 blocks ≈ 5 min. veChainStaleBlockGap uint64 = 30 + // iconStaleBlockGap: ICON produces one block every ~2 s, + // so 150 blocks ≈ 5 min. + iconStaleBlockGap uint64 = 150 ) // chainTips tracks the highest block seen for each chain across all @@ -328,6 +331,8 @@ func probeOne(ctx context.Context, c Chain, p Provider) { block, result, latency, err = callWavesBlock(probeCtx, p.URL) case "vechain": block, result, latency, err = callVeChainBlock(probeCtx, p.URL) + case "icon": + block, result, latency, err = callICONBlock(probeCtx, p.URL) default: block, hash, result, latency, err = callLatestBlock(probeCtx, p.URL) } @@ -385,6 +390,8 @@ func probeOne(ctx context.Context, c Chain, p Provider) { gap = wavesStaleBlockGap case "vechain": gap = veChainStaleBlockGap + case "icon": + gap = iconStaleBlockGap } if tip > 0 && block+gap < tip { result = "stale" @@ -406,7 +413,7 @@ func probeOne(ctx context.Context, c Chain, p Provider) { case "solana", "polkadot", "cosmos", "starknet", "stellar", "sui", "aptos", "xrpl", "algorand", "gram", "near", "flow", "hedera", "ckb", "multiversx", "neo", - "tezos", "antelope", "waves", "vechain", "dogecoin", "zcash", "bitcoin-cash", "litecoin": + "tezos", "antelope", "waves", "vechain", "icon", "dogecoin", "zcash", "bitcoin-cash", "litecoin": // no consensus participation default: if result == "ok" || result == "stale" { @@ -1465,6 +1472,56 @@ func callVeChainBlock(ctx context.Context, url string) (blockNum uint64, result return blk.Number, "ok", latencyMs, nil } +type iconLastBlock struct { + Result struct { + Height int64 `json:"height"` + } `json:"result"` +} + +// callICONBlock probes an ICON node via POST /api/v3 icx_getLastBlock. +// Returns the block height. Staleness uses iconStaleBlockGap in probeOne. +func callICONBlock(ctx context.Context, url string) (height uint64, result string, latencyMs float64, err error) { + target := strings.TrimRight(url, "/") + "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/api/v3" + body := []byte(fmt.Sprintf( + `{"jsonrpc":"2.0","method":"icx_getLastBlock","id":%d,"params":{}}`, + time.Now().UnixNano(), + )) + req, _ := http.NewRequestWithContext(ctx, "POST", target, bytes.NewReader(body)) + req.Header.Set("Content-Type", "application/json") + req.Header.Set("User-Agent", "OpenChainBench/1.0 (+https://openchainbench.com)") + client := &http.Client{Timeout: probeTimeout} + + start := time.Now() + resp, err := client.Do(req) + latencyMs = float64(time.Since(start).Nanoseconds()) / 1e6 + + if err != nil { + if ctx.Err() != nil || strings.Contains(err.Error(), "deadline exceeded") || strings.Contains(err.Error(), "Timeout") { + return 0, "timeout", latencyMs, err + } + return 0, "http_err", latencyMs, err + } + defer resp.Body.Close() + + if resp.StatusCode != 200 { + _, _ = io.Copy(io.Discard, resp.Body) + return 0, "http_err", latencyMs, fmt.Errorf("status %d", resp.StatusCode) + } + + raw, err := io.ReadAll(resp.Body) + if err != nil { + return 0, "http_err", latencyMs, err + } + var blk iconLastBlock + if err := json.Unmarshal(raw, &blk); err != nil { + return 0, "http_err", latencyMs, err + } + if blk.Result.Height <= 0 { + return 0, "jsonrpc_err", latencyMs, fmt.Errorf("icon block missing height") + } + return uint64(blk.Result.Height), "ok", latencyMs, nil +} + // callNeoBlockCount probes a NEO N3 node via getblockcount JSON-RPC. // The result is a plain decimal integer. Staleness uses neoStaleBlockGap in probeOne. func callNeoBlockCount(ctx context.Context, url string) (count uint64, result string, latencyMs float64, err error) { diff --git a/public/logos/icon.svg b/public/logos/icon.svg new file mode 100644 index 000000000..5d65cf529 --- /dev/null +++ b/public/logos/icon.svg @@ -0,0 +1,4 @@ + + + ICON + diff --git a/public/logos/iconblockchain.svg b/public/logos/iconblockchain.svg new file mode 100644 index 000000000..ca414b9c0 --- /dev/null +++ b/public/logos/iconblockchain.svg @@ -0,0 +1,5 @@ + + + ICON + BLOCKCHAIN + diff --git a/src/data/provider-registry.ts b/src/data/provider-registry.ts index dd0f2bd4e..c20e83126 100644 --- a/src/data/provider-registry.ts +++ b/src/data/provider-registry.ts @@ -2672,6 +2672,25 @@ export const PROVIDER_REGISTRY: Record = { }, + // ─── ICON providers (bench 258) ────────────────────────────────────── + "icon-solidwallet": { + url: "https://www.icondev.io", + description: + "ICON Foundation official public RPC node at ctz.solidwallet.io. Keyless icx_getLastBlock endpoint maintained by the ICON Foundation.", + twitter: "@helloiconworld", + }, + "icon-community": { + url: "https://icon.community", + description: + "ICON Community public RPC at api.icon.community. Keyless icx_getLastBlock endpoint run by the ICON community.", + twitter: "@helloiconworld", + }, + iconblockchain: { + url: "https://iconblockchain.xyz", + description: + "iconblockchain.xyz community-operated public ICON RPC node. Keyless icx_getLastBlock endpoint.", + }, + // ─── Union providers (bench 253) ───────────────────────────────────── "nodes-guru": { url: "https://nodes.guru", diff --git a/src/lib/brand.ts b/src/lib/brand.ts index 7ce6a49e1..b5d76ef28 100644 --- a/src/lib/brand.ts +++ b/src/lib/brand.ts @@ -195,6 +195,12 @@ const BRANDS: Record = { stakewolle: { color: "#F97316" }, // stakewolle orange nodestake: { color: "#8B5CF6" }, // nodestake violet + // ─── ICON (bench 258) ─── + icon: { color: "#00B8CC" }, // ICON teal (official brand) + "icon-solidwallet": { color: "#00B8CC" }, // ICON Foundation inherits brand teal + "icon-community": { color: "#1A9CBB" }, // ICON Community slightly darker teal + iconblockchain: { color: "#0D7A9E" }, // iconblockchain.xyz dark cyan + // ─── 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 e893db5ab..9c3f74ba7 100644 --- a/src/lib/logo-manifest.ts +++ b/src/lib/logo-manifest.ts @@ -343,6 +343,8 @@ const RAW: Record = { cheqd: "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/logos/cheqd.svg", stakewolle: "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/logos/stakewolle.svg", nodestake: "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/logos/nodestake.svg", + icon: "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/logos/icon.svg", + iconblockchain: "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/logos/iconblockchain.svg", // ─── Oracle deviation (bench 025) — additional brand logos ─── // (pairs alias to chain/asset logos in the ALIASES block below) @@ -758,6 +760,8 @@ const ALIASES: Record = { "mantrachain-official": "mantrachain", "band-official": "bandchain", "cheqd-official": "cheqd", + "icon-solidwallet": "icon", + "icon-community": "icon", // Non-EVM wave-3 (benches 222-231) — provider-official aliases to chain slug "ecadinfra": "tezos",