Skip to main content

x402_networks/svm/
exact.rs

1use bon::Builder;
2use serde::{Deserialize, Serialize};
3
4use x402_core::{
5    core::{Payment, Scheme},
6    transport::PaymentRequirements,
7};
8
9use crate::svm::{ExplicitSvmAsset, ExplicitSvmNetwork, SvmAddress, SvmNetwork};
10
11#[derive(Builder, Debug, Clone)]
12pub struct ExactSvm<A: ExplicitSvmAsset> {
13    pub asset: A,
14    #[builder(into)]
15    pub pay_to: SvmAddress,
16    pub amount: u64,
17    pub max_timeout_seconds_override: Option<u64>,
18}
19
20impl<A: ExplicitSvmAsset> From<ExactSvm<A>> for Payment<ExactSvmScheme, SvmAddress> {
21    fn from(scheme: ExactSvm<A>) -> Self {
22        Payment {
23            scheme: ExactSvmScheme(A::Network::NETWORK),
24            pay_to: scheme.pay_to,
25            asset: A::ASSET,
26            amount: scheme.amount.into(),
27            max_timeout_seconds: scheme.max_timeout_seconds_override.unwrap_or(300),
28            extra: None,
29        }
30    }
31}
32
33impl<A: ExplicitSvmAsset> From<ExactSvm<A>> for PaymentRequirements {
34    fn from(scheme: ExactSvm<A>) -> Self {
35        PaymentRequirements::from(Payment::from(scheme))
36    }
37}
38
39pub struct ExactSvmScheme(pub SvmNetwork);
40
41impl Scheme for ExactSvmScheme {
42    type Network = SvmNetwork;
43    type Payload = ExplicitSvmPayload;
44    const SCHEME_NAME: &'static str = "exact";
45
46    fn network(&self) -> &Self::Network {
47        &self.0
48    }
49}
50
51#[derive(Debug, Clone, Serialize, Deserialize)]
52#[serde(rename_all = "camelCase")]
53pub struct ExplicitSvmPayload {
54    pub transaction: String,
55}
56
57#[cfg(test)]
58mod tests {
59    use solana_pubkey::pubkey;
60
61    use crate::svm::assets::UsdcSolanaDevnet;
62
63    use super::*;
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}