x402_kit/schemes/
exact_svm.rs1use bon::Builder;
2use serde::{Deserialize, Serialize};
3
4use crate::{
5 core::{Payment, Scheme},
6 networks::svm::{ExplicitSvmAsset, ExplicitSvmNetwork, SvmAddress, SvmNetwork},
7 transport::PaymentRequirements,
8};
9
10#[derive(Builder, Debug, Clone)]
11pub struct ExactSvm<A: ExplicitSvmAsset> {
12 pub asset: A,
13 #[builder(into)]
14 pub pay_to: SvmAddress,
15 pub amount: u64,
16 pub max_timeout_seconds_override: Option<u64>,
17}
18
19impl<A: ExplicitSvmAsset> From<ExactSvm<A>> for Payment<ExactSvmScheme, SvmAddress> {
20 fn from(scheme: ExactSvm<A>) -> Self {
21 Payment {
22 scheme: ExactSvmScheme(A::Network::NETWORK),
23 pay_to: scheme.pay_to,
24 asset: A::ASSET,
25 amount: scheme.amount.into(),
26 max_timeout_seconds: scheme.max_timeout_seconds_override.unwrap_or(300),
27 extra: None,
28 }
29 }
30}
31
32impl<A: ExplicitSvmAsset> From<ExactSvm<A>> for PaymentRequirements {
33 fn from(scheme: ExactSvm<A>) -> Self {
34 PaymentRequirements::from(Payment::from(scheme))
35 }
36}
37
38pub struct ExactSvmScheme(pub SvmNetwork);
39
40impl Scheme for ExactSvmScheme {
41 type Network = SvmNetwork;
42 type Payload = ExplicitSvmPayload;
43 const SCHEME_NAME: &'static str = "exact";
44
45 fn network(&self) -> &Self::Network {
46 &self.0
47 }
48}
49
50#[derive(Debug, Clone, Serialize, Deserialize)]
51#[serde(rename_all = "camelCase")]
52pub struct ExplicitSvmPayload {
53 pub transaction: String,
54}
55
56#[cfg(test)]
57mod tests {
58 use solana_pubkey::pubkey;
59
60 use crate::{
61 networks::svm::assets::UsdcSolanaDevnet, schemes::exact_svm::ExactSvm,
62 transport::PaymentRequirements,
63 };
64
65 #[test]
66 fn test_build_payment_requirements() {
67 let pr: PaymentRequirements = ExactSvm::builder()
68 .asset(UsdcSolanaDevnet)
69 .amount(1000)
70 .pay_to(pubkey!("Ge3jkza5KRfXvaq3GELNLh6V1pjjdEKNpEdGXJgjjKUR"))
71 .build()
72 .into();
73
74 assert_eq!(pr.scheme, "exact");
75 assert_eq!(pr.network, "solana:EtWTRABZaYq6iMfeYKouRu166VU2xqa1");
76 assert_eq!(pr.amount, 1000u64.into());
77 assert!(pr.extra.is_none());
78 }
79}