x402_kit/schemes/
exact_svm.rs

1use 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}