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
13 changes: 13 additions & 0 deletions benchmarks/asset-registry-coverage.yml
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,19 @@ providers:
sample_size: networks_supported_total{provider="coingecko"}
series: networks_supported_total{provider="coingecko"}

- slug: serialized
name: Serialized
tag: Own indexers, 18 EVM + Solana
formula: "Count of chains returned as live by Serialized's `/v1/meta/chains` endpoint, refreshed every 6 hours."
queries:
p50: networks_supported_total{provider="serialized"}
p90: networks_supported_total{provider="serialized"}
p99: networks_supported_total{provider="serialized"}
mean: networks_supported_total{provider="serialized"}
success: clamp_max(networks_supported_total{provider="serialized"} > bool 0, 1)
sample_size: networks_supported_total{provider="serialized"}
series: networks_supported_total{provider="serialized"}

- slug: coinpaprika
name: CoinPaprika
tag: Market-data API asset registry
Expand Down
13 changes: 13 additions & 0 deletions benchmarks/dex-network-coverage.yml
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,19 @@ providers:
sample_size: networks_supported_total{provider="geckoterminal"}
series: networks_supported_total{provider="geckoterminal"}

- slug: serialized
name: Serialized
tag: Own indexers, 18 EVM + Solana
formula: "Count of chains returned as live by Serialized's `/v1/meta/chains` endpoint; every listed chain carries DEX pool indexing. Refreshed every 6 hours."
queries:
p50: networks_supported_total{provider="serialized"}
p90: networks_supported_total{provider="serialized"}
p99: networks_supported_total{provider="serialized"}
mean: networks_supported_total{provider="serialized"}
success: clamp_max(networks_supported_total{provider="serialized"} > bool 0, 1)
sample_size: networks_supported_total{provider="serialized"}
series: networks_supported_total{provider="serialized"}

- slug: codex
name: Codex
tag: Defined.fi DEX data API
Expand Down
6 changes: 4 additions & 2 deletions harnesses/network-coverage/cmd/script/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ type Config struct {
CodexSessionCookie string // fallback path: mint JWT from Defined.fi cookie
DefinedTokenURL string // optional: pre-minted JWT sidecar
CoinStatsAPIKey string
SerializedAPIKey string
SimDuneAPIKey string // optional — Sim's public endpoint works keyless, but a key avoids rate limits
HTTPProxy string
RefreshInterval time.Duration
Expand All @@ -26,6 +27,7 @@ func loadConfig() *Config {
CodexSessionCookie: os.Getenv("DEFINED_SESSION_COOKIE"),
DefinedTokenURL: os.Getenv("DEFINED_TOKEN_SERVICE_URL"),
CoinStatsAPIKey: os.Getenv("COINSTATS_API_KEY"),
SerializedAPIKey: os.Getenv("SERIALIZED_API_KEY"),
SimDuneAPIKey: os.Getenv("SIM_DUNE_API_KEY"),
HTTPProxy: os.Getenv("HTTP_PROXY"),
RefreshInterval: 6 * time.Hour,
Expand All @@ -47,8 +49,8 @@ func loadConfig() *Config {
} else if c.CodexSessionCookie != "" {
codexAuth = "cookie+mint"
}
fmt.Printf("Config: refresh=%v, testnets=%v, mobula_key=%v, codex=%s, coinstats_key=%v, sim_dune_key=%v\n",
fmt.Printf("Config: refresh=%v, testnets=%v, mobula_key=%v, codex=%s, coinstats_key=%v, sim_dune_key=%v, serialized_key=%v\n",
c.RefreshInterval, c.IncludeTestnets, c.MobulaAPIKey != "", codexAuth,
c.CoinStatsAPIKey != "", c.SimDuneAPIKey != "")
c.CoinStatsAPIKey != "", c.SimDuneAPIKey != "", c.SerializedAPIKey != "")
return c
}
1 change: 1 addition & 0 deletions harnesses/network-coverage/cmd/script/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,7 @@ func fetchAll(cfg *Config) {
{"coinstats", fetchCoinStats},
{"coingecko", fetchCoinGecko},
{"dexpaprika", fetchDexPaprika},
{"serialized", fetchSerialized},
}

var wg sync.WaitGroup
Expand Down
80 changes: 80 additions & 0 deletions harnesses/network-coverage/cmd/script/serialized.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
package main

import (
"encoding/json"
"fmt"
"io"
"net/http"
"time"
)

// Serialized publishes its chain list at GET /v1/meta/chains. The endpoint
// is free (0 credits) and returns one row per chain with a `status` field.
//
// The same list answers both benches this harness feeds: Serialized runs its
// own indexers and does not separate "chains where we know tokens" from
// "chains where we index DEX pools" — every listed chain carries both. So
// the count is identical on bench 005 and bench 090 by construction, which
// is worth knowing when reading the two leaderboards side by side.
const serializedChainsURL = "https://api.serialized.xyz/v1/meta/chains"

type serializedChain struct {
Chain string `json:"chain"` // "evm:8453" or "solana"
Name string `json:"name"`
Slug string `json:"slug"`
Family string `json:"family"`
Status string `json:"status"`
}

type serializedChainsResponse struct {
Data []serializedChain `json:"data"`
}

func fetchSerialized(cfg *Config) ProviderResult {
res := ProviderResult{Provider: "serialized"}
if cfg.SerializedAPIKey == "" {
res.Err = "missing_api_key"
return res
}

client := &http.Client{Timeout: 15 * time.Second}
req, _ := http.NewRequest("GET", serializedChainsURL, nil)
// Raw key, no Bearer prefix — a prefixed key is rejected with 401.
req.Header.Set("Authorization", cfg.SerializedAPIKey)
req.Header.Set("Accept", "application/json")

resp, err := client.Do(req)
if err != nil {
res.Err = fmt.Sprintf("request_error: %v", err)
return res
}
defer resp.Body.Close()
body, _ := io.ReadAll(resp.Body)

if resp.StatusCode != 200 {
res.Err = fmt.Sprintf("status_%d", resp.StatusCode)
return res
}

var parsed serializedChainsResponse
if err := json.Unmarshal(body, &parsed); err != nil {
res.Err = fmt.Sprintf("parse_error: %v", err)
return res
}

for _, c := range parsed.Data {
// Only chains the provider declares live. Everything on this
// endpoint is mainnet, so no testnet filter is needed, but a
// future "beta"/"deprecated" status must not inflate the count.
if c.Status != "live" {
continue
}
res.Networks = append(res.Networks, Network{
ChainID: c.Chain,
Slug: c.Slug,
Name: c.Name,
})
}

return res
}
Loading