x402_kit/networks/
evm.rs

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