rust_x402/
lib.rs

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// HTTP/3 support (feature-gated)
15#[cfg(feature = "http3")]
16pub mod http3;
17
18// Server abstraction (feature-gated, requires axum)
19#[cfg(feature = "axum")]
20pub mod server;
21
22// Middleware support (feature-gated, requires axum)
23#[cfg(feature = "axum")]
24pub mod middleware;
25
26// Proxy support (feature-gated, requires axum)
27#[cfg(feature = "axum")]
28pub mod proxy;
29
30// Re-exports for convenience
31pub 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// Feature-gated framework support
41#[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
50/// Current version of the x402 library
51pub const VERSION: &str = env!("CARGO_PKG_VERSION");
52
53/// x402 protocol version
54pub 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        // VERSION is a const string from CARGO_PKG_VERSION
64        // Verify it contains version-like content (e.g., contains a dot or digit)
65        assert!(VERSION.contains('.') || VERSION.chars().any(|c| c.is_ascii_digit()));
66    }
67
68    #[test]
69    fn test_payment_requirements_creation() {
70        let requirements = PaymentRequirements::new(
71            "exact",
72            "base-sepolia",
73            "1000000",
74            "0x036CbD53842c5426634e7929541eC2318f3dCF7e",
75            "0x209693Bc6afc0C5328bA36FaF03C514EF312287C",
76            "https://example.com/test",
77            "Test payment",
78        );
79
80        assert_eq!(requirements.scheme, "exact");
81        assert_eq!(requirements.network, "base-sepolia");
82        assert_eq!(requirements.max_amount_required, "1000000");
83        assert_eq!(
84            requirements.asset,
85            "0x036CbD53842c5426634e7929541eC2318f3dCF7e"
86        );
87        assert_eq!(
88            requirements.pay_to,
89            "0x209693Bc6afc0C5328bA36FaF03C514EF312287C"
90        );
91        assert_eq!(requirements.resource, "https://example.com/test");
92        assert_eq!(requirements.description, "Test payment");
93    }
94
95    #[test]
96    fn test_payment_requirements_usdc_info() {
97        let mut requirements = PaymentRequirements::new(
98            "exact",
99            "base-sepolia",
100            "1000000",
101            "0x036CbD53842c5426634e7929541eC2318f3dCF7e",
102            "0x209693Bc6afc0C5328bA36FaF03C514EF312287C",
103            "https://example.com/test",
104            "Test payment",
105        );
106
107        requirements
108            .set_usdc_info(crate::types::Network::Testnet)
109            .unwrap();
110        assert!(requirements.extra.is_some());
111
112        let extra = requirements.extra.as_ref().unwrap();
113        assert_eq!(extra["name"], "USDC");
114        assert_eq!(extra["version"], "2");
115    }
116
117    #[test]
118    fn test_payment_payload_creation() {
119        let authorization = ExactEvmPayloadAuthorization::new(
120            "0x857b06519E91e3A54538791bDbb0E22373e36b66",
121            "0x209693Bc6afc0C5328bA36FaF03C514EF312287C",
122            "1000000",
123            "1745323800",
124            "1745323985",
125            "0xf3746613c2d920b5fdabc0856f2aeb2d4f88ee6037b8cc5d04a71a4462f13480",
126        );
127
128        let payload = ExactEvmPayload {
129            signature: "0x2d6a7588d6acca505cbf0d9a4a227e0c52c6c34008c8e8986a1283259764173608a2ce6496642e377d6da8dbbf5836e9bd15092f9ecab05ded3d6293af148b571c".to_string(),
130            authorization,
131        };
132
133        let payment_payload = PaymentPayload::new("exact", "base-sepolia", payload);
134
135        assert_eq!(payment_payload.x402_version, X402_VERSION);
136        assert_eq!(payment_payload.scheme, "exact");
137        assert_eq!(payment_payload.network, "base-sepolia");
138    }
139
140    #[test]
141    fn test_payment_payload_base64_encoding() {
142        let authorization = ExactEvmPayloadAuthorization::new(
143            "0x857b06519E91e3A54538791bDbb0E22373e36b66",
144            "0x209693Bc6afc0C5328bA36FaF03C514EF312287C",
145            "1000000",
146            "1745323800",
147            "1745323985",
148            "0xf3746613c2d920b5fdabc0856f2aeb2d4f88ee6037b8cc5d04a71a4462f13480",
149        );
150
151        let payload = ExactEvmPayload {
152            signature: "0x2d6a7588d6acca505cbf0d9a4a227e0c52c6c34008c8e8986a1283259764173608a2ce6496642e377d6da8dbbf5836e9bd15092f9ecab05ded3d6293af148b571c".to_string(),
153            authorization,
154        };
155
156        let payment_payload = PaymentPayload::new("exact", "base-sepolia", payload);
157        let encoded = payment_payload.to_base64().unwrap();
158        let decoded = PaymentPayload::from_base64(&encoded).unwrap();
159
160        assert_eq!(payment_payload.x402_version, decoded.x402_version);
161        assert_eq!(payment_payload.scheme, decoded.scheme);
162        assert_eq!(payment_payload.network, decoded.network);
163    }
164
165    #[test]
166    fn test_authorization_validity() {
167        let now = chrono::Utc::now().timestamp();
168        let valid_after = (now - 100).to_string();
169        let valid_before = (now + 100).to_string();
170
171        let authorization = ExactEvmPayloadAuthorization::new(
172            "0x857b06519E91e3A54538791bDbb0E22373e36b66",
173            "0x209693Bc6afc0C5328bA36FaF03C514EF312287C",
174            "1000000",
175            valid_after,
176            valid_before,
177            "0xf3746613c2d920b5fdabc0856f2aeb2d4f88ee6037b8cc5d04a71a4462f13480",
178        );
179
180        assert!(authorization.is_valid_now().unwrap());
181    }
182
183    #[test]
184    fn test_authorization_expired() {
185        let now = chrono::Utc::now().timestamp();
186        let valid_after = (now - 200).to_string();
187        let valid_before = (now - 100).to_string(); // Expired
188
189        let authorization = ExactEvmPayloadAuthorization::new(
190            "0x857b06519E91e3A54538791bDbb0E22373e36b66",
191            "0x209693Bc6afc0C5328bA36FaF03C514EF312287C",
192            "1000000",
193            valid_after,
194            valid_before,
195            "0xf3746613c2d920b5fdabc0856f2aeb2d4f88ee6037b8cc5d04a71a4462f13480",
196        );
197
198        assert!(!authorization.is_valid_now().unwrap());
199    }
200
201    #[test]
202    fn test_facilitator_config() {
203        let config = FacilitatorConfig {
204            url: "https://example.com/facilitator".to_string(),
205            timeout: Some(std::time::Duration::from_secs(30)),
206            create_auth_headers: None,
207        };
208
209        assert_eq!(config.url, "https://example.com/facilitator".to_string());
210        assert_eq!(config.timeout, Some(std::time::Duration::from_secs(30)));
211    }
212
213    #[test]
214    fn test_blockchain_facilitator_config() {
215        let config = BlockchainFacilitatorConfig {
216            rpc_url: Some("https://example.com/facilitator".to_string()),
217            network: "base-sepolia".to_string(),
218            verification_timeout: std::time::Duration::from_secs(30),
219            confirmation_blocks: 1,
220            max_retries: 3,
221            retry_delay: std::time::Duration::from_secs(1),
222        };
223
224        assert_eq!(
225            config.rpc_url,
226            Some("https://example.com/facilitator".to_string())
227        );
228        assert_eq!(
229            config.verification_timeout,
230            std::time::Duration::from_secs(30)
231        );
232    }
233
234    #[test]
235    fn test_networks() {
236        assert_eq!(networks::BASE_MAINNET, "base");
237        assert_eq!(networks::BASE_SEPOLIA, "base-sepolia");
238        assert_eq!(networks::AVALANCHE_MAINNET, "avalanche");
239        assert_eq!(networks::AVALANCHE_FUJI, "avalanche-fuji");
240
241        assert!(networks::is_supported("base-sepolia"));
242        assert!(networks::is_supported("base"));
243        assert!(!networks::is_supported("unsupported-network"));
244
245        assert_eq!(
246            networks::get_usdc_address("base-sepolia"),
247            Some("0x036CbD53842c5426634e7929541eC2318f3dCF7e")
248        );
249        assert_eq!(
250            networks::get_usdc_address("base"),
251            Some("0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913")
252        );
253    }
254
255    #[test]
256    fn test_schemes() {
257        assert_eq!(schemes::EXACT, "exact");
258    }
259}