1#![doc = include_str!("../README.md")]
2
3pub mod blockchain;
4pub mod blockchain_facilitator;
5pub mod client;
6pub mod crypto;
7pub mod error;
8pub mod facilitator;
9pub mod facilitator_storage;
10pub mod template;
11pub mod types;
12pub mod wallet;
13
14#[cfg(feature = "http3")]
16pub mod http3;
17
18#[cfg(feature = "axum")]
20pub mod server;
21
22#[cfg(feature = "axum")]
24pub mod middleware;
25
26#[cfg(feature = "axum")]
28pub mod proxy;
29
30pub use blockchain::{BlockchainClient, BlockchainClientFactory};
32pub use blockchain_facilitator::{
33 BlockchainFacilitatorClient, BlockchainFacilitatorConfig, BlockchainFacilitatorFactory,
34};
35pub use client::X402Client;
36pub use error::{Result, X402Error};
37pub use types::*;
38pub use wallet::{Wallet, WalletFactory};
39
40#[cfg(feature = "axum")]
42pub mod axum;
43
44#[cfg(feature = "actix-web")]
45pub mod actix_web;
46
47#[cfg(feature = "warp")]
48pub mod warp;
49
50pub const VERSION: &str = env!("CARGO_PKG_VERSION");
52
53pub const X402_VERSION: u32 = 1;
55
56#[cfg(test)]
57mod tests {
58 use super::*;
59
60 #[test]
61 fn test_version_constants() {
62 assert_eq!(X402_VERSION, 1);
63 assert!(!VERSION.is_empty());
65 }
66
67 #[test]
68 fn test_payment_requirements_creation() {
69 let requirements = PaymentRequirements::new(
70 "exact",
71 "base-sepolia",
72 "1000000",
73 "0x036CbD53842c5426634e7929541eC2318f3dCF7e",
74 "0x209693Bc6afc0C5328bA36FaF03C514EF312287C",
75 "https://example.com/test",
76 "Test payment",
77 );
78
79 assert_eq!(requirements.scheme, "exact");
80 assert_eq!(requirements.network, "base-sepolia");
81 assert_eq!(requirements.max_amount_required, "1000000");
82 assert_eq!(
83 requirements.asset,
84 "0x036CbD53842c5426634e7929541eC2318f3dCF7e"
85 );
86 assert_eq!(
87 requirements.pay_to,
88 "0x209693Bc6afc0C5328bA36FaF03C514EF312287C"
89 );
90 assert_eq!(requirements.resource, "https://example.com/test");
91 assert_eq!(requirements.description, "Test payment");
92 }
93
94 #[test]
95 fn test_payment_requirements_usdc_info() {
96 let mut requirements = PaymentRequirements::new(
97 "exact",
98 "base-sepolia",
99 "1000000",
100 "0x036CbD53842c5426634e7929541eC2318f3dCF7e",
101 "0x209693Bc6afc0C5328bA36FaF03C514EF312287C",
102 "https://example.com/test",
103 "Test payment",
104 );
105
106 requirements
107 .set_usdc_info(crate::types::Network::Testnet)
108 .unwrap();
109 assert!(requirements.extra.is_some());
110
111 let extra = requirements.extra.as_ref().unwrap();
112 assert_eq!(extra["name"], "USDC");
113 assert_eq!(extra["version"], "2");
114 }
115
116 #[test]
117 fn test_payment_payload_creation() {
118 let authorization = ExactEvmPayloadAuthorization::new(
119 "0x857b06519E91e3A54538791bDbb0E22373e36b66",
120 "0x209693Bc6afc0C5328bA36FaF03C514EF312287C",
121 "1000000",
122 "1745323800",
123 "1745323985",
124 "0xf3746613c2d920b5fdabc0856f2aeb2d4f88ee6037b8cc5d04a71a4462f13480",
125 );
126
127 let payload = ExactEvmPayload {
128 signature: "0x2d6a7588d6acca505cbf0d9a4a227e0c52c6c34008c8e8986a1283259764173608a2ce6496642e377d6da8dbbf5836e9bd15092f9ecab05ded3d6293af148b571c".to_string(),
129 authorization,
130 };
131
132 let payment_payload = PaymentPayload::new("exact", "base-sepolia", payload);
133
134 assert_eq!(payment_payload.x402_version, X402_VERSION);
135 assert_eq!(payment_payload.scheme, "exact");
136 assert_eq!(payment_payload.network, "base-sepolia");
137 }
138
139 #[test]
140 fn test_payment_payload_base64_encoding() {
141 let authorization = ExactEvmPayloadAuthorization::new(
142 "0x857b06519E91e3A54538791bDbb0E22373e36b66",
143 "0x209693Bc6afc0C5328bA36FaF03C514EF312287C",
144 "1000000",
145 "1745323800",
146 "1745323985",
147 "0xf3746613c2d920b5fdabc0856f2aeb2d4f88ee6037b8cc5d04a71a4462f13480",
148 );
149
150 let payload = ExactEvmPayload {
151 signature: "0x2d6a7588d6acca505cbf0d9a4a227e0c52c6c34008c8e8986a1283259764173608a2ce6496642e377d6da8dbbf5836e9bd15092f9ecab05ded3d6293af148b571c".to_string(),
152 authorization,
153 };
154
155 let payment_payload = PaymentPayload::new("exact", "base-sepolia", payload);
156 let encoded = payment_payload.to_base64().unwrap();
157 let decoded = PaymentPayload::from_base64(&encoded).unwrap();
158
159 assert_eq!(payment_payload.x402_version, decoded.x402_version);
160 assert_eq!(payment_payload.scheme, decoded.scheme);
161 assert_eq!(payment_payload.network, decoded.network);
162 }
163
164 #[test]
165 fn test_authorization_validity() {
166 let now = chrono::Utc::now().timestamp();
167 let valid_after = (now - 100).to_string();
168 let valid_before = (now + 100).to_string();
169
170 let authorization = ExactEvmPayloadAuthorization::new(
171 "0x857b06519E91e3A54538791bDbb0E22373e36b66",
172 "0x209693Bc6afc0C5328bA36FaF03C514EF312287C",
173 "1000000",
174 valid_after,
175 valid_before,
176 "0xf3746613c2d920b5fdabc0856f2aeb2d4f88ee6037b8cc5d04a71a4462f13480",
177 );
178
179 assert!(authorization.is_valid_now().unwrap());
180 }
181
182 #[test]
183 fn test_authorization_expired() {
184 let now = chrono::Utc::now().timestamp();
185 let valid_after = (now - 200).to_string();
186 let valid_before = (now - 100).to_string(); let authorization = ExactEvmPayloadAuthorization::new(
189 "0x857b06519E91e3A54538791bDbb0E22373e36b66",
190 "0x209693Bc6afc0C5328bA36FaF03C514EF312287C",
191 "1000000",
192 valid_after,
193 valid_before,
194 "0xf3746613c2d920b5fdabc0856f2aeb2d4f88ee6037b8cc5d04a71a4462f13480",
195 );
196
197 assert!(!authorization.is_valid_now().unwrap());
198 }
199
200 #[test]
201 fn test_facilitator_config() {
202 let config = FacilitatorConfig {
203 url: "https://example.com/facilitator".to_string(),
204 timeout: Some(std::time::Duration::from_secs(30)),
205 create_auth_headers: None,
206 };
207
208 assert_eq!(config.url, "https://example.com/facilitator".to_string());
209 assert_eq!(config.timeout, Some(std::time::Duration::from_secs(30)));
210 }
211
212 #[test]
213 fn test_blockchain_facilitator_config() {
214 let config = BlockchainFacilitatorConfig {
215 rpc_url: Some("https://example.com/facilitator".to_string()),
216 network: "base-sepolia".to_string(),
217 verification_timeout: std::time::Duration::from_secs(30),
218 confirmation_blocks: 1,
219 max_retries: 3,
220 retry_delay: std::time::Duration::from_secs(1),
221 };
222
223 assert_eq!(
224 config.rpc_url,
225 Some("https://example.com/facilitator".to_string())
226 );
227 assert_eq!(
228 config.verification_timeout,
229 std::time::Duration::from_secs(30)
230 );
231 }
232
233 #[test]
234 fn test_networks() {
235 assert_eq!(networks::BASE_MAINNET, "base");
236 assert_eq!(networks::BASE_SEPOLIA, "base-sepolia");
237 assert_eq!(networks::AVALANCHE_MAINNET, "avalanche");
238 assert_eq!(networks::AVALANCHE_FUJI, "avalanche-fuji");
239
240 assert!(networks::is_supported("base-sepolia"));
241 assert!(networks::is_supported("base"));
242 assert!(!networks::is_supported("unsupported-network"));
243
244 assert_eq!(
245 networks::get_usdc_address("base-sepolia"),
246 Some("0x036CbD53842c5426634e7929541eC2318f3dCF7e")
247 );
248 assert_eq!(
249 networks::get_usdc_address("base"),
250 Some("0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913")
251 );
252 }
253
254 #[test]
255 fn test_schemes() {
256 assert_eq!(schemes::EXACT, "exact");
257 }
258}