x402_kit/networks/
evm.rs

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