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