Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
16 commits
Select commit Hold shift + click to select a range
99bb913
fix(harnesses): cap LZ/CCIP/Axelar cardinality to bench-tracked chain…
Flotapponnier Aug 28, 2026
2708d1a
fix(cache): strip heavy fields from aggregate blob to pass 2MB limit …
Flotapponnier Aug 28, 2026
9039b8d
fix: include still-open Gains positions in carry projection (#2189)
Flotapponnier Aug 29, 2026
b37c7bb
fix: update Gains action names v5→v6, handle position size increases …
Flotapponnier Aug 29, 2026
26a1ef1
fix: Gains carry projection (v6 action names + still-open positions)
Flotapponnier Aug 29, 2026
9aec777
fix: widen coin name column for long tickers (xyz:NVDA)
Flotapponnier Aug 29, 2026
4ee8273
feat(rpc): add Union bench #253 (Nodes.Guru, Stake And Relax, High St…
Flotapponnier Aug 29, 2026
e1def89
fix(union-rpc): remove em dashes from YAML (#2196)
Flotapponnier Aug 29, 2026
9591ec1
feat(rpc): Shentu #254 + MANTRA Chain #255 (#2199)
Flotapponnier Aug 29, 2026
270e98b
feat: Band Protocol #256 + cheqd #257 benches
Flotapponnier Aug 29, 2026
1d3ad1c
fix: bench-blob revalidate 300s to fix perp ISR conflict (#2206)
Flotapponnier Aug 29, 2026
3914371
fix(fee-compare): clearer Gains labels — Position size + vault fee (#…
Flotapponnier Aug 29, 2026
f0b1bbd
fix(fee-compare): total position value + inline vault fee explanation…
Flotapponnier Aug 29, 2026
f0ac45f
fix(fee-compare): remove vault fee — carry fees settled per-action, n…
Flotapponnier Aug 29, 2026
2197a7d
feat(rpc): ICON bench #258 (#2210)
Flotapponnier Aug 30, 2026
a48ce5a
merge main into dev, keep ICON bench additions
Flotapponnier Aug 30, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
35 changes: 35 additions & 0 deletions benchmarks/icon-rpc.yml
Original file line number Diff line number Diff line change
@@ -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."
11 changes: 11 additions & 0 deletions harnesses/rpc-capabilities/cmd/script/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down
59 changes: 58 additions & 1 deletion harnesses/rpc-capabilities/cmd/script/probe.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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)
}
Expand Down Expand Up @@ -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"
Expand All @@ -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" {
Expand Down Expand Up @@ -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, "/") + "/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) {
Expand Down
4 changes: 4 additions & 0 deletions public/logos/icon.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
5 changes: 5 additions & 0 deletions public/logos/iconblockchain.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
18 changes: 18 additions & 0 deletions src/data/provider-registry.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2672,6 +2672,24 @@ export const PROVIDER_REGISTRY: Record<string, ProviderRegistryEntry> = {
},


// ─── 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",
Expand Down
6 changes: 6 additions & 0 deletions src/lib/brand.ts
Original file line number Diff line number Diff line change
Expand Up @@ -195,6 +195,12 @@ const BRANDS: Record<string, Brand> = {
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
Expand Down
4 changes: 4 additions & 0 deletions src/lib/logo-manifest.ts
Original file line number Diff line number Diff line change
Expand Up @@ -343,6 +343,8 @@ const RAW: Record<string, string> = {
cheqd: "/logos/cheqd.svg",
stakewolle: "/logos/stakewolle.svg",
nodestake: "/logos/nodestake.svg",
icon: "/logos/icon.svg",
iconblockchain: "/logos/iconblockchain.svg",

// ─── Oracle deviation (bench 025) — additional brand logos ───
// (pairs alias to chain/asset logos in the ALIASES block below)
Expand Down Expand Up @@ -758,6 +760,8 @@ const ALIASES: Record<string, string> = {
"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",
Expand Down
Loading