Skip to main content

x402_networks/evm/
mod.rs

1use std::{
2    fmt::{Debug, Display},
3    str::FromStr,
4};
5
6use serde::{Deserialize, Serialize};
7
8use x402_core::core::{Address, Asset, NetworkFamily};
9
10pub mod exact;
11
12#[derive(Debug, Clone, Copy)]
13pub struct EvmNetwork {
14    pub name: &'static str,
15    pub chain_id: u64,
16    pub network_id: &'static str,
17}
18
19impl NetworkFamily for EvmNetwork {
20    fn network_name(&self) -> &str {
21        self.name
22    }
23    fn network_id(&self) -> &str {
24        self.network_id
25    }
26}
27
28#[derive(Clone, Copy, PartialEq, Eq, Hash)]
29pub struct EvmAddress(pub alloy_primitives::Address);
30
31impl From<alloy_primitives::Address> for EvmAddress {
32    fn from(addr: alloy_primitives::Address) -> Self {
33        EvmAddress(addr)
34    }
35}
36
37impl FromStr for EvmAddress {
38    type Err = alloy_primitives::AddressError;
39
40    fn from_str(s: &str) -> Result<Self, Self::Err> {
41        let addr = alloy_primitives::Address::from_str(s)?;
42        Ok(EvmAddress(addr))
43    }
44}
45
46impl Display for EvmAddress {
47    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
48        write!(f, "{}", self.0)
49    }
50}
51
52impl Debug for EvmAddress {
53    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
54        write!(f, "EvmAddress({})", self.0)
55    }
56}
57
58impl Serialize for EvmAddress {
59    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
60    where
61        S: serde::Serializer,
62    {
63        serializer.serialize_str(&self.to_string())
64    }
65}
66
67impl<'de> Deserialize<'de> for EvmAddress {
68    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
69    where
70        D: serde::Deserializer<'de>,
71    {
72        let s = String::deserialize(deserializer)?;
73        EvmAddress::from_str(&s).map_err(serde::de::Error::custom)
74    }
75}
76
77impl Address for EvmAddress {
78    type Network = EvmNetwork;
79}
80
81#[derive(Clone, Copy, PartialEq, Eq, Hash)]
82pub struct EvmSignature(pub alloy_primitives::Signature);
83
84impl Display for EvmSignature {
85    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
86        write!(f, "{}", self.0)
87    }
88}
89
90impl Debug for EvmSignature {
91    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
92        write!(f, "EvmSignature({})", self.0)
93    }
94}
95
96impl FromStr for EvmSignature {
97    type Err = alloy_primitives::SignatureError;
98
99    fn from_str(s: &str) -> Result<Self, Self::Err> {
100        let sig = alloy_primitives::Signature::from_str(s)?;
101        Ok(EvmSignature(sig))
102    }
103}
104
105impl Serialize for EvmSignature {
106    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
107    where
108        S: serde::Serializer,
109    {
110        serializer.serialize_str(&self.to_string())
111    }
112}
113
114impl<'de> Deserialize<'de> for EvmSignature {
115    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
116    where
117        D: serde::Deserializer<'de>,
118    {
119        let s = String::deserialize(deserializer)?;
120        EvmSignature::from_str(&s).map_err(serde::de::Error::custom)
121    }
122}
123
124impl From<alloy_primitives::Signature> for EvmSignature {
125    fn from(sig: alloy_primitives::Signature) -> Self {
126        EvmSignature(sig)
127    }
128}
129
130pub type EvmAsset = Asset<EvmAddress>;
131
132pub trait ExplicitEvmNetwork {
133    const NETWORK: EvmNetwork;
134}
135
136#[derive(Debug, Clone, Copy, Serialize)]
137pub struct Eip712Domain {
138    pub name: &'static str,
139    pub version: &'static str,
140}
141
142pub trait ExplicitEvmAsset {
143    type Network: ExplicitEvmNetwork;
144
145    const ASSET: EvmAsset;
146    const EIP712_DOMAIN: Option<Eip712Domain>;
147}
148
149impl<T> From<T> for EvmNetwork
150where
151    T: ExplicitEvmNetwork,
152{
153    fn from(_: T) -> Self {
154        T::NETWORK
155    }
156}
157
158pub mod networks {
159    use super::*;
160
161    macro_rules! define_explicit_evm_network {
162        ($struct_name:ident, $network_const:expr) => {
163            pub struct $struct_name;
164
165            impl ExplicitEvmNetwork for $struct_name {
166                const NETWORK: EvmNetwork = $network_const;
167            }
168        };
169    }
170
171    define_explicit_evm_network!(
172        Ethereum,
173        EvmNetwork {
174            name: "ethereum",
175            chain_id: 1,
176            network_id: "eip155:1",
177        }
178    );
179    define_explicit_evm_network!(
180        EthereumSepolia,
181        EvmNetwork {
182            name: "ethereum-sepolia",
183            chain_id: 11155111,
184            network_id: "eip155:11155111",
185        }
186    );
187    define_explicit_evm_network!(
188        Base,
189        EvmNetwork {
190            name: "base",
191            chain_id: 8453,
192            network_id: "eip155:8453",
193        }
194    );
195    define_explicit_evm_network!(
196        BaseSepolia,
197        EvmNetwork {
198            name: "base-sepolia",
199            chain_id: 84532,
200            network_id: "eip155:84532",
201        }
202    );
203}
204
205pub mod assets {
206    use alloy_primitives::address;
207
208    use super::*;
209
210    macro_rules! define_explicit_evm_asset {
211        (
212            $struct_name:ident,
213            $network_struct:ty,
214            $addr:expr,
215            $decimals:expr,
216            $name:expr,
217            $symbol:expr,
218            $eip712_domain:expr
219        ) => {
220            pub struct $struct_name;
221
222            impl ExplicitEvmAsset for $struct_name {
223                type Network = $network_struct;
224
225                const ASSET: EvmAsset = EvmAsset {
226                    address: EvmAddress(address!($addr)),
227                    decimals: $decimals,
228                    name: $name,
229                    symbol: $symbol,
230                };
231
232                const EIP712_DOMAIN: Option<Eip712Domain> = $eip712_domain;
233            }
234        };
235    }
236
237    macro_rules! define_explicit_usdc {
238        ($struct_name:ident, $network_struct:ty, $addr:expr) => {
239            define_explicit_evm_asset!(
240                $struct_name,
241                $network_struct,
242                $addr,
243                6,
244                "USD Coin",
245                "USDC",
246                Some(Eip712Domain {
247                    name: "USD Coin",
248                    version: "2",
249                })
250            );
251        };
252    }
253
254    define_explicit_usdc!(
255        UsdcEthereum,
256        networks::Ethereum,
257        "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48"
258    );
259
260    define_explicit_evm_asset!(
261        UsdcEthereumSepolia,
262        networks::EthereumSepolia,
263        "0x1c7D4B196Cb0C7B01d743Fbc6116a902379C7238",
264        6,
265        "USDC",
266        "USDC",
267        Some(Eip712Domain {
268            name: "USDC",
269            version: "2",
270        })
271    );
272
273    define_explicit_usdc!(
274        UsdcBase,
275        networks::Base,
276        "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913"
277    );
278
279    define_explicit_evm_asset!(
280        UsdcBaseSepolia,
281        networks::BaseSepolia,
282        "0x036CbD53842c5426634e7929541eC2318f3dCF7e",
283        6,
284        "USDC",
285        "USDC",
286        Some(Eip712Domain {
287            name: "USDC",
288            version: "2",
289        })
290    );
291}