x402_networks/svm/
exact.rs1use 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}