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}