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
156impl<T> From<T> for EvmAsset
157where
158    T: ExplicitEvmAsset,
159{
160    fn from(_: T) -> Self {
161        T::ASSET
162    }
163}
164
165pub mod networks {
166    use super::*;
167
168    macro_rules! define_explicit_evm_network {
169        ($struct_name:ident, $network_const:expr) => {
170            pub struct $struct_name;
171
172            impl ExplicitEvmNetwork for $struct_name {
173                const NETWORK: EvmNetwork = $network_const;
174            }
175        };
176    }
177
178    define_explicit_evm_network!(
179        Ethereum,
180        EvmNetwork {
181            name: "ethereum",
182            chain_id: 1,
183            network_id: "eip155:1",
184        }
185    );
186    define_explicit_evm_network!(
187        EthereumSepolia,
188        EvmNetwork {
189            name: "ethereum-sepolia",
190            chain_id: 11155111,
191            network_id: "eip155:11155111",
192        }
193    );
194    define_explicit_evm_network!(
195        Base,
196        EvmNetwork {
197            name: "base",
198            chain_id: 8453,
199            network_id: "eip155:8453",
200        }
201    );
202    define_explicit_evm_network!(
203        BaseSepolia,
204        EvmNetwork {
205            name: "base-sepolia",
206            chain_id: 84532,
207            network_id: "eip155:84532",
208        }
209    );
210}
211
212pub mod assets {
213    use alloy_primitives::address;
214
215    use super::*;
216
217    macro_rules! define_explicit_evm_asset {
218        (
219            $struct_name:ident,
220            $network_struct:ty,
221            $addr:expr,
222            $decimals:expr,
223            $name:expr,
224            $symbol:expr,
225            $eip712_domain:expr
226        ) => {
227            pub struct $struct_name;
228
229            impl ExplicitEvmAsset for $struct_name {
230                type Network = $network_struct;
231
232                const ASSET: EvmAsset = EvmAsset {
233                    address: EvmAddress(address!($addr)),
234                    decimals: $decimals,
235                    name: $name,
236                    symbol: $symbol,
237                };
238
239                const EIP712_DOMAIN: Option<Eip712Domain> = $eip712_domain;
240            }
241        };
242    }
243
244    macro_rules! define_explicit_usdc {
245        ($struct_name:ident, $network_struct:ty, $addr:expr) => {
246            define_explicit_evm_asset!(
247                $struct_name,
248                $network_struct,
249                $addr,
250                6,
251                "USDC",
252                "USDC",
253                Some(Eip712Domain {
254                    name: "USDC",
255                    version: "2",
256                })
257            );
258        };
259    }
260
261    define_explicit_usdc!(
262        UsdcEthereum,
263        networks::Ethereum,
264        "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48"
265    );
266
267    define_explicit_usdc!(
268        UsdcEthereumSepolia,
269        networks::EthereumSepolia,
270        "0x1c7D4B196Cb0C7B01d743Fbc6116a902379C7238"
271    );
272
273    define_explicit_usdc!(
274        UsdcBase,
275        networks::Base,
276        "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913"
277    );
278
279    define_explicit_usdc!(
280        UsdcBaseSepolia,
281        networks::BaseSepolia,
282        "0x036CbD53842c5426634e7929541eC2318f3dCF7e"
283    );
284}