Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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: 6 additions & 29 deletions benchmarks/cronos-zkevm-rpc.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ slug: cronos-zkevm-rpc
number: "193"
title: Fastest free Cronos zkEVM RPC, live no-key endpoint latency
seo_title: "Fastest free Cronos zkEVM RPC 2026"
seo_description: "{{best_name}} leads free Cronos zkEVM RPC at {{best_p50}} (eth_getBlockByNumber p50, 24h). 3 no-key providers measured every 60s from 3 regions."
seo_description: "{{best_name}} leads free Cronos zkEVM RPC at {{best_p50}} (eth_getBlockByNumber p50, 24h). 2 no-key providers measured every 60s from 3 regions."
subtitle: HTTP round-trip latency for eth_getBlockByNumber against every free, no-key public Cronos zkEVM endpoint, audited every 60 seconds from 3 regions.

category: RPCs
Expand All @@ -14,7 +14,7 @@ unit: ms
higher_is_better: false

seo_intro: |
Cronos zkEVM (chain 388) is Crypto.com's zkEVM Layer 2 built on the zkSync Era stack, distinct from Cronos EVM. CRO is the bridged gas token. Three keyless endpoints are probed continuously: the Cronos official mainnet gateway (mainnet.zkevm.cronos.org), dRPC (cronos-zkevm.drpc.org), and Blast API.
Cronos zkEVM (chain 388) is Crypto.com's zkEVM Layer 2 built on the zkSync Era stack, distinct from Cronos EVM. CRO is the bridged gas token. Two keyless endpoints are probed continuously: the Cronos official mainnet gateway (mainnet.zkevm.cronos.org) and dRPC (cronos-zkevm.drpc.org).

abstract: |
Per-chain member of the RPC latency cluster, extended to Cronos zkEVM.
Expand All @@ -31,16 +31,16 @@ methodology:
- "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` (HTTP 200 + non-empty result), `http_err`, `jsonrpc_err`, `stale` (more than 20 blocks behind the cross-provider tip), `timeout`."
- "This page is part of the per-chain RPC cluster derived from the cross-chain [rpc-capabilities](https://openchainbench.com/benchmarks/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=\"cronos-zkevm\"`. Provider coverage at launch: 3 no-key endpoints."
- "Chain scope: every query on this page is pinned to `chain=\"cronos-zkevm\"`. Provider coverage at launch: 2 no-key endpoints."

findings:
- "{{best_name}} currently leads free Cronos zkEVM RPC at {{best_p50}} (`eth_getBlockByNumber` p50, 24h) across 3 measured providers."
- "{{best_name}} currently leads free Cronos zkEVM RPC at {{best_p50}} (`eth_getBlockByNumber` p50, 24h) across 2 measured providers."

faq:
- q: "What is the fastest free Cronos zkEVM RPC right now?"
a: "{{best_name}} currently leads at {{best_p50}} (`eth_getBlockByNumber` p50 over the last 24h), measured against 3 no-key providers probed every 60 seconds from us-east, eu-west and Singapore."
a: "{{best_name}} currently leads at {{best_p50}} (`eth_getBlockByNumber` p50 over the last 24h), measured against 2 no-key providers probed every 60 seconds from us-east, eu-west and Singapore."
- q: "Which Cronos zkEVM RPCs work without an API key?"
a: "3 endpoints sustain continuous keyless probing: Cronos official, dRPC, and Blast API. Every endpoint was live-verified before inclusion."
a: "2 endpoints sustain continuous keyless probing: Cronos official and dRPC. Every endpoint was live-verified before inclusion."
- q: "Does the fastest Cronos zkEVM RPC change by region?"
a: "Often. The region tabs at the top of the page re-scope every number to a single origin."
- q: "How is Cronos zkEVM RPC latency measured here?"
Expand Down Expand Up @@ -107,26 +107,3 @@ providers:
- region: ap-southeast
p50: avg(ocb:rpc_latency_milliseconds:p50_24h{provider="drpc", chain="cronos-zkevm", region="sgp"})
series: avg_over_time(rpc_latency_milliseconds{provider="drpc", chain="cronos-zkevm", region="sgp"}[1h])

- slug: blastapi
name: Blast API
tag: Blast API public gateway for Cronos zkEVM
formula: "50th percentile over 24h of client-side round-trip latency (ms) for a single `eth_getBlockByNumber` POST sent every 60s from 3 regions to Blast API's no-key Cronos zkEVM endpoint."
queries:
p50: avg(ocb:rpc_latency_milliseconds:p50_24h{provider="blastapi", chain="cronos-zkevm"})
p90: avg(ocb:rpc_latency_milliseconds:p90_24h{provider="blastapi", chain="cronos-zkevm"})
p99: avg(ocb:rpc_latency_milliseconds:p99_24h{provider="blastapi", chain="cronos-zkevm"})
mean: avg(ocb:rpc_latency_milliseconds:mean_24h{provider="blastapi", chain="cronos-zkevm"})
success: sum(ocb:rpc_call:ok_rate_24h{provider="blastapi", chain="cronos-zkevm"}) / sum(ocb:rpc_call:rate_24h{provider="blastapi", chain="cronos-zkevm"})
sample_size: sum(ocb:rpc_call:increase_24h{provider="blastapi", chain="cronos-zkevm"})
series: avg(avg_over_time(rpc_latency_milliseconds{provider="blastapi", chain="cronos-zkevm"}[1h]))
regions:
- region: us-east
p50: avg(ocb:rpc_latency_milliseconds:p50_24h{provider="blastapi", chain="cronos-zkevm", region="us-east"})
series: avg_over_time(rpc_latency_milliseconds{provider="blastapi", chain="cronos-zkevm", region="us-east"}[1h])
- region: eu-west
p50: avg(ocb:rpc_latency_milliseconds:p50_24h{provider="blastapi", chain="cronos-zkevm", region="eu-west"})
series: avg_over_time(rpc_latency_milliseconds{provider="blastapi", chain="cronos-zkevm", region="eu-west"}[1h])
- region: ap-southeast
p50: avg(ocb:rpc_latency_milliseconds:p50_24h{provider="blastapi", chain="cronos-zkevm", region="sgp"})
series: avg_over_time(rpc_latency_milliseconds{provider="blastapi", chain="cronos-zkevm", region="sgp"}[1h])
3 changes: 2 additions & 1 deletion harnesses/pm-rate-limits/cmd/script/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ func main() {
log.SetFlags(0)
log.Printf("[pm-rate-limits] starting, region=%s ramp_hour=%02d:00 UTC build=%s", currentRegion, rampHour(), buildMarker)

initProxy()
cfg := loadConfig()

go func() {
Expand All @@ -40,7 +41,7 @@ func main() {
stateByVenue[v.Slug] = v.state
go initialPin(ctx, v, pinClient)
go repinLoop(ctx, v, v.state, pinClient)
warmClient := &http.Client{Transport: &http.Transport{MaxIdleConnsPerHost: 4, IdleConnTimeout: 90 * time.Second}}
warmClient := &http.Client{Transport: makeTransport(v.UseProxy)}
for _, cl := range v.Classes {
go warmLoop(ctx, v, cl, v.state, warmClient)
}
Expand Down
2 changes: 1 addition & 1 deletion harnesses/pm-rate-limits/cmd/script/probe.go
Original file line number Diff line number Diff line change
Expand Up @@ -283,7 +283,7 @@ func coldLoop(ctx context.Context, v *Venue, st *venueState) {
if st.getPin().Market == "" {
continue
}
tr := &http.Transport{DisableKeepAlives: true}
tr := makeTransport(v.UseProxy, func(t *http.Transport) { t.DisableKeepAlives = true })
client := &http.Client{Transport: tr}
res := probeOnce(ctx, client, v, cl, "cold")
tr.CloseIdleConnections()
Expand Down
43 changes: 43 additions & 0 deletions harnesses/pm-rate-limits/cmd/script/proxy.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
package main

import (
"log"
"net/http"
"net/url"
"os"
"time"
)

// proxyURL is parsed once at startup from PROBE_PROXY_URL. Nil means no proxy.
var proxyURL *url.URL

func initProxy() {
raw := os.Getenv("PROBE_PROXY_URL")
if raw == "" {
return
}
u, err := url.Parse(raw)
if err != nil {
log.Printf("[proxy] bad PROBE_PROXY_URL %q: %v", raw, err)
return
}
proxyURL = u
log.Printf("[proxy] rotating proxy configured: %s://%s@%s", u.Scheme, u.User.Username(), u.Host)
}

// makeTransport returns a transport with optional proxy and any extra tweaks
// applied via functional options. Call with useProxy=true for venues blocked
// from Railway IPs.
func makeTransport(useProxy bool, opts ...func(*http.Transport)) *http.Transport {
tr := &http.Transport{
MaxIdleConnsPerHost: 4,
IdleConnTimeout: 90 * time.Second,
}
if useProxy && proxyURL != nil {
tr.Proxy = http.ProxyURL(proxyURL)
}
for _, opt := range opts {
opt(tr)
}
return tr
}
2 changes: 1 addition & 1 deletion harnesses/pm-rate-limits/cmd/script/ramp.go
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,7 @@ func runRamp(ctx context.Context, v *Venue) {
}
cl := v.Classes[0]
url := cl.URL(pin)
client := &http.Client{Transport: &http.Transport{MaxIdleConnsPerHost: 32}}
client := &http.Client{Transport: makeTransport(v.UseProxy, func(t *http.Transport) { t.MaxIdleConnsPerHost = 32 })}
log.Printf("[ramp][%s] start, baseline p50=%.0fms (n=%d), target %s", v.Slug, baseP50*1000, n, url)

for _, rate := range v.RampRates {
Expand Down
6 changes: 4 additions & 2 deletions harnesses/pm-rate-limits/cmd/script/venues.go
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ type Venue struct {
StopOn429 bool // Kalshi: documented token bucket, stop at first 429
InvalidBody []string // body substrings that mark a probe_invalid (stale pin)
RequestMutator func(*http.Request) // optional: stamps auth/custom headers before send
UseProxy bool // route all requests for this venue through PROBE_PROXY_URL

state *venueState // wired at startup
}
Expand Down Expand Up @@ -155,6 +156,7 @@ func venues(cfg Config) []*Venue {
PinFunc: pinPredictIt,
RampRates: nil, // rate-limited: no ramp; probing at their published cap already
StopOn429: true,
UseProxy: true, // Railway IPs blocked by PredictIt
},
{
Slug: "smarkets",
Expand All @@ -169,12 +171,12 @@ func venues(cfg Config) []*Venue {
}},
{Name: "list", Interval: 30 * time.Second, Timeout: 15 * time.Second,
URL: func(Pin) string {
// Popular events listing, no event-ID dependency for the list probe
return "https://api.smarkets.com/v3/events/?type_domain=politics&sort=popular&per_page=20&state=upcoming"
return "https://api.smarkets.com/v3/events/?type_domain=politics&sort=start_datetime%2Cid&per_page=20&state=upcoming"
}},
},
PinFunc: pinSmarkets,
RampRates: []int{10, 25, 50},
UseProxy: true, // Railway IPs blocked by Smarkets
},
// Metaculus: public forecasting platform, no auth, no order book.
// Primary class is price (single question endpoint). Conservative ramp
Expand Down
39 changes: 39 additions & 0 deletions harnesses/rpc-capabilities/cmd/script/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ package main
import (
"context"
"fmt"
"net/http"
"net/url"
"os"
"os/signal"
"strings"
Expand Down Expand Up @@ -59,8 +61,45 @@ func normalizeRailwayRegion(raw string) string {
}
}

// initSelectiveProxy patches http.DefaultTransport so requests to hosts listed
// in PROBE_PROXY_HOSTS are routed through PROBE_PROXY_URL. All other requests
// go direct. This lets us unblock providers that ban Railway datacenter IPs
// without corrupting latency measurements for providers that don't.
func initSelectiveProxy() {
raw := strings.TrimSpace(os.Getenv("PROBE_PROXY_URL"))
if raw == "" {
return
}
proxyURL, err := url.Parse(raw)
if err != nil {
fmt.Printf("[proxy] bad PROBE_PROXY_URL: %v\n", err)
return
}
hostsRaw := strings.Split(os.Getenv("PROBE_PROXY_HOSTS"), ",")
hostSet := make(map[string]bool, len(hostsRaw))
for _, h := range hostsRaw {
if h = strings.TrimSpace(h); h != "" {
hostSet[h] = true
}
}
if len(hostSet) == 0 {
fmt.Println("[proxy] PROBE_PROXY_URL set but PROBE_PROXY_HOSTS is empty — skipping")
return
}
dt := http.DefaultTransport.(*http.Transport).Clone()
dt.Proxy = func(r *http.Request) (*url.URL, error) {
if hostSet[r.URL.Hostname()] {
return proxyURL, nil
}
return nil, nil
}
http.DefaultTransport = dt
fmt.Printf("[proxy] selective proxy active for %d host(s): %v\n", len(hostSet), hostsRaw)
}

func main() {
installLogCapture() // capture stdout into /logs ring buffer
initSelectiveProxy()
fmt.Println("=== RPC Capabilities Harness ===")
fmt.Println("OpenChainBench - public RPC latency, reliability, and archive depth.")
fmt.Printf("Region: %s (set via $REGION env)\n", currentRegion)
Expand Down
4 changes: 2 additions & 2 deletions src/lib/sitemap-builder.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { readFileSync, statSync } from "node:fs";
import path from "node:path";
import type { MetadataRoute } from "next";
import { getBenchmarks } from "@/data/benchmarks";
import { getBenchmarksSafe } from "@/data/benchmarks";
import { getAllReports, getAllReportCategories } from "@/lib/reports/loader";
import { COMPARE_PAIRS } from "@/data/compare-pairs";
import { adHocPairs } from "@/lib/compare/adhoc-pairs";
Expand Down Expand Up @@ -209,7 +209,7 @@ async function buildStaticFallback(): Promise<MetadataRoute.Sitemap> {

async function buildFullSitemap(): Promise<MetadataRoute.Sitemap> {
const [benchmarks, alternatives, answers, providerSlugs] = await Promise.all([
safeLoad<Benchmark[]>("benchmarks", () => getBenchmarks(), []),
safeLoad<Benchmark[]>("benchmarks", () => getBenchmarksSafe(), []),
safeLoad<Awaited<ReturnType<typeof loadAllAlternatives>>>(
"alternatives",
() => loadAllAlternatives(),
Expand Down
Loading