x402_types/chain/mod.rs
1//! Blockchain-specific types and providers for x402 payment processing.
2//!
3//! This module provides abstractions for interacting with different blockchain networks
4//! in the x402 protocol.
5//!
6//! # Architecture
7//!
8//! The module is organized around the concept of chain providers and chain identifiers:
9//!
10//! - [`ChainId`] - A CAIP-2 compliant chain identifier (e.g., `eip155:8453` for Base)
11//! - [`ChainIdPattern`] - Pattern matching for chain IDs (exact, wildcard, or set)
12//! - [`ChainRegistry`] - Registry of configured chain providers
13
14mod chain_id;
15
16pub use chain_id::*;
17
18use std::collections::HashMap;
19use std::sync::Arc;
20
21/// Asynchronously constructs an instance of `Self` from a configuration type.
22///
23/// This trait provides a generic mechanism for initializing structs from their
24/// corresponding configuration types. It is used throughout the x402-rs crate
25/// to build providers, registries, and other components from configuration files.
26///
27/// # Type Parameters
28///
29/// - `TConfig` - The configuration type that `Self` can be constructed from
30///
31/// Return an error if:
32/// - Configuration validation fails
33/// - Required external connections (RPC, etc.) cannot be established
34/// - Configuration values are invalid or missing
35#[async_trait::async_trait]
36pub trait FromConfig<TConfig>
37where
38 Self: Sized,
39{
40 async fn from_config(config: &TConfig) -> Result<Self, Box<dyn std::error::Error>>;
41}
42
43/// Common operations available on all chain providers.
44///
45/// This trait provides a unified interface for querying chain provider metadata
46/// regardless of the underlying blockchain type.
47pub trait ChainProviderOps {
48 /// Returns the addresses of all configured signers for this chain.
49 ///
50 /// For EVM chains, these are Ethereum addresses (0x-prefixed hex).
51 /// For Solana, these are base58-encoded public keys.
52 fn signer_addresses(&self) -> Vec<String>;
53
54 /// Returns the CAIP-2 chain identifier for this provider.
55 fn chain_id(&self) -> ChainId;
56}
57
58impl<T: ChainProviderOps> ChainProviderOps for Arc<T> {
59 fn signer_addresses(&self) -> Vec<String> {
60 (**self).signer_addresses()
61 }
62 fn chain_id(&self) -> ChainId {
63 (**self).chain_id()
64 }
65}
66
67/// Registry of configured chain providers indexed by chain ID.
68///
69/// The registry is built from configuration and provides lookup methods
70/// for finding providers by exact chain ID or by pattern matching.
71///
72/// # Type Parameters
73///
74/// - `P` - The chain provider type (e.g., [`ChainProvider`] or a custom provider type)
75///
76/// # Example
77///
78/// ```ignore
79/// use x402_rs::chain::{ChainRegistry, ChainIdPattern, ChainProvider};
80/// use x402_rs::config::Config;
81///
82/// let config = Config::load()?;
83/// let registry = ChainRegistry::from_config(config.chains()).await?;
84///
85/// // Find provider for a specific chain
86/// let base_provider = registry.by_chain_id(ChainId::new("eip155", "8453"));
87///
88/// // Find provider matching a pattern
89/// let any_evm = registry.by_chain_id_pattern(&ChainIdPattern::wildcard("eip155"));
90/// ```
91#[derive(Debug)]
92pub struct ChainRegistry<P>(HashMap<ChainId, P>);
93
94impl<P> ChainRegistry<P> {
95 pub fn new(providers: HashMap<ChainId, P>) -> Self {
96 Self(providers)
97 }
98}
99
100impl<P> ChainRegistry<P> {
101 /// Looks up a provider by exact chain ID.
102 ///
103 /// Returns `None` if no provider is configured for the given chain.
104 #[allow(dead_code)]
105 pub fn by_chain_id(&self, chain_id: ChainId) -> Option<&P> {
106 self.0.get(&chain_id)
107 }
108
109 /// Looks up providers by chain ID pattern matching.
110 ///
111 /// Returns all providers whose chain IDs match the given pattern.
112 /// The pattern can be:
113 /// - Wildcard: Matches any chain within a namespace (e.g., `eip155:*`)
114 /// - Exact: Matches a specific chain (e.g., `eip155:8453`)
115 /// - Set: Matches any chain from a set of references (e.g., `eip155:{1,8453,137}`)
116 ///
117 /// # Example
118 ///
119 /// ```ignore
120 /// use x402_rs::chain::{ChainRegistry, ChainIdPattern};
121 /// use x402_rs::config::Config;
122 ///
123 /// let config = Config::load()?;
124 /// let registry = ChainRegistry::from_config(config.chains()).await?;
125 ///
126 /// // Find all EVM chain providers
127 /// let evm_providers = registry.by_chain_id_pattern(&ChainIdPattern::wildcard("eip155"));
128 /// assert!(!evm_providers.is_empty());
129 ///
130 /// // Find providers for specific chains
131 /// let mainnet_chains = ChainIdPattern::set("eip155", ["1", "8453", "137"].into_iter().map(String::from).collect());
132 /// let mainnet_providers = registry.by_chain_id_pattern(&mainnet_chains);
133 /// ```
134 pub fn by_chain_id_pattern(&self, pattern: &ChainIdPattern) -> Vec<&P> {
135 self.0
136 .iter()
137 .filter_map(|(chain_id, provider)| pattern.matches(chain_id).then_some(provider))
138 .collect()
139 }
140}
141
142/// A token amount paired with its deployment information.
143///
144/// This type associates a numeric amount with the token deployment it refers to,
145/// enabling type-safe handling of token amounts across different chains and tokens.
146///
147/// # Type Parameters
148///
149/// - `TAmount` - The numeric type for the amount (e.g., `U256` for EVM, `u64` for Solana)
150/// - `TToken` - The token deployment type containing chain and address information
151#[derive(Debug, Clone)]
152#[allow(dead_code)] // Public for consumption by downstream crates.
153pub struct DeployedTokenAmount<TAmount, TToken> {
154 /// The token amount in the token's smallest unit (e.g., wei for ETH, lamports for SOL).
155 pub amount: TAmount,
156 /// The token deployment information including chain, address, and decimals.
157 pub token: TToken,
158}