breez_sdk_spark/models/
mod.rs

1pub(crate) mod adaptors;
2pub mod payment_observer;
3pub use payment_observer::*;
4
5use core::fmt;
6use lnurl_models::RecoverLnurlPayResponse;
7use serde::{Deserialize, Serialize};
8use serde_json::Value;
9use std::{collections::HashMap, fmt::Display, str::FromStr};
10
11use crate::{
12    BitcoinAddressDetails, BitcoinNetwork, Bolt11InvoiceDetails, ExternalInputParser, FiatCurrency,
13    LnurlPayRequestDetails, LnurlWithdrawRequestDetails, Rate, SparkInvoiceDetails, SuccessAction,
14    SuccessActionProcessed, error::DepositClaimError,
15};
16
17/// A list of external input parsers that are used by default.
18/// To opt-out, set `use_default_external_input_parsers` in [Config] to false.
19pub const DEFAULT_EXTERNAL_INPUT_PARSERS: &[(&str, &str, &str)] = &[
20    (
21        "picknpay",
22        "(.*)(za.co.electrum.picknpay)(.*)",
23        "https://cryptoqr.net/.well-known/lnurlp/<input>",
24    ),
25    (
26        "bootleggers",
27        r"(.*)(wigroup\.co|yoyogroup\.co)(.*)",
28        "https://cryptoqr.net/.well-known/lnurlw/<input>",
29    ),
30];
31
32/// Represents the seed for wallet generation, either as a mnemonic phrase with an optional
33/// passphrase or as raw entropy bytes.
34#[derive(Debug, Clone)]
35#[cfg_attr(feature = "uniffi", derive(uniffi::Enum))]
36pub enum Seed {
37    /// A BIP-39 mnemonic phrase with an optional passphrase.
38    Mnemonic {
39        /// The mnemonic phrase. 12 or 24 words.
40        mnemonic: String,
41        /// An optional passphrase for the mnemonic.
42        passphrase: Option<String>,
43    },
44    /// Raw entropy bytes.
45    Entropy(Vec<u8>),
46}
47
48#[cfg_attr(feature = "uniffi", derive(uniffi::Record))]
49pub struct ConnectRequest {
50    pub config: Config,
51    pub seed: Seed,
52    pub storage_dir: String,
53}
54
55/// The type of payment
56#[derive(Debug, Clone, Copy, PartialEq, Serialize, Deserialize)]
57#[cfg_attr(feature = "uniffi", derive(uniffi::Enum))]
58pub enum PaymentType {
59    /// Payment sent from this wallet
60    Send,
61    /// Payment received to this wallet
62    Receive,
63}
64
65impl fmt::Display for PaymentType {
66    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
67        match self {
68            PaymentType::Send => write!(f, "send"),
69            PaymentType::Receive => write!(f, "receive"),
70        }
71    }
72}
73
74impl FromStr for PaymentType {
75    type Err = String;
76
77    fn from_str(s: &str) -> Result<Self, Self::Err> {
78        Ok(match s.to_lowercase().as_str() {
79            "receive" => PaymentType::Receive,
80            "send" => PaymentType::Send,
81            _ => return Err(format!("invalid payment type '{s}'")),
82        })
83    }
84}
85
86/// The status of a payment
87#[derive(Debug, Clone, Copy, PartialEq, Serialize, Deserialize)]
88#[cfg_attr(feature = "uniffi", derive(uniffi::Enum))]
89pub enum PaymentStatus {
90    /// Payment is completed successfully
91    Completed,
92    /// Payment is in progress
93    Pending,
94    /// Payment has failed
95    Failed,
96}
97
98impl PaymentStatus {
99    /// Returns true if the payment status is final (either Completed or Failed)
100    pub fn is_final(&self) -> bool {
101        matches!(self, PaymentStatus::Completed | PaymentStatus::Failed)
102    }
103}
104
105impl fmt::Display for PaymentStatus {
106    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
107        match self {
108            PaymentStatus::Completed => write!(f, "completed"),
109            PaymentStatus::Pending => write!(f, "pending"),
110            PaymentStatus::Failed => write!(f, "failed"),
111        }
112    }
113}
114
115impl FromStr for PaymentStatus {
116    type Err = String;
117
118    fn from_str(s: &str) -> Result<Self, Self::Err> {
119        Ok(match s.to_lowercase().as_str() {
120            "completed" => PaymentStatus::Completed,
121            "pending" => PaymentStatus::Pending,
122            "failed" => PaymentStatus::Failed,
123            _ => return Err(format!("Invalid payment status '{s}'")),
124        })
125    }
126}
127
128#[derive(Debug, Clone, Copy, PartialEq, Serialize, Deserialize)]
129#[cfg_attr(feature = "uniffi", derive(uniffi::Enum))]
130pub enum PaymentMethod {
131    Lightning,
132    Spark,
133    Token,
134    Deposit,
135    Withdraw,
136    Unknown,
137}
138
139impl Display for PaymentMethod {
140    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
141        match self {
142            PaymentMethod::Lightning => write!(f, "lightning"),
143            PaymentMethod::Spark => write!(f, "spark"),
144            PaymentMethod::Token => write!(f, "token"),
145            PaymentMethod::Deposit => write!(f, "deposit"),
146            PaymentMethod::Withdraw => write!(f, "withdraw"),
147            PaymentMethod::Unknown => write!(f, "unknown"),
148        }
149    }
150}
151
152impl FromStr for PaymentMethod {
153    type Err = ();
154
155    fn from_str(s: &str) -> Result<Self, Self::Err> {
156        match s {
157            "lightning" => Ok(PaymentMethod::Lightning),
158            "spark" => Ok(PaymentMethod::Spark),
159            "token" => Ok(PaymentMethod::Token),
160            "deposit" => Ok(PaymentMethod::Deposit),
161            "withdraw" => Ok(PaymentMethod::Withdraw),
162            "unknown" => Ok(PaymentMethod::Unknown),
163            _ => Err(()),
164        }
165    }
166}
167
168/// Represents a payment (sent or received)
169#[derive(Debug, Clone, Serialize, Deserialize)]
170#[cfg_attr(feature = "uniffi", derive(uniffi::Record))]
171pub struct Payment {
172    /// Unique identifier for the payment
173    pub id: String,
174    /// Type of payment (send or receive)
175    pub payment_type: PaymentType,
176    /// Status of the payment
177    pub status: PaymentStatus,
178    /// Amount in satoshis or token base units
179    pub amount: u128,
180    /// Fee paid in satoshis or token base units
181    pub fees: u128,
182    /// Timestamp of when the payment was created
183    pub timestamp: u64,
184    /// Method of payment. Sometimes the payment details is empty so this field
185    /// is used to determine the payment method.
186    pub method: PaymentMethod,
187    /// Details of the payment
188    pub details: Option<PaymentDetails>,
189}
190
191#[cfg(feature = "uniffi")]
192uniffi::custom_type!(u128, String);
193
194#[cfg(feature = "uniffi")]
195impl crate::UniffiCustomTypeConverter for u128 {
196    type Builtin = String;
197
198    fn into_custom(val: Self::Builtin) -> ::uniffi::Result<Self>
199    where
200        Self: ::std::marker::Sized,
201    {
202        val.parse::<u128>()
203            .map_err(uniffi::deps::anyhow::Error::msg)
204    }
205
206    fn from_custom(obj: Self) -> Self::Builtin {
207        obj.to_string()
208    }
209}
210
211// TODO: fix large enum variant lint - may be done by boxing lnurl_pay_info but that requires
212//  some changes to the wasm bindgen macro
213#[allow(clippy::large_enum_variant)]
214#[derive(Debug, Clone, Serialize, Deserialize)]
215#[cfg_attr(feature = "uniffi", derive(uniffi::Enum))]
216pub enum PaymentDetails {
217    Spark {
218        /// The invoice details if the payment fulfilled a spark invoice
219        invoice_details: Option<SparkInvoicePaymentDetails>,
220    },
221    Token {
222        metadata: TokenMetadata,
223        tx_hash: String,
224        /// The invoice details if the payment fulfilled a spark invoice
225        invoice_details: Option<SparkInvoicePaymentDetails>,
226    },
227    Lightning {
228        /// Represents the invoice description
229        description: Option<String>,
230        /// The preimage of the paid invoice (proof of payment).
231        preimage: Option<String>,
232        /// Represents the Bolt11/Bolt12 invoice associated with a payment
233        /// In the case of a Send payment, this is the invoice paid by the user
234        /// In the case of a Receive payment, this is the invoice paid to the user
235        invoice: String,
236
237        /// The payment hash of the invoice
238        payment_hash: String,
239
240        /// The invoice destination/payee pubkey
241        destination_pubkey: String,
242
243        /// Lnurl payment information if this was an lnurl payment.
244        lnurl_pay_info: Option<LnurlPayInfo>,
245
246        /// Lnurl withdrawal information if this was an lnurl payment.
247        lnurl_withdraw_info: Option<LnurlWithdrawInfo>,
248    },
249    Withdraw {
250        tx_id: String,
251    },
252    Deposit {
253        tx_id: String,
254    },
255}
256
257#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
258#[cfg_attr(feature = "uniffi", derive(uniffi::Record))]
259pub struct SparkInvoicePaymentDetails {
260    /// Represents the spark invoice description
261    pub description: Option<String>,
262    /// The raw spark invoice string
263    pub invoice: String,
264}
265
266#[derive(Debug, Clone, Copy)]
267#[cfg_attr(feature = "uniffi", derive(uniffi::Enum))]
268pub enum Network {
269    Mainnet,
270    Regtest,
271}
272
273impl std::fmt::Display for Network {
274    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
275        match self {
276            Network::Mainnet => write!(f, "Mainnet"),
277            Network::Regtest => write!(f, "Regtest"),
278        }
279    }
280}
281
282impl From<Network> for BitcoinNetwork {
283    fn from(network: Network) -> Self {
284        match network {
285            Network::Mainnet => BitcoinNetwork::Bitcoin,
286            Network::Regtest => BitcoinNetwork::Regtest,
287        }
288    }
289}
290
291impl From<Network> for breez_sdk_common::network::BitcoinNetwork {
292    fn from(network: Network) -> Self {
293        match network {
294            Network::Mainnet => breez_sdk_common::network::BitcoinNetwork::Bitcoin,
295            Network::Regtest => breez_sdk_common::network::BitcoinNetwork::Regtest,
296        }
297    }
298}
299
300impl From<Network> for bitcoin::Network {
301    fn from(network: Network) -> Self {
302        match network {
303            Network::Mainnet => bitcoin::Network::Bitcoin,
304            Network::Regtest => bitcoin::Network::Regtest,
305        }
306    }
307}
308
309impl FromStr for Network {
310    type Err = String;
311
312    fn from_str(s: &str) -> Result<Self, Self::Err> {
313        match s {
314            "mainnet" => Ok(Network::Mainnet),
315            "regtest" => Ok(Network::Regtest),
316            _ => Err("Invalid network".to_string()),
317        }
318    }
319}
320
321#[derive(Debug, Clone)]
322#[cfg_attr(feature = "uniffi", derive(uniffi::Record))]
323pub struct Config {
324    pub api_key: Option<String>,
325    pub network: Network,
326    pub sync_interval_secs: u32,
327
328    // The maximum fee that can be paid for a static deposit claim
329    // If not set then any fee is allowed
330    pub max_deposit_claim_fee: Option<Fee>,
331
332    /// The domain used for receiving through lnurl-pay and lightning address.
333    pub lnurl_domain: Option<String>,
334
335    /// When this is set to `true` we will prefer to use spark payments over
336    /// lightning when sending and receiving. This has the benefit of lower fees
337    /// but is at the cost of privacy.
338    pub prefer_spark_over_lightning: bool,
339
340    /// A set of external input parsers that are used by [`BreezSdk::parse`](crate::sdk::BreezSdk::parse) when the input
341    /// is not recognized. See [`ExternalInputParser`] for more details on how to configure
342    /// external parsing.
343    pub external_input_parsers: Option<Vec<ExternalInputParser>>,
344    /// The SDK includes some default external input parsers
345    /// ([`DEFAULT_EXTERNAL_INPUT_PARSERS`]).
346    /// Set this to false in order to prevent their use.
347    pub use_default_external_input_parsers: bool,
348
349    /// Url to use for the real-time sync server. Defaults to the Breez real-time sync server.
350    pub real_time_sync_server_url: Option<String>,
351
352    /// Whether the Spark private mode is enabled by default.
353    ///
354    /// If set to true, the Spark private mode will be enabled on the first initialization of the SDK.
355    /// If set to false, no changes will be made to the Spark private mode.
356    pub private_enabled_default: bool,
357}
358
359impl Config {
360    pub(crate) fn get_all_external_input_parsers(&self) -> Vec<ExternalInputParser> {
361        let mut external_input_parsers = Vec::new();
362        if self.use_default_external_input_parsers {
363            let default_parsers = DEFAULT_EXTERNAL_INPUT_PARSERS
364                .iter()
365                .map(|(id, regex, url)| ExternalInputParser {
366                    provider_id: (*id).to_string(),
367                    input_regex: (*regex).to_string(),
368                    parser_url: (*url).to_string(),
369                })
370                .collect::<Vec<_>>();
371            external_input_parsers.extend(default_parsers);
372        }
373        external_input_parsers.extend(self.external_input_parsers.clone().unwrap_or_default());
374
375        external_input_parsers
376    }
377}
378
379#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
380#[cfg_attr(feature = "uniffi", derive(uniffi::Enum))]
381pub enum Fee {
382    // Fixed fee amount in sats
383    Fixed { amount: u64 },
384    // Relative fee rate in satoshis per vbyte
385    Rate { sat_per_vbyte: u64 },
386}
387
388impl Fee {
389    pub fn to_sats(&self, vbytes: u64) -> u64 {
390        match self {
391            Fee::Fixed { amount } => *amount,
392            Fee::Rate { sat_per_vbyte } => sat_per_vbyte.saturating_mul(vbytes),
393        }
394    }
395}
396
397#[derive(Debug, Clone, Serialize, Deserialize)]
398#[cfg_attr(feature = "uniffi", derive(uniffi::Record))]
399pub struct DepositInfo {
400    pub txid: String,
401    pub vout: u32,
402    pub amount_sats: u64,
403    pub refund_tx: Option<String>,
404    pub refund_tx_id: Option<String>,
405    pub claim_error: Option<DepositClaimError>,
406}
407
408#[cfg_attr(feature = "uniffi", derive(uniffi::Record))]
409pub struct ClaimDepositRequest {
410    pub txid: String,
411    pub vout: u32,
412    #[cfg_attr(feature = "uniffi", uniffi(default=None))]
413    pub max_fee: Option<Fee>,
414}
415
416#[derive(Debug, Clone, Serialize)]
417#[cfg_attr(feature = "uniffi", derive(uniffi::Record))]
418pub struct ClaimDepositResponse {
419    pub payment: Payment,
420}
421
422#[derive(Debug, Clone, Serialize)]
423#[cfg_attr(feature = "uniffi", derive(uniffi::Record))]
424pub struct RefundDepositRequest {
425    pub txid: String,
426    pub vout: u32,
427    pub destination_address: String,
428    pub fee: Fee,
429}
430
431#[derive(Debug, Clone, Serialize)]
432#[cfg_attr(feature = "uniffi", derive(uniffi::Record))]
433pub struct RefundDepositResponse {
434    pub tx_id: String,
435    pub tx_hex: String,
436}
437
438#[derive(Debug, Clone, Serialize)]
439#[cfg_attr(feature = "uniffi", derive(uniffi::Record))]
440pub struct ListUnclaimedDepositsRequest {}
441
442#[derive(Debug, Clone, Serialize)]
443#[cfg_attr(feature = "uniffi", derive(uniffi::Record))]
444pub struct ListUnclaimedDepositsResponse {
445    pub deposits: Vec<DepositInfo>,
446}
447
448impl std::fmt::Display for Fee {
449    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
450        match self {
451            Fee::Fixed { amount } => write!(f, "Fixed: {amount}"),
452            Fee::Rate { sat_per_vbyte } => write!(f, "Rate: {sat_per_vbyte}"),
453        }
454    }
455}
456
457#[derive(Debug, Clone)]
458#[cfg_attr(feature = "uniffi", derive(uniffi::Record))]
459pub struct Credentials {
460    pub username: String,
461    pub password: String,
462}
463
464/// Request to get the balance of the wallet
465#[derive(Debug, Clone)]
466#[cfg_attr(feature = "uniffi", derive(uniffi::Record))]
467pub struct GetInfoRequest {
468    pub ensure_synced: Option<bool>,
469}
470
471/// Response containing the balance of the wallet
472#[derive(Debug, Clone, Serialize)]
473#[cfg_attr(feature = "uniffi", derive(uniffi::Record))]
474pub struct GetInfoResponse {
475    /// The balance in satoshis
476    pub balance_sats: u64,
477    /// The balances of the tokens in the wallet keyed by the token identifier
478    pub token_balances: HashMap<String, TokenBalance>,
479}
480
481#[derive(Debug, Clone, Serialize, Deserialize)]
482#[cfg_attr(feature = "uniffi", derive(uniffi::Record))]
483pub struct TokenBalance {
484    pub balance: u128,
485    pub token_metadata: TokenMetadata,
486}
487
488#[derive(Debug, Clone, Serialize, Deserialize)]
489#[cfg_attr(feature = "uniffi", derive(uniffi::Record))]
490pub struct TokenMetadata {
491    pub identifier: String,
492    /// Hex representation of the issuer public key
493    pub issuer_public_key: String,
494    pub name: String,
495    pub ticker: String,
496    /// Number of decimals the token uses
497    pub decimals: u32,
498    pub max_supply: u128,
499    pub is_freezable: bool,
500}
501
502/// Request to sync the wallet with the Spark network
503#[derive(Debug, Clone)]
504#[cfg_attr(feature = "uniffi", derive(uniffi::Record))]
505pub struct SyncWalletRequest {}
506
507/// Response from synchronizing the wallet
508#[derive(Debug, Clone, Serialize)]
509#[cfg_attr(feature = "uniffi", derive(uniffi::Record))]
510pub struct SyncWalletResponse {}
511
512#[derive(Debug, Clone, Serialize)]
513#[cfg_attr(feature = "uniffi", derive(uniffi::Enum))]
514pub enum ReceivePaymentMethod {
515    SparkAddress,
516    SparkInvoice {
517        /// Amount to receive. Denominated in sats if token identifier is empty, otherwise in the token base units
518        amount: Option<u128>,
519        /// The presence of this field indicates that the payment is for a token
520        /// If empty, it is a Bitcoin payment
521        token_identifier: Option<String>,
522        /// The expiry time of the invoice in seconds since the Unix epoch
523        expiry_time: Option<u64>,
524        /// A description to embed in the invoice.
525        description: Option<String>,
526        /// If set, the invoice may only be fulfilled by a payer with this public key
527        sender_public_key: Option<String>,
528    },
529    BitcoinAddress,
530    Bolt11Invoice {
531        description: String,
532        amount_sats: Option<u64>,
533    },
534}
535
536#[derive(Debug, Clone, Serialize)]
537#[cfg_attr(feature = "uniffi", derive(uniffi::Enum))]
538pub enum SendPaymentMethod {
539    BitcoinAddress {
540        address: BitcoinAddressDetails,
541        fee_quote: SendOnchainFeeQuote,
542    },
543    Bolt11Invoice {
544        invoice_details: Bolt11InvoiceDetails,
545        spark_transfer_fee_sats: Option<u64>,
546        lightning_fee_sats: u64,
547    }, // should be replaced with the parsed invoice
548    SparkAddress {
549        address: String,
550        /// Fee to pay for the transaction
551        /// Denominated in sats if token identifier is empty, otherwise in the token base units
552        fee: u128,
553        /// The presence of this field indicates that the payment is for a token
554        /// If empty, it is a Bitcoin payment
555        token_identifier: Option<String>,
556    },
557    SparkInvoice {
558        spark_invoice_details: SparkInvoiceDetails,
559        /// Fee to pay for the transaction
560        /// Denominated in sats if token identifier is empty, otherwise in the token base units
561        fee: u128,
562        /// The presence of this field indicates that the payment is for a token
563        /// If empty, it is a Bitcoin payment
564        token_identifier: Option<String>,
565    },
566}
567
568#[cfg_attr(feature = "uniffi", derive(uniffi::Record))]
569#[derive(Debug, Clone, Serialize)]
570pub struct SendOnchainFeeQuote {
571    pub id: String,
572    pub expires_at: u64,
573    pub speed_fast: SendOnchainSpeedFeeQuote,
574    pub speed_medium: SendOnchainSpeedFeeQuote,
575    pub speed_slow: SendOnchainSpeedFeeQuote,
576}
577
578#[cfg_attr(feature = "uniffi", derive(uniffi::Record))]
579#[derive(Debug, Clone, Serialize)]
580pub struct SendOnchainSpeedFeeQuote {
581    pub user_fee_sat: u64,
582    pub l1_broadcast_fee_sat: u64,
583}
584
585impl SendOnchainSpeedFeeQuote {
586    pub fn total_fee_sat(&self) -> u64 {
587        self.user_fee_sat.saturating_add(self.l1_broadcast_fee_sat)
588    }
589}
590
591#[cfg_attr(feature = "uniffi", derive(uniffi::Record))]
592pub struct ReceivePaymentRequest {
593    pub payment_method: ReceivePaymentMethod,
594}
595
596#[derive(Debug, Clone, Serialize)]
597#[cfg_attr(feature = "uniffi", derive(uniffi::Record))]
598pub struct ReceivePaymentResponse {
599    pub payment_request: String,
600    /// Fee to pay to receive the payment
601    /// Denominated in sats or token base units
602    pub fee: u128,
603}
604
605#[cfg_attr(feature = "uniffi", derive(uniffi::Record))]
606pub struct PrepareLnurlPayRequest {
607    pub amount_sats: u64,
608    pub pay_request: LnurlPayRequestDetails,
609    #[cfg_attr(feature = "uniffi", uniffi(default=None))]
610    pub comment: Option<String>,
611    #[cfg_attr(feature = "uniffi", uniffi(default=None))]
612    pub validate_success_action_url: Option<bool>,
613}
614
615#[derive(Debug)]
616#[cfg_attr(feature = "uniffi", derive(uniffi::Record))]
617pub struct PrepareLnurlPayResponse {
618    pub amount_sats: u64,
619    pub comment: Option<String>,
620    pub pay_request: LnurlPayRequestDetails,
621    pub fee_sats: u64,
622    pub invoice_details: Bolt11InvoiceDetails,
623    pub success_action: Option<SuccessAction>,
624}
625
626#[cfg_attr(feature = "uniffi", derive(uniffi::Record))]
627pub struct LnurlPayRequest {
628    pub prepare_response: PrepareLnurlPayResponse,
629}
630
631#[derive(Debug, Serialize)]
632#[cfg_attr(feature = "uniffi", derive(uniffi::Record))]
633pub struct LnurlPayResponse {
634    pub payment: Payment,
635    pub success_action: Option<SuccessActionProcessed>,
636}
637
638#[cfg_attr(feature = "uniffi", derive(uniffi::Record))]
639pub struct LnurlWithdrawRequest {
640    /// The amount to withdraw in satoshis
641    /// Must be within the min and max withdrawable limits
642    pub amount_sats: u64,
643    pub withdraw_request: LnurlWithdrawRequestDetails,
644    /// If set, the function will return the payment if it is still pending after this
645    /// number of seconds. If unset, the function will return immediately after
646    /// initiating the LNURL withdraw.
647    #[cfg_attr(feature = "uniffi", uniffi(default=None))]
648    pub completion_timeout_secs: Option<u32>,
649}
650
651#[derive(Debug, Serialize)]
652#[cfg_attr(feature = "uniffi", derive(uniffi::Record))]
653pub struct LnurlWithdrawResponse {
654    /// The Lightning invoice generated for the LNURL withdraw
655    pub payment_request: String,
656    pub payment: Option<Payment>,
657}
658
659/// Represents the payment LNURL info
660#[derive(Clone, Debug, Default, Deserialize, PartialEq, Serialize)]
661#[cfg_attr(feature = "uniffi", derive(uniffi::Record))]
662pub struct LnurlPayInfo {
663    pub ln_address: Option<String>,
664    pub comment: Option<String>,
665    pub domain: Option<String>,
666    pub metadata: Option<String>,
667    pub processed_success_action: Option<SuccessActionProcessed>,
668    pub raw_success_action: Option<SuccessAction>,
669}
670
671/// Represents the withdraw LNURL info
672#[derive(Clone, Debug, Deserialize, Serialize)]
673#[cfg_attr(feature = "uniffi", derive(uniffi::Record))]
674pub struct LnurlWithdrawInfo {
675    pub withdraw_url: String,
676}
677
678impl LnurlPayInfo {
679    pub fn extract_description(&self) -> Option<String> {
680        let Some(metadata) = &self.metadata else {
681            return None;
682        };
683
684        let Ok(metadata) = serde_json::from_str::<Vec<Vec<Value>>>(metadata) else {
685            return None;
686        };
687
688        for arr in metadata {
689            if arr.len() != 2 {
690                continue;
691            }
692            if let (Some(key), Some(value)) = (arr[0].as_str(), arr[1].as_str())
693                && key == "text/plain"
694            {
695                return Some(value.to_string());
696            }
697        }
698
699        None
700    }
701}
702
703#[cfg_attr(feature = "uniffi", derive(uniffi::Enum))]
704#[derive(Debug, Clone, Serialize)]
705pub enum OnchainConfirmationSpeed {
706    Fast,
707    Medium,
708    Slow,
709}
710
711#[cfg_attr(feature = "uniffi", derive(uniffi::Record))]
712pub struct PrepareSendPaymentRequest {
713    pub payment_request: String,
714    /// Amount to send. By default is denominated in sats.
715    /// If a token identifier is provided, the amount will be denominated in the token base units.
716    #[cfg_attr(feature = "uniffi", uniffi(default=None))]
717    pub amount: Option<u128>,
718    /// If provided, the payment will be for a token
719    /// May only be provided if the payment request is a spark address
720    #[cfg_attr(feature = "uniffi", uniffi(default=None))]
721    pub token_identifier: Option<String>,
722}
723
724#[derive(Debug, Clone, Serialize)]
725#[cfg_attr(feature = "uniffi", derive(uniffi::Record))]
726pub struct PrepareSendPaymentResponse {
727    pub payment_method: SendPaymentMethod,
728    /// Amount to send. By default is denominated in sats.
729    /// If a token identifier is provided, the amount will be denominated in the token base units.
730    pub amount: u128,
731    /// The presence of this field indicates that the payment is for a token
732    /// If empty, it is a Bitcoin payment
733    pub token_identifier: Option<String>,
734}
735
736#[cfg_attr(feature = "uniffi", derive(uniffi::Enum))]
737pub enum SendPaymentOptions {
738    BitcoinAddress {
739        confirmation_speed: OnchainConfirmationSpeed,
740    },
741    Bolt11Invoice {
742        prefer_spark: bool,
743
744        /// If set, the function will return the payment if it is still pending after this
745        /// number of seconds. If unset, the function will return immediately after initiating the payment.
746        completion_timeout_secs: Option<u32>,
747    },
748}
749
750#[cfg_attr(feature = "uniffi", derive(uniffi::Record))]
751pub struct SendPaymentRequest {
752    pub prepare_response: PrepareSendPaymentResponse,
753    #[cfg_attr(feature = "uniffi", uniffi(default=None))]
754    pub options: Option<SendPaymentOptions>,
755}
756
757#[derive(Debug, Clone, Serialize)]
758#[cfg_attr(feature = "uniffi", derive(uniffi::Record))]
759pub struct SendPaymentResponse {
760    pub payment: Payment,
761}
762
763/// Request to list payments with optional filters and pagination
764#[derive(Debug, Clone, Default)]
765#[cfg_attr(feature = "uniffi", derive(uniffi::Record))]
766pub struct ListPaymentsRequest {
767    #[cfg_attr(feature = "uniffi", uniffi(default=None))]
768    pub type_filter: Option<Vec<PaymentType>>,
769    #[cfg_attr(feature = "uniffi", uniffi(default=None))]
770    pub status_filter: Option<Vec<PaymentStatus>>,
771    #[cfg_attr(feature = "uniffi", uniffi(default=None))]
772    pub asset_filter: Option<AssetFilter>,
773    /// Only include payments created after this timestamp (inclusive)
774    #[cfg_attr(feature = "uniffi", uniffi(default=None))]
775    pub from_timestamp: Option<u64>,
776    /// Only include payments created before this timestamp (exclusive)
777    #[cfg_attr(feature = "uniffi", uniffi(default=None))]
778    pub to_timestamp: Option<u64>,
779    /// Number of records to skip
780    #[cfg_attr(feature = "uniffi", uniffi(default=None))]
781    pub offset: Option<u32>,
782    /// Maximum number of records to return
783    #[cfg_attr(feature = "uniffi", uniffi(default=None))]
784    pub limit: Option<u32>,
785    #[cfg_attr(feature = "uniffi", uniffi(default=None))]
786    pub sort_ascending: Option<bool>,
787}
788
789/// A field of [`ListPaymentsRequest`] when listing payments filtered by asset
790#[derive(Debug, Clone)]
791#[cfg_attr(feature = "uniffi", derive(uniffi::Enum))]
792pub enum AssetFilter {
793    Bitcoin,
794    Token {
795        /// Optional token identifier to filter by
796        token_identifier: Option<String>,
797    },
798}
799
800impl FromStr for AssetFilter {
801    type Err = String;
802
803    fn from_str(s: &str) -> Result<Self, Self::Err> {
804        Ok(match s.to_lowercase().as_str() {
805            "bitcoin" => AssetFilter::Bitcoin,
806            "token" => AssetFilter::Token {
807                token_identifier: None,
808            },
809            str if str.starts_with("token:") => AssetFilter::Token {
810                token_identifier: Some(
811                    str.split_once(':')
812                        .ok_or(format!("Invalid asset filter '{s}'"))?
813                        .1
814                        .to_string(),
815                ),
816            },
817            _ => return Err(format!("Invalid asset filter '{s}'")),
818        })
819    }
820}
821
822/// Response from listing payments
823#[derive(Debug, Clone, Serialize)]
824#[cfg_attr(feature = "uniffi", derive(uniffi::Record))]
825pub struct ListPaymentsResponse {
826    /// The list of payments
827    pub payments: Vec<Payment>,
828}
829
830#[cfg_attr(feature = "uniffi", derive(uniffi::Record))]
831pub struct GetPaymentRequest {
832    pub payment_id: String,
833}
834
835#[derive(Debug, Clone, Serialize)]
836#[cfg_attr(feature = "uniffi", derive(uniffi::Record))]
837pub struct GetPaymentResponse {
838    pub payment: Payment,
839}
840
841#[cfg_attr(feature = "uniffi", uniffi::export(callback_interface))]
842pub trait Logger: Send + Sync {
843    fn log(&self, l: LogEntry);
844}
845
846#[derive(Debug, Clone, Serialize)]
847#[cfg_attr(feature = "uniffi", derive(uniffi::Record))]
848pub struct LogEntry {
849    pub line: String,
850    pub level: String,
851}
852
853#[cfg_attr(feature = "uniffi", derive(uniffi::Record))]
854#[derive(Debug, Clone, Serialize, Deserialize)]
855pub struct CheckLightningAddressRequest {
856    pub username: String,
857}
858
859#[cfg_attr(feature = "uniffi", derive(uniffi::Record))]
860#[derive(Debug, Clone, Serialize, Deserialize)]
861pub struct RegisterLightningAddressRequest {
862    pub username: String,
863    #[cfg_attr(feature = "uniffi", uniffi(default=None))]
864    pub description: Option<String>,
865}
866
867#[cfg_attr(feature = "uniffi", derive(uniffi::Record))]
868#[derive(Deserialize, Serialize)]
869pub struct LightningAddressInfo {
870    pub description: String,
871    pub lightning_address: String,
872    pub lnurl: String,
873    pub username: String,
874}
875
876impl From<RecoverLnurlPayResponse> for LightningAddressInfo {
877    fn from(resp: RecoverLnurlPayResponse) -> Self {
878        Self {
879            description: resp.description,
880            lightning_address: resp.lightning_address,
881            lnurl: resp.lnurl,
882            username: resp.username,
883        }
884    }
885}
886
887#[cfg_attr(feature = "uniffi", derive(uniffi::Enum))]
888#[derive(Debug, Default, Clone, Copy, PartialEq, Eq)]
889pub enum KeySetType {
890    #[default]
891    Default,
892    Taproot,
893    NativeSegwit,
894    WrappedSegwit,
895    Legacy,
896}
897
898impl From<spark_wallet::KeySetType> for KeySetType {
899    fn from(value: spark_wallet::KeySetType) -> Self {
900        match value {
901            spark_wallet::KeySetType::Default => KeySetType::Default,
902            spark_wallet::KeySetType::Taproot => KeySetType::Taproot,
903            spark_wallet::KeySetType::NativeSegwit => KeySetType::NativeSegwit,
904            spark_wallet::KeySetType::WrappedSegwit => KeySetType::WrappedSegwit,
905            spark_wallet::KeySetType::Legacy => KeySetType::Legacy,
906        }
907    }
908}
909
910impl From<KeySetType> for spark_wallet::KeySetType {
911    fn from(value: KeySetType) -> Self {
912        match value {
913            KeySetType::Default => spark_wallet::KeySetType::Default,
914            KeySetType::Taproot => spark_wallet::KeySetType::Taproot,
915            KeySetType::NativeSegwit => spark_wallet::KeySetType::NativeSegwit,
916            KeySetType::WrappedSegwit => spark_wallet::KeySetType::WrappedSegwit,
917            KeySetType::Legacy => spark_wallet::KeySetType::Legacy,
918        }
919    }
920}
921
922/// Response from listing fiat currencies
923#[derive(Debug, Clone, Serialize)]
924#[cfg_attr(feature = "uniffi", derive(uniffi::Record))]
925pub struct ListFiatCurrenciesResponse {
926    /// The list of fiat currencies
927    pub currencies: Vec<FiatCurrency>,
928}
929
930/// Response from listing fiat rates
931#[derive(Debug, Clone, Serialize)]
932#[cfg_attr(feature = "uniffi", derive(uniffi::Record))]
933pub struct ListFiatRatesResponse {
934    /// The list of fiat rates
935    pub rates: Vec<Rate>,
936}
937
938#[cfg_attr(feature = "uniffi", derive(uniffi::Record))]
939pub struct WaitForPaymentRequest {
940    pub identifier: WaitForPaymentIdentifier,
941}
942
943#[cfg_attr(feature = "uniffi", derive(uniffi::Enum))]
944pub enum WaitForPaymentIdentifier {
945    PaymentId(String),
946    PaymentRequest(String),
947}
948
949#[cfg_attr(feature = "uniffi", derive(uniffi::Record))]
950pub struct WaitForPaymentResponse {
951    pub payment: Payment,
952}
953
954#[cfg_attr(feature = "uniffi", derive(uniffi::Record))]
955pub struct GetTokensMetadataRequest {
956    pub token_identifiers: Vec<String>,
957}
958
959#[derive(Debug, Clone, Serialize)]
960#[cfg_attr(feature = "uniffi", derive(uniffi::Record))]
961pub struct GetTokensMetadataResponse {
962    pub tokens_metadata: Vec<TokenMetadata>,
963}
964
965#[cfg_attr(feature = "uniffi", derive(uniffi::Record))]
966pub struct SignMessageRequest {
967    pub message: String,
968    /// If true, the signature will be encoded in compact format instead of DER format
969    pub compact: bool,
970}
971
972#[cfg_attr(feature = "uniffi", derive(uniffi::Record))]
973pub struct SignMessageResponse {
974    pub pubkey: String,
975    /// The DER or compact hex encoded signature
976    pub signature: String,
977}
978
979#[cfg_attr(feature = "uniffi", derive(uniffi::Record))]
980pub struct CheckMessageRequest {
981    /// The message that was signed
982    pub message: String,
983    /// The public key that signed the message
984    pub pubkey: String,
985    /// The DER or compact hex encoded signature
986    pub signature: String,
987}
988
989#[cfg_attr(feature = "uniffi", derive(uniffi::Record))]
990pub struct CheckMessageResponse {
991    pub is_valid: bool,
992}
993
994#[cfg_attr(feature = "uniffi", derive(uniffi::Record))]
995#[derive(Debug, Clone, Serialize)]
996pub struct UserSettings {
997    pub spark_private_mode_enabled: bool,
998}
999
1000#[cfg_attr(feature = "uniffi", derive(uniffi::Record))]
1001pub struct UpdateUserSettingsRequest {
1002    pub spark_private_mode_enabled: Option<bool>,
1003}