Skip to main content

x402_types/
networks.rs

1//! Known blockchain networks and CAIP-2 chain ID management.
2//!
3//! This module provides a comprehensive registry of well-known blockchain networks with their
4//! corresponding CAIP-2 (Chain Agnostic Improvement Proposal 2) chain identifiers. It is designed
5//! to improve developer experience (DX) when working with the x402 protocol, which operates on
6//! CAIP-2 chain IDs.
7//!
8//! # x402 v1 Protocol Relevance
9//!
10//! **This module is primarily relevant for x402 v1 protocol compatibility.** The registry of
11//! known networks represents the set of blockchain networks that were supported in x402 v1.
12//! For x402 v2 and beyond, the protocol is designed to work with any CAIP-2 chain ID without
13//! requiring a predefined registry.
14//!
15//! Despite being v1-focused, this module continues to provide value for improved developer
16//! experience by offering convenient methods to work with well-known networks without manually
17//! constructing CAIP-2 identifiers.
18//!
19//! # Purpose
20//!
21//! This module serves two main purposes:
22//! 1. **x402 v1 Protocol Compatibility**: Maintains support for networks that were known in v1
23//! 2. **Better Developer Experience**: Provides convenient methods to work with well-known networks
24//!    without manually constructing CAIP-2 identifiers
25//!
26//! # Usage Across the Codebase
27//!
28//! This module is used in several ways throughout the x402 ecosystem:
29//!
30//! - **ChainId Methods**: The [`ChainId::from_network_name()`](crate::chain::ChainId::from_network_name)
31//!   and [`ChainId::as_network_name()`](crate::chain::ChainId::as_network_name) methods use this
32//!   module for convenient network name lookups
33//! - **Chain-Specific Traits**: Chain-specific crates (e.g., `x402-chain-eip155`, `x402-chain-solana`)
34//!   implement namespace-specific traits like [`KnownNetworkEip155`] and [`KnownNetworkSolana`]
35//!   for type-safe network access
36//! - **Token Deployments**: The [`USDC`] and [`SBC`] marker structs are used by chain-specific
37//!   crates to provide per-network token deployment information (e.g., token addresses on
38//!   different chains)
39//!
40//! # CAIP-2 Standard
41//!
42//! CAIP-2 is a standard for identifying blockchain networks in a chain-agnostic way. A CAIP-2
43//! chain ID consists of two parts separated by a colon:
44//! - **Namespace**: The blockchain ecosystem (e.g., "eip155" for EVM, "solana" for Solana)
45//! - **Reference**: The chain-specific identifier (e.g., "8453" for Base, "137" for Polygon)
46//!
47//! For more information, see: https://chainagnostic.org/CAIPs/caip-2
48//!
49//! # Module Contents
50//!
51//! - [`NetworkInfo`]: A struct representing a known network with its name, namespace, and reference
52//! - [`KnownNetworkEip155`]: Trait for convenient access to EVM networks (eip155 namespace)
53//! - [`KnownNetworkSolana`]: Trait for convenient access to Solana networks
54//! - [`KNOWN_NETWORKS`]: A static array of all well-known networks
55//! - [`chain_id_by_network_name`]: Lookup function to get ChainId by network name
56//! - [`network_name_by_chain_id`]: Reverse lookup function to get network name by ChainId
57//! - [`USDC`] and [`SBC`]: Marker structs used for token deployment implementations
58//!
59//! # Namespace-Specific Traits
60//!
61//! The module provides namespace-specific traits for better organization and flexibility:
62//!
63//! ## KnownNetworkEip155
64//! Provides convenient static methods for all EVM networks (eip155 namespace):
65//! - Base, Base Sepolia
66//! - Polygon, Polygon Amoy
67//! - Avalanche, Avalanche Fuji
68//! - Sei, Sei Testnet
69//! - XDC, XRPL EVM, Peaq, IoTeX
70//! - Celo, Celo Sepolia
71//! - Radius, Radius Testnet
72//!
73//! ## KnownNetworkSolana
74//! Provides convenient static methods for Solana networks:
75//! - Solana mainnet
76//! - Solana devnet
77//!
78//! # Supported Networks
79//!
80//! The module supports 18 blockchain networks across two namespaces:
81//! - **EVM Networks (16)**: All networks in the eip155 namespace
82//! - **Solana Networks (2)**: Solana mainnet and devnet
83//!
84//! # Examples
85//!
86//! ```
87//! use x402_types::chain::ChainId;
88//! use x402_types::networks::chain_id_by_network_name;
89//!
90//! // Using lookup functions
91//! let polygon = chain_id_by_network_name("polygon").unwrap();
92//! assert_eq!(polygon.namespace, "eip155");
93//! assert_eq!(polygon.reference, "137");
94//!
95//! // Using ChainId::from_network_name
96//! let base = ChainId::from_network_name("base").unwrap();
97//! assert_eq!(base.namespace, "eip155");
98//! assert_eq!(base.reference, "8453");
99//!
100//! // Reverse lookup
101//! let chain_id = ChainId::new("solana", "5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp");
102//! assert_eq!(chain_id.as_network_name(), Some("solana"));
103//! ```
104
105use std::collections::HashMap;
106use std::sync::LazyLock;
107
108use crate::chain::ChainId;
109
110/// A known network definition with its chain ID and human-readable name.
111#[derive(Debug, Clone, PartialEq, Eq)]
112pub struct NetworkInfo {
113    /// Human-readable network name (e.g., "base-sepolia", "solana")
114    pub name: &'static str,
115    /// CAIP-2 namespace (e.g., "eip155", "solana")
116    pub namespace: &'static str,
117    /// Chain reference (e.g., "84532" for Base Sepolia, "5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp" for Solana mainnet)
118    pub reference: &'static str,
119}
120
121impl NetworkInfo {
122    /// Create a ChainId from this network info
123    pub fn chain_id(&self) -> ChainId {
124        ChainId::new(self.namespace, self.reference)
125    }
126}
127
128/// A static array of well-known blockchain networks.
129///
130/// This array contains a registry of well-known blockchain networks for improved
131/// developer experience and x402 protocol v1 compatibility, organized by ecosystem
132/// (EVM networks first, then Solana networks). Each entry includes the network's
133/// human-readable name, CAIP-2 namespace, and chain reference.
134///
135/// # x402 v1 Protocol Relevance
136///
137/// This registry represents the set of blockchain networks that were known and supported
138/// in x402 v1. For x402 v2 and beyond, the protocol is designed to work with any CAIP-2
139/// chain ID without requiring a predefined registry.
140///
141/// The array is used to populate the lazy-initialized lookup hashmaps:
142/// - [`NAME_TO_CHAIN_ID`] for name-based lookups
143/// - [`CHAIN_ID_TO_NAME`] for ChainId-based lookups
144///
145/// # Developer Experience Benefits
146///
147/// Despite being v1-focused, this registry continues to provide value by:
148/// - Enabling convenient network name lookups via [`ChainId::from_network_name()`](crate::chain::ChainId::from_network_name)
149/// - Providing human-readable network names via [`ChainId::as_network_name()`](crate::chain::ChainId::as_network_name)
150/// - Serving as a reference for commonly used blockchain networks
151pub static KNOWN_NETWORKS: &[NetworkInfo] = &[
152    // EVM Networks
153    // Base
154    NetworkInfo {
155        name: "base",
156        namespace: "eip155",
157        reference: "8453",
158    },
159    NetworkInfo {
160        name: "base-sepolia",
161        namespace: "eip155",
162        reference: "84532",
163    },
164    // Polygon
165    NetworkInfo {
166        name: "polygon",
167        namespace: "eip155",
168        reference: "137",
169    },
170    NetworkInfo {
171        name: "polygon-amoy",
172        namespace: "eip155",
173        reference: "80002",
174    },
175    // Avalanche
176    NetworkInfo {
177        name: "avalanche",
178        namespace: "eip155",
179        reference: "43114",
180    },
181    NetworkInfo {
182        name: "avalanche-fuji",
183        namespace: "eip155",
184        reference: "43113",
185    },
186    // Sei
187    NetworkInfo {
188        name: "sei",
189        namespace: "eip155",
190        reference: "1329",
191    },
192    NetworkInfo {
193        name: "sei-testnet",
194        namespace: "eip155",
195        reference: "1328",
196    },
197    // XDC
198    NetworkInfo {
199        name: "xdc",
200        namespace: "eip155",
201        reference: "50",
202    },
203    // XRPL EVM
204    NetworkInfo {
205        name: "xrpl-evm",
206        namespace: "eip155",
207        reference: "1440000",
208    },
209    // Peaq
210    NetworkInfo {
211        name: "peaq",
212        namespace: "eip155",
213        reference: "3338",
214    },
215    // IoTeX
216    NetworkInfo {
217        name: "iotex",
218        namespace: "eip155",
219        reference: "4689",
220    },
221    // Celo Networks
222    NetworkInfo {
223        name: "celo",
224        namespace: "eip155",
225        reference: "42220",
226    },
227    NetworkInfo {
228        name: "celo-sepolia",
229        namespace: "eip155",
230        reference: "11142220",
231    },
232    // Radius Networks
233    NetworkInfo {
234        name: "radius",
235        namespace: "eip155",
236        reference: "723487",
237    },
238    NetworkInfo {
239        name: "radius-testnet",
240        namespace: "eip155",
241        reference: "72344",
242    },
243    // Solana Networks
244    NetworkInfo {
245        name: "solana",
246        namespace: "solana",
247        reference: "5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp",
248    },
249    NetworkInfo {
250        name: "solana-devnet",
251        namespace: "solana",
252        reference: "EtWTRABZaYq6iMfeYKouRu166VU2xqa1",
253    },
254];
255
256/// Lazy-initialized hashmap for network name to ChainId lookups.
257///
258/// Maps human-readable network names (e.g., "base", "polygon", "solana") to their
259/// corresponding [`ChainId`] instances. This hashmap is populated once on first access
260/// from the [`KNOWN_NETWORKS`] array.
261///
262/// # x402 v1 Protocol Relevance
263///
264/// This hashmap provides the network name lookup functionality that was used in x402 v1.
265/// For x402 v2 and beyond, the protocol is designed to work with any CAIP-2 chain ID
266/// without requiring a predefined registry.
267///
268/// # Developer Experience Benefits
269///
270/// Despite being v1-focused, this hashmap continues to provide value by enabling
271/// convenient network name lookups via [`ChainId::from_network_name()`](crate::chain::ChainId::from_network_name).
272///
273/// # Examples
274///
275/// ```
276/// use x402_types::networks::chain_id_by_network_name;
277///
278/// let base = chain_id_by_network_name("base").unwrap();
279/// assert_eq!(base.namespace, "eip155");
280/// assert_eq!(base.reference, "8453");
281/// ```
282pub static NAME_TO_CHAIN_ID: LazyLock<HashMap<&'static str, ChainId>> = LazyLock::new(|| {
283    KNOWN_NETWORKS
284        .iter()
285        .map(|n| (n.name, n.chain_id()))
286        .collect()
287});
288
289/// Lazy-initialized hashmap for ChainId to network name lookups.
290///
291/// Maps [`ChainId`] instances to their human-readable network names. This hashmap is
292/// populated once on first access from the [`KNOWN_NETWORKS`] array. Useful for
293/// reverse lookups when you have a ChainId and need to find its network name.
294///
295/// # x402 v1 Protocol Relevance
296///
297/// This hashmap provides the reverse lookup functionality that was used in x402 v1.
298/// For x402 v2 and beyond, the protocol is designed to work with any CAIP-2 chain ID
299/// without requiring a predefined registry.
300///
301/// # Developer Experience Benefits
302///
303/// Despite being v1-focused, this hashmap continues to provide value by enabling
304/// human-readable network name lookups via [`ChainId::as_network_name()`](crate::chain::ChainId::as_network_name).
305///
306/// # Examples
307///
308/// ```
309/// use x402_types::chain::ChainId;
310/// use x402_types::networks::network_name_by_chain_id;
311///
312/// let chain_id = ChainId::new("eip155", "137");
313/// let name = network_name_by_chain_id(&chain_id).unwrap();
314/// assert_eq!(name, "polygon");
315/// ```
316pub static CHAIN_ID_TO_NAME: LazyLock<HashMap<ChainId, &'static str>> = LazyLock::new(|| {
317    KNOWN_NETWORKS
318        .iter()
319        .map(|n| (n.chain_id(), n.name))
320        .collect()
321});
322
323/// Retrieves a ChainId by its network name.
324///
325/// Performs a lookup in the [`NAME_TO_CHAIN_ID`] hashmap to find the ChainId
326/// corresponding to the given network name. The lookup is case-sensitive.
327///
328/// # x402 v1 Protocol Relevance
329///
330/// This function provides the network name lookup functionality that was used in x402 v1.
331/// For x402 v2 and beyond, the protocol is designed to work with any CAIP-2 chain ID
332/// without requiring a predefined registry.
333///
334/// # Developer Experience Benefits
335///
336/// Despite being v1-focused, this function continues to provide value by enabling
337/// convenient network name lookups. It is used by [`ChainId::from_network_name()`](crate::chain::ChainId::from_network_name)
338/// to provide a developer-friendly API for creating ChainId instances.
339///
340/// # Arguments
341///
342/// * `name` - The human-readable network name (e.g., "base", "polygon-amoy", "solana")
343///
344/// # Returns
345///
346/// Returns `Some(&ChainId)` if the network name is found, or `None` if the name
347/// is not in the known networks registry.
348///
349/// # Examples
350///
351/// ```
352/// use x402_types::networks::chain_id_by_network_name;
353///
354/// let base = chain_id_by_network_name("base").unwrap();
355/// assert_eq!(base.namespace, "eip155");
356/// assert_eq!(base.reference, "8453");
357///
358/// assert!(chain_id_by_network_name("unknown-network").is_none());
359/// ```
360pub fn chain_id_by_network_name(name: &str) -> Option<&ChainId> {
361    NAME_TO_CHAIN_ID.get(name)
362}
363
364/// Retrieves a network name by its ChainId.
365///
366/// Performs a reverse lookup in the [`CHAIN_ID_TO_NAME`] hashmap to find the
367/// human-readable network name corresponding to the given ChainId.
368///
369/// # x402 v1 Protocol Relevance
370///
371/// This function provides the reverse lookup functionality that was used in x402 v1.
372/// For x402 v2 and beyond, the protocol is designed to work with any CAIP-2 chain ID
373/// without requiring a predefined registry.
374///
375/// # Developer Experience Benefits
376///
377/// Despite being v1-focused, this function continues to provide value by enabling
378/// human-readable network name lookups. It is used by [`ChainId::as_network_name()`](crate::chain::ChainId::as_network_name)
379/// to provide a developer-friendly API for displaying network names.
380///
381/// # Arguments
382///
383/// * `chain_id` - A reference to the ChainId to look up
384///
385/// # Returns
386///
387/// Returns `Some(&'static str)` containing the network name if the ChainId is found,
388/// or `None` if the ChainId is not in the known networks registry.
389///
390/// # Examples
391///
392/// ```
393/// use x402_types::chain::ChainId;
394/// use x402_types::networks::network_name_by_chain_id;
395///
396/// let chain_id = ChainId::new("eip155", "8453");
397/// let name = network_name_by_chain_id(&chain_id).unwrap();
398/// assert_eq!(name, "base");
399///
400/// let unknown = ChainId::new("eip155", "999999");
401/// assert!(network_name_by_chain_id(&unknown).is_none());
402/// ```
403pub fn network_name_by_chain_id(chain_id: &ChainId) -> Option<&'static str> {
404    CHAIN_ID_TO_NAME.get(chain_id).copied()
405}
406
407/// Marker struct for USDC token deployment implementations.
408///
409/// This struct is used as a type parameter for chain-specific traits (e.g., `KnownNetworkEip155`,
410/// `KnownNetworkSolana`) to provide per-network USDC token deployment information.
411///
412/// # Usage
413///
414/// Chain-specific crates implement traits for this marker struct to provide USDC token
415/// deployments on different networks. For example:
416///
417/// - `x402-chain-eip155` implements `KnownNetworkEip155<Eip155TokenDeployment>` for `USDC`
418/// - `x402-chain-solana` implements `KnownNetworkSolana<SolanaTokenDeployment>` for `USDC`
419/// - `x402-chain-aptos` implements `KnownNetworkAptos<AptosTokenDeployment>` for `USDC`
420///
421/// # Example
422///
423/// ```ignore
424/// use x402_chain_eip155::{KnownNetworkEip155, Eip155TokenDeployment};
425/// use x402_types::networks::USDC;
426///
427/// // Get USDC deployment on Base mainnet
428/// let usdc_base: Eip155TokenDeployment = USDC::base();
429/// assert_eq!(usdc_base.chain_reference.value(), 8453);
430/// ```
431#[allow(dead_code, clippy::upper_case_acronyms)] // Public for consumption by downstream crates.
432pub struct USDC;
433
434/// Marker struct for SBC token deployment implementations.
435///
436/// Chain-specific crates implement traits for this marker struct to provide
437/// per-network SBC token deployment information.
438#[allow(dead_code, clippy::upper_case_acronyms)] // Public for consumption by downstream crates.
439pub struct SBC;
440
441#[cfg(test)]
442mod tests {
443    use super::*;
444
445    #[test]
446    fn test_chain_id_from_network_name() {
447        let base = chain_id_by_network_name("base").unwrap();
448        assert_eq!(base.namespace, "eip155");
449        assert_eq!(base.reference, "8453");
450
451        let base_sepolia = chain_id_by_network_name("base-sepolia").unwrap();
452        assert_eq!(base_sepolia.namespace, "eip155");
453        assert_eq!(base_sepolia.reference, "84532");
454
455        let polygon = chain_id_by_network_name("polygon").unwrap();
456        assert_eq!(polygon.namespace, "eip155");
457        assert_eq!(polygon.reference, "137");
458
459        let celo = chain_id_by_network_name("celo").unwrap();
460        assert_eq!(celo.namespace, "eip155");
461        assert_eq!(celo.reference, "42220");
462
463        let radius = chain_id_by_network_name("radius").unwrap();
464        assert_eq!(radius.namespace, "eip155");
465        assert_eq!(radius.reference, "723487");
466
467        let radius_testnet = chain_id_by_network_name("radius-testnet").unwrap();
468        assert_eq!(radius_testnet.namespace, "eip155");
469        assert_eq!(radius_testnet.reference, "72344");
470
471        let solana = chain_id_by_network_name("solana").unwrap();
472        assert_eq!(solana.namespace, "solana");
473        assert_eq!(solana.reference, "5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp");
474
475        assert!(chain_id_by_network_name("unknown").is_none());
476    }
477
478    #[test]
479    fn test_network_name_by_chain_id() {
480        let chain_id = ChainId::new("eip155", "8453");
481        let network_name = network_name_by_chain_id(&chain_id).unwrap();
482        assert_eq!(network_name, "base");
483
484        let celo_chain_id = ChainId::new("eip155", "42220");
485        let network_name = network_name_by_chain_id(&celo_chain_id).unwrap();
486        assert_eq!(network_name, "celo");
487
488        let celo_sepolia_chain_id = ChainId::new("eip155", "11142220");
489        let network_name = network_name_by_chain_id(&celo_sepolia_chain_id).unwrap();
490        assert_eq!(network_name, "celo-sepolia");
491
492        let radius_chain_id = ChainId::new("eip155", "723487");
493        let network_name = network_name_by_chain_id(&radius_chain_id).unwrap();
494        assert_eq!(network_name, "radius");
495
496        let radius_testnet_chain_id = ChainId::new("eip155", "72344");
497        let network_name = network_name_by_chain_id(&radius_testnet_chain_id).unwrap();
498        assert_eq!(network_name, "radius-testnet");
499
500        let solana_chain_id = ChainId::new("solana", "5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp");
501        let network_name = network_name_by_chain_id(&solana_chain_id).unwrap();
502        assert_eq!(network_name, "solana");
503
504        let unknown_chain_id = ChainId::new("eip155", "999999");
505        assert!(network_name_by_chain_id(&unknown_chain_id).is_none());
506    }
507
508    #[test]
509    fn test_chain_id_as_network_name() {
510        let chain_id = ChainId::new("eip155", "8453");
511        assert_eq!(chain_id.as_network_name(), Some("base"));
512
513        let celo_chain_id = ChainId::new("eip155", "42220");
514        assert_eq!(celo_chain_id.as_network_name(), Some("celo"));
515
516        let radius_chain_id = ChainId::new("eip155", "723487");
517        assert_eq!(radius_chain_id.as_network_name(), Some("radius"));
518
519        let solana_chain_id = ChainId::new("solana", "5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp");
520        assert_eq!(solana_chain_id.as_network_name(), Some("solana"));
521
522        let unknown_chain_id = ChainId::new("eip155", "999999");
523        assert!(unknown_chain_id.as_network_name().is_none());
524    }
525}