breez_sdk_spark/models/
mod.rs

1pub(crate) mod adaptors;
2pub mod payment_observer;
3use flashnet::{BTC_ASSET_ADDRESS, Pool};
4pub use payment_observer::*;
5
6use core::fmt;
7use lnurl_models::RecoverLnurlPayResponse;
8use serde::{Deserialize, Serialize};
9use serde_json::Value;
10use std::{collections::HashMap, fmt::Display, str::FromStr};
11
12use crate::{
13    BitcoinAddressDetails, BitcoinChainService, BitcoinNetwork, Bolt11InvoiceDetails,
14    ExternalInputParser, FiatCurrency, LnurlPayRequestDetails, LnurlWithdrawRequestDetails, Rate,
15    SdkError, SparkInvoiceDetails, SuccessAction, SuccessActionProcessed, error::DepositClaimError,
16};
17
18/// A list of external input parsers that are used by default.
19/// To opt-out, set `use_default_external_input_parsers` in [Config] to false.
20pub const DEFAULT_EXTERNAL_INPUT_PARSERS: &[(&str, &str, &str)] = &[
21    (
22        "picknpay",
23        "(.*)(za.co.electrum.picknpay)(.*)",
24        "https://cryptoqr.net/.well-known/lnurlp/<input>",
25    ),
26    (
27        "bootleggers",
28        r"(.*)(wigroup\.co|yoyogroup\.co)(.*)",
29        "https://cryptoqr.net/.well-known/lnurlw/<input>",
30    ),
31];
32
33/// Represents the seed for wallet generation, either as a mnemonic phrase with an optional
34/// passphrase or as raw entropy bytes.
35#[derive(Debug, Clone)]
36#[cfg_attr(feature = "uniffi", derive(uniffi::Enum))]
37pub enum Seed {
38    /// A BIP-39 mnemonic phrase with an optional passphrase.
39    Mnemonic {
40        /// The mnemonic phrase. 12 or 24 words.
41        mnemonic: String,
42        /// An optional passphrase for the mnemonic.
43        passphrase: Option<String>,
44    },
45    /// Raw entropy bytes.
46    Entropy(Vec<u8>),
47}
48
49impl Seed {
50    pub fn to_bytes(&self) -> Result<Vec<u8>, SdkError> {
51        match self {
52            Seed::Mnemonic {
53                mnemonic,
54                passphrase,
55            } => {
56                let mnemonic = bip39::Mnemonic::parse(mnemonic)
57                    .map_err(|e| SdkError::Generic(e.to_string()))?;
58
59                Ok(mnemonic
60                    .to_seed(passphrase.as_deref().unwrap_or(""))
61                    .to_vec())
62            }
63            Seed::Entropy(entropy) => Ok(entropy.clone()),
64        }
65    }
66}
67
68#[cfg_attr(feature = "uniffi", derive(uniffi::Record))]
69pub struct ConnectRequest {
70    pub config: Config,
71    pub seed: Seed,
72    pub storage_dir: String,
73}
74
75/// Request object for connecting to the Spark network using an external signer.
76///
77/// This allows using a custom signer implementation instead of providing a seed directly.
78#[cfg(not(all(target_family = "wasm", target_os = "unknown")))]
79#[cfg_attr(feature = "uniffi", derive(uniffi::Record))]
80pub struct ConnectWithSignerRequest {
81    pub config: Config,
82    pub signer: std::sync::Arc<dyn crate::signer::ExternalSigner>,
83    pub storage_dir: String,
84}
85
86/// The type of payment
87#[derive(Debug, Clone, Copy, PartialEq, Serialize, Deserialize)]
88#[cfg_attr(feature = "uniffi", derive(uniffi::Enum))]
89pub enum PaymentType {
90    /// Payment sent from this wallet
91    Send,
92    /// Payment received to this wallet
93    Receive,
94}
95
96impl fmt::Display for PaymentType {
97    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
98        match self {
99            PaymentType::Send => write!(f, "send"),
100            PaymentType::Receive => write!(f, "receive"),
101        }
102    }
103}
104
105impl FromStr for PaymentType {
106    type Err = String;
107
108    fn from_str(s: &str) -> Result<Self, Self::Err> {
109        Ok(match s.to_lowercase().as_str() {
110            "receive" => PaymentType::Receive,
111            "send" => PaymentType::Send,
112            _ => return Err(format!("invalid payment type '{s}'")),
113        })
114    }
115}
116
117/// The status of a payment
118#[derive(Debug, Clone, Copy, PartialEq, Serialize, Deserialize)]
119#[cfg_attr(feature = "uniffi", derive(uniffi::Enum))]
120pub enum PaymentStatus {
121    /// Payment is completed successfully
122    Completed,
123    /// Payment is in progress
124    Pending,
125    /// Payment has failed
126    Failed,
127}
128
129impl PaymentStatus {
130    /// Returns true if the payment status is final (either Completed or Failed)
131    pub fn is_final(&self) -> bool {
132        matches!(self, PaymentStatus::Completed | PaymentStatus::Failed)
133    }
134}
135
136impl fmt::Display for PaymentStatus {
137    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
138        match self {
139            PaymentStatus::Completed => write!(f, "completed"),
140            PaymentStatus::Pending => write!(f, "pending"),
141            PaymentStatus::Failed => write!(f, "failed"),
142        }
143    }
144}
145
146impl FromStr for PaymentStatus {
147    type Err = String;
148
149    fn from_str(s: &str) -> Result<Self, Self::Err> {
150        Ok(match s.to_lowercase().as_str() {
151            "completed" => PaymentStatus::Completed,
152            "pending" => PaymentStatus::Pending,
153            "failed" => PaymentStatus::Failed,
154            _ => return Err(format!("Invalid payment status '{s}'")),
155        })
156    }
157}
158
159#[derive(Debug, Clone, Copy, PartialEq, Serialize, Deserialize)]
160#[cfg_attr(feature = "uniffi", derive(uniffi::Enum))]
161pub enum PaymentMethod {
162    Lightning,
163    Spark,
164    Token,
165    Deposit,
166    Withdraw,
167    Unknown,
168}
169
170impl Display for PaymentMethod {
171    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
172        match self {
173            PaymentMethod::Lightning => write!(f, "lightning"),
174            PaymentMethod::Spark => write!(f, "spark"),
175            PaymentMethod::Token => write!(f, "token"),
176            PaymentMethod::Deposit => write!(f, "deposit"),
177            PaymentMethod::Withdraw => write!(f, "withdraw"),
178            PaymentMethod::Unknown => write!(f, "unknown"),
179        }
180    }
181}
182
183impl FromStr for PaymentMethod {
184    type Err = ();
185
186    fn from_str(s: &str) -> Result<Self, Self::Err> {
187        match s {
188            "lightning" => Ok(PaymentMethod::Lightning),
189            "spark" => Ok(PaymentMethod::Spark),
190            "token" => Ok(PaymentMethod::Token),
191            "deposit" => Ok(PaymentMethod::Deposit),
192            "withdraw" => Ok(PaymentMethod::Withdraw),
193            "unknown" => Ok(PaymentMethod::Unknown),
194            _ => Err(()),
195        }
196    }
197}
198
199/// Represents a payment (sent or received)
200#[derive(Debug, Clone, Serialize, Deserialize)]
201#[cfg_attr(feature = "uniffi", derive(uniffi::Record))]
202pub struct Payment {
203    /// Unique identifier for the payment
204    pub id: String,
205    /// Type of payment (send or receive)
206    pub payment_type: PaymentType,
207    /// Status of the payment
208    pub status: PaymentStatus,
209    /// Amount in satoshis or token base units
210    pub amount: u128,
211    /// Fee paid in satoshis or token base units
212    pub fees: u128,
213    /// Timestamp of when the payment was created
214    pub timestamp: u64,
215    /// Method of payment. Sometimes the payment details is empty so this field
216    /// is used to determine the payment method.
217    pub method: PaymentMethod,
218    /// Details of the payment
219    pub details: Option<PaymentDetails>,
220}
221
222#[cfg(feature = "uniffi")]
223uniffi::custom_type!(u128, String);
224
225#[cfg(feature = "uniffi")]
226impl crate::UniffiCustomTypeConverter for u128 {
227    type Builtin = String;
228
229    fn into_custom(val: Self::Builtin) -> ::uniffi::Result<Self>
230    where
231        Self: ::std::marker::Sized,
232    {
233        val.parse::<u128>()
234            .map_err(uniffi::deps::anyhow::Error::msg)
235    }
236
237    fn from_custom(obj: Self) -> Self::Builtin {
238        obj.to_string()
239    }
240}
241
242// TODO: fix large enum variant lint - may be done by boxing lnurl_pay_info but that requires
243//  some changes to the wasm bindgen macro
244#[allow(clippy::large_enum_variant)]
245#[derive(Debug, Clone, Serialize, Deserialize)]
246#[cfg_attr(feature = "uniffi", derive(uniffi::Enum))]
247pub enum PaymentDetails {
248    Spark {
249        /// The invoice details if the payment fulfilled a spark invoice
250        invoice_details: Option<SparkInvoicePaymentDetails>,
251        /// The HTLC transfer details if the payment fulfilled an HTLC transfer
252        htlc_details: Option<SparkHtlcDetails>,
253        /// The information for a conversion
254        conversion_info: Option<ConversionInfo>,
255    },
256    Token {
257        metadata: TokenMetadata,
258        tx_hash: String,
259        /// The invoice details if the payment fulfilled a spark invoice
260        invoice_details: Option<SparkInvoicePaymentDetails>,
261        /// The information for a conversion
262        conversion_info: Option<ConversionInfo>,
263    },
264    Lightning {
265        /// Represents the invoice description
266        description: Option<String>,
267        /// The preimage of the paid invoice (proof of payment).
268        preimage: Option<String>,
269        /// Represents the Bolt11/Bolt12 invoice associated with a payment
270        /// In the case of a Send payment, this is the invoice paid by the user
271        /// In the case of a Receive payment, this is the invoice paid to the user
272        invoice: String,
273
274        /// The payment hash of the invoice
275        payment_hash: String,
276
277        /// The invoice destination/payee pubkey
278        destination_pubkey: String,
279
280        /// Lnurl payment information if this was an lnurl payment.
281        lnurl_pay_info: Option<LnurlPayInfo>,
282
283        /// Lnurl withdrawal information if this was an lnurl payment.
284        lnurl_withdraw_info: Option<LnurlWithdrawInfo>,
285
286        /// Lnurl receive information if this was a received lnurl payment.
287        lnurl_receive_metadata: Option<LnurlReceiveMetadata>,
288    },
289    Withdraw {
290        tx_id: String,
291    },
292    Deposit {
293        tx_id: String,
294    },
295}
296
297#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
298#[cfg_attr(feature = "uniffi", derive(uniffi::Record))]
299pub struct SparkInvoicePaymentDetails {
300    /// Represents the spark invoice description
301    pub description: Option<String>,
302    /// The raw spark invoice string
303    pub invoice: String,
304}
305
306#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
307#[cfg_attr(feature = "uniffi", derive(uniffi::Record))]
308pub struct SparkHtlcDetails {
309    /// The payment hash of the HTLC
310    pub payment_hash: String,
311    /// The preimage of the HTLC. Empty until receiver has released it.
312    pub preimage: Option<String>,
313    /// The expiry time of the HTLC as a unix timestamp in seconds
314    pub expiry_time: u64,
315    /// The HTLC status
316    pub status: SparkHtlcStatus,
317}
318
319#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq)]
320#[cfg_attr(feature = "uniffi", derive(uniffi::Enum))]
321pub enum SparkHtlcStatus {
322    /// The HTLC is waiting for the preimage to be shared by the receiver
323    WaitingForPreimage,
324    /// The HTLC preimage has been shared and the transfer can be or has been claimed by the receiver
325    PreimageShared,
326    /// The HTLC has been returned to the sender due to expiry
327    Returned,
328}
329
330impl fmt::Display for SparkHtlcStatus {
331    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
332        match self {
333            SparkHtlcStatus::WaitingForPreimage => write!(f, "WaitingForPreimage"),
334            SparkHtlcStatus::PreimageShared => write!(f, "PreimageShared"),
335            SparkHtlcStatus::Returned => write!(f, "Returned"),
336        }
337    }
338}
339
340impl FromStr for SparkHtlcStatus {
341    type Err = String;
342
343    fn from_str(s: &str) -> Result<Self, Self::Err> {
344        match s {
345            "WaitingForPreimage" => Ok(SparkHtlcStatus::WaitingForPreimage),
346            "PreimageShared" => Ok(SparkHtlcStatus::PreimageShared),
347            "Returned" => Ok(SparkHtlcStatus::Returned),
348            _ => Err("Invalid Spark HTLC status".to_string()),
349        }
350    }
351}
352
353#[derive(Debug, Clone, Copy)]
354#[cfg_attr(feature = "uniffi", derive(uniffi::Enum))]
355pub enum Network {
356    Mainnet,
357    Regtest,
358}
359
360impl std::fmt::Display for Network {
361    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
362        match self {
363            Network::Mainnet => write!(f, "Mainnet"),
364            Network::Regtest => write!(f, "Regtest"),
365        }
366    }
367}
368
369impl From<Network> for BitcoinNetwork {
370    fn from(network: Network) -> Self {
371        match network {
372            Network::Mainnet => BitcoinNetwork::Bitcoin,
373            Network::Regtest => BitcoinNetwork::Regtest,
374        }
375    }
376}
377
378impl From<Network> for breez_sdk_common::network::BitcoinNetwork {
379    fn from(network: Network) -> Self {
380        match network {
381            Network::Mainnet => breez_sdk_common::network::BitcoinNetwork::Bitcoin,
382            Network::Regtest => breez_sdk_common::network::BitcoinNetwork::Regtest,
383        }
384    }
385}
386
387impl From<Network> for bitcoin::Network {
388    fn from(network: Network) -> Self {
389        match network {
390            Network::Mainnet => bitcoin::Network::Bitcoin,
391            Network::Regtest => bitcoin::Network::Regtest,
392        }
393    }
394}
395
396impl FromStr for Network {
397    type Err = String;
398
399    fn from_str(s: &str) -> Result<Self, Self::Err> {
400        match s {
401            "mainnet" => Ok(Network::Mainnet),
402            "regtest" => Ok(Network::Regtest),
403            _ => Err("Invalid network".to_string()),
404        }
405    }
406}
407
408#[derive(Debug, Clone)]
409#[cfg_attr(feature = "uniffi", derive(uniffi::Record))]
410pub struct Config {
411    pub api_key: Option<String>,
412    pub network: Network,
413    pub sync_interval_secs: u32,
414
415    // The maximum fee that can be paid for a static deposit claim
416    // If not set then any fee is allowed
417    pub max_deposit_claim_fee: Option<MaxFee>,
418
419    /// The domain used for receiving through lnurl-pay and lightning address.
420    pub lnurl_domain: Option<String>,
421
422    /// When this is set to `true` we will prefer to use spark payments over
423    /// lightning when sending and receiving. This has the benefit of lower fees
424    /// but is at the cost of privacy.
425    pub prefer_spark_over_lightning: bool,
426
427    /// A set of external input parsers that are used by [`BreezSdk::parse`](crate::sdk::BreezSdk::parse) when the input
428    /// is not recognized. See [`ExternalInputParser`] for more details on how to configure
429    /// external parsing.
430    pub external_input_parsers: Option<Vec<ExternalInputParser>>,
431    /// The SDK includes some default external input parsers
432    /// ([`DEFAULT_EXTERNAL_INPUT_PARSERS`]).
433    /// Set this to false in order to prevent their use.
434    pub use_default_external_input_parsers: bool,
435
436    /// Url to use for the real-time sync server. Defaults to the Breez real-time sync server.
437    pub real_time_sync_server_url: Option<String>,
438
439    /// Whether the Spark private mode is enabled by default.
440    ///
441    /// If set to true, the Spark private mode will be enabled on the first initialization of the SDK.
442    /// If set to false, no changes will be made to the Spark private mode.
443    pub private_enabled_default: bool,
444
445    /// Configuration for leaf optimization.
446    ///
447    /// Leaf optimization controls the denominations of leaves that are held in the wallet.
448    /// Fewer, bigger leaves allow for more funds to be exited unilaterally.
449    /// More leaves allow payments to be made without needing a swap, reducing payment latency.
450    pub optimization_config: OptimizationConfig,
451}
452
453#[derive(Debug, Clone)]
454#[cfg_attr(feature = "uniffi", derive(uniffi::Record))]
455pub struct OptimizationConfig {
456    /// Whether automatic leaf optimization is enabled.
457    ///
458    /// If set to true, the SDK will automatically optimize the leaf set when it changes.
459    /// Otherwise, the manual optimization API must be used to optimize the leaf set.
460    ///
461    /// Default value is true.
462    pub auto_enabled: bool,
463    /// The desired multiplicity for the leaf set. Acceptable values are 0-5.
464    ///
465    /// Setting this to 0 will optimize for maximizing unilateral exit.
466    /// Higher values will optimize for minimizing transfer swaps, with higher values
467    /// being more aggressive.
468    ///
469    /// Default value is 1.
470    pub multiplicity: u8,
471}
472
473impl Config {
474    pub(crate) fn get_all_external_input_parsers(&self) -> Vec<ExternalInputParser> {
475        let mut external_input_parsers = Vec::new();
476        if self.use_default_external_input_parsers {
477            let default_parsers = DEFAULT_EXTERNAL_INPUT_PARSERS
478                .iter()
479                .map(|(id, regex, url)| ExternalInputParser {
480                    provider_id: (*id).to_string(),
481                    input_regex: (*regex).to_string(),
482                    parser_url: (*url).to_string(),
483                })
484                .collect::<Vec<_>>();
485            external_input_parsers.extend(default_parsers);
486        }
487        external_input_parsers.extend(self.external_input_parsers.clone().unwrap_or_default());
488
489        external_input_parsers
490    }
491}
492
493#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
494#[cfg_attr(feature = "uniffi", derive(uniffi::Enum))]
495pub enum MaxFee {
496    // Fixed fee amount in sats
497    Fixed { amount: u64 },
498    // Relative fee rate in satoshis per vbyte
499    Rate { sat_per_vbyte: u64 },
500    // Fastest network recommended fee at the time of claim, with a leeway in satoshis per vbyte
501    NetworkRecommended { leeway_sat_per_vbyte: u64 },
502}
503
504impl MaxFee {
505    pub(crate) async fn to_fee(&self, client: &dyn BitcoinChainService) -> Result<Fee, SdkError> {
506        match self {
507            MaxFee::Fixed { amount } => Ok(Fee::Fixed { amount: *amount }),
508            MaxFee::Rate { sat_per_vbyte } => Ok(Fee::Rate {
509                sat_per_vbyte: *sat_per_vbyte,
510            }),
511            MaxFee::NetworkRecommended {
512                leeway_sat_per_vbyte,
513            } => {
514                let recommended_fees = client.recommended_fees().await?;
515                let max_fee_rate = recommended_fees
516                    .fastest_fee
517                    .saturating_add(*leeway_sat_per_vbyte);
518                Ok(Fee::Rate {
519                    sat_per_vbyte: max_fee_rate,
520                })
521            }
522        }
523    }
524}
525
526#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
527#[cfg_attr(feature = "uniffi", derive(uniffi::Enum))]
528pub enum Fee {
529    // Fixed fee amount in sats
530    Fixed { amount: u64 },
531    // Relative fee rate in satoshis per vbyte
532    Rate { sat_per_vbyte: u64 },
533}
534
535impl Fee {
536    pub fn to_sats(&self, vbytes: u64) -> u64 {
537        match self {
538            Fee::Fixed { amount } => *amount,
539            Fee::Rate { sat_per_vbyte } => sat_per_vbyte.saturating_mul(vbytes),
540        }
541    }
542}
543
544#[derive(Debug, Clone, Serialize, Deserialize)]
545#[cfg_attr(feature = "uniffi", derive(uniffi::Record))]
546pub struct DepositInfo {
547    pub txid: String,
548    pub vout: u32,
549    pub amount_sats: u64,
550    pub refund_tx: Option<String>,
551    pub refund_tx_id: Option<String>,
552    pub claim_error: Option<DepositClaimError>,
553}
554
555#[cfg_attr(feature = "uniffi", derive(uniffi::Record))]
556pub struct ClaimDepositRequest {
557    pub txid: String,
558    pub vout: u32,
559    #[cfg_attr(feature = "uniffi", uniffi(default=None))]
560    pub max_fee: Option<MaxFee>,
561}
562
563#[derive(Debug, Clone, Serialize)]
564#[cfg_attr(feature = "uniffi", derive(uniffi::Record))]
565pub struct ClaimDepositResponse {
566    pub payment: Payment,
567}
568
569#[derive(Debug, Clone, Serialize)]
570#[cfg_attr(feature = "uniffi", derive(uniffi::Record))]
571pub struct RefundDepositRequest {
572    pub txid: String,
573    pub vout: u32,
574    pub destination_address: String,
575    pub fee: Fee,
576}
577
578#[derive(Debug, Clone, Serialize)]
579#[cfg_attr(feature = "uniffi", derive(uniffi::Record))]
580pub struct RefundDepositResponse {
581    pub tx_id: String,
582    pub tx_hex: String,
583}
584
585#[derive(Debug, Clone, Serialize)]
586#[cfg_attr(feature = "uniffi", derive(uniffi::Record))]
587pub struct ListUnclaimedDepositsRequest {}
588
589#[derive(Debug, Clone, Serialize)]
590#[cfg_attr(feature = "uniffi", derive(uniffi::Record))]
591pub struct ListUnclaimedDepositsResponse {
592    pub deposits: Vec<DepositInfo>,
593}
594
595impl std::fmt::Display for MaxFee {
596    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
597        match self {
598            MaxFee::Fixed { amount } => write!(f, "Fixed: {amount}"),
599            MaxFee::Rate { sat_per_vbyte } => write!(f, "Rate: {sat_per_vbyte}"),
600            MaxFee::NetworkRecommended {
601                leeway_sat_per_vbyte,
602            } => write!(f, "NetworkRecommended: {leeway_sat_per_vbyte}"),
603        }
604    }
605}
606
607#[derive(Debug, Clone)]
608#[cfg_attr(feature = "uniffi", derive(uniffi::Record))]
609pub struct Credentials {
610    pub username: String,
611    pub password: String,
612}
613
614/// Request to get the balance of the wallet
615#[derive(Debug, Clone)]
616#[cfg_attr(feature = "uniffi", derive(uniffi::Record))]
617pub struct GetInfoRequest {
618    pub ensure_synced: Option<bool>,
619}
620
621/// Response containing the balance of the wallet
622#[derive(Debug, Clone, Serialize)]
623#[cfg_attr(feature = "uniffi", derive(uniffi::Record))]
624pub struct GetInfoResponse {
625    /// The balance in satoshis
626    pub balance_sats: u64,
627    /// The balances of the tokens in the wallet keyed by the token identifier
628    pub token_balances: HashMap<String, TokenBalance>,
629}
630
631#[derive(Debug, Clone, Serialize, Deserialize)]
632#[cfg_attr(feature = "uniffi", derive(uniffi::Record))]
633pub struct TokenBalance {
634    pub balance: u128,
635    pub token_metadata: TokenMetadata,
636}
637
638#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
639#[cfg_attr(feature = "uniffi", derive(uniffi::Record))]
640pub struct TokenMetadata {
641    pub identifier: String,
642    /// Hex representation of the issuer public key
643    pub issuer_public_key: String,
644    pub name: String,
645    pub ticker: String,
646    /// Number of decimals the token uses
647    pub decimals: u32,
648    pub max_supply: u128,
649    pub is_freezable: bool,
650}
651
652/// Request to sync the wallet with the Spark network
653#[derive(Debug, Clone)]
654#[cfg_attr(feature = "uniffi", derive(uniffi::Record))]
655pub struct SyncWalletRequest {}
656
657/// Response from synchronizing the wallet
658#[derive(Debug, Clone, Serialize)]
659#[cfg_attr(feature = "uniffi", derive(uniffi::Record))]
660pub struct SyncWalletResponse {}
661
662#[derive(Debug, Clone, Serialize)]
663#[cfg_attr(feature = "uniffi", derive(uniffi::Enum))]
664pub enum ReceivePaymentMethod {
665    SparkAddress,
666    SparkInvoice {
667        /// Amount to receive. Denominated in sats if token identifier is empty, otherwise in the token base units
668        amount: Option<u128>,
669        /// The presence of this field indicates that the payment is for a token
670        /// If empty, it is a Bitcoin payment
671        token_identifier: Option<String>,
672        /// The expiry time of the invoice as a unix timestamp in seconds
673        expiry_time: Option<u64>,
674        /// A description to embed in the invoice.
675        description: Option<String>,
676        /// If set, the invoice may only be fulfilled by a payer with this public key
677        sender_public_key: Option<String>,
678    },
679    BitcoinAddress,
680    Bolt11Invoice {
681        description: String,
682        amount_sats: Option<u64>,
683        /// The expiry of the invoice as a duration in seconds
684        expiry_secs: Option<u32>,
685    },
686}
687
688#[derive(Debug, Clone, Serialize)]
689#[cfg_attr(feature = "uniffi", derive(uniffi::Enum))]
690pub enum SendPaymentMethod {
691    BitcoinAddress {
692        address: BitcoinAddressDetails,
693        fee_quote: SendOnchainFeeQuote,
694    },
695    Bolt11Invoice {
696        invoice_details: Bolt11InvoiceDetails,
697        spark_transfer_fee_sats: Option<u64>,
698        lightning_fee_sats: u64,
699    }, // should be replaced with the parsed invoice
700    SparkAddress {
701        address: String,
702        /// Fee to pay for the transaction
703        /// Denominated in sats if token identifier is empty, otherwise in the token base units
704        fee: u128,
705        /// The presence of this field indicates that the payment is for a token
706        /// If empty, it is a Bitcoin payment
707        token_identifier: Option<String>,
708    },
709    SparkInvoice {
710        spark_invoice_details: SparkInvoiceDetails,
711        /// Fee to pay for the transaction
712        /// Denominated in sats if token identifier is empty, otherwise in the token base units
713        fee: u128,
714        /// The presence of this field indicates that the payment is for a token
715        /// If empty, it is a Bitcoin payment
716        token_identifier: Option<String>,
717    },
718}
719
720#[cfg_attr(feature = "uniffi", derive(uniffi::Record))]
721#[derive(Debug, Clone, Serialize)]
722pub struct SendOnchainFeeQuote {
723    pub id: String,
724    pub expires_at: u64,
725    pub speed_fast: SendOnchainSpeedFeeQuote,
726    pub speed_medium: SendOnchainSpeedFeeQuote,
727    pub speed_slow: SendOnchainSpeedFeeQuote,
728}
729
730#[cfg_attr(feature = "uniffi", derive(uniffi::Record))]
731#[derive(Debug, Clone, Serialize)]
732pub struct SendOnchainSpeedFeeQuote {
733    pub user_fee_sat: u64,
734    pub l1_broadcast_fee_sat: u64,
735}
736
737impl SendOnchainSpeedFeeQuote {
738    pub fn total_fee_sat(&self) -> u64 {
739        self.user_fee_sat.saturating_add(self.l1_broadcast_fee_sat)
740    }
741}
742
743#[cfg_attr(feature = "uniffi", derive(uniffi::Record))]
744pub struct ReceivePaymentRequest {
745    pub payment_method: ReceivePaymentMethod,
746}
747
748#[derive(Debug, Clone, Serialize)]
749#[cfg_attr(feature = "uniffi", derive(uniffi::Record))]
750pub struct ReceivePaymentResponse {
751    pub payment_request: String,
752    /// Fee to pay to receive the payment
753    /// Denominated in sats or token base units
754    pub fee: u128,
755}
756
757#[cfg_attr(feature = "uniffi", derive(uniffi::Record))]
758pub struct PrepareLnurlPayRequest {
759    pub amount_sats: u64,
760    pub pay_request: LnurlPayRequestDetails,
761    #[cfg_attr(feature = "uniffi", uniffi(default=None))]
762    pub comment: Option<String>,
763    #[cfg_attr(feature = "uniffi", uniffi(default=None))]
764    pub validate_success_action_url: Option<bool>,
765}
766
767#[derive(Debug)]
768#[cfg_attr(feature = "uniffi", derive(uniffi::Record))]
769pub struct PrepareLnurlPayResponse {
770    pub amount_sats: u64,
771    pub comment: Option<String>,
772    pub pay_request: LnurlPayRequestDetails,
773    pub fee_sats: u64,
774    pub invoice_details: Bolt11InvoiceDetails,
775    pub success_action: Option<SuccessAction>,
776}
777
778#[cfg_attr(feature = "uniffi", derive(uniffi::Record))]
779pub struct LnurlPayRequest {
780    pub prepare_response: PrepareLnurlPayResponse,
781    /// If set, providing the same idempotency key for multiple requests will ensure that only one
782    /// payment is made. If an idempotency key is re-used, the same payment will be returned.
783    /// The idempotency key must be a valid UUID.
784    #[cfg_attr(feature = "uniffi", uniffi(default=None))]
785    pub idempotency_key: Option<String>,
786}
787
788#[derive(Debug, Serialize)]
789#[cfg_attr(feature = "uniffi", derive(uniffi::Record))]
790pub struct LnurlPayResponse {
791    pub payment: Payment,
792    pub success_action: Option<SuccessActionProcessed>,
793}
794
795#[cfg_attr(feature = "uniffi", derive(uniffi::Record))]
796pub struct LnurlWithdrawRequest {
797    /// The amount to withdraw in satoshis
798    /// Must be within the min and max withdrawable limits
799    pub amount_sats: u64,
800    pub withdraw_request: LnurlWithdrawRequestDetails,
801    /// If set, the function will return the payment if it is still pending after this
802    /// number of seconds. If unset, the function will return immediately after
803    /// initiating the LNURL withdraw.
804    #[cfg_attr(feature = "uniffi", uniffi(default=None))]
805    pub completion_timeout_secs: Option<u32>,
806}
807
808#[derive(Debug, Serialize)]
809#[cfg_attr(feature = "uniffi", derive(uniffi::Record))]
810pub struct LnurlWithdrawResponse {
811    /// The Lightning invoice generated for the LNURL withdraw
812    pub payment_request: String,
813    pub payment: Option<Payment>,
814}
815
816/// Represents the payment LNURL info
817#[derive(Clone, Debug, Default, Deserialize, PartialEq, Serialize)]
818#[cfg_attr(feature = "uniffi", derive(uniffi::Record))]
819pub struct LnurlPayInfo {
820    pub ln_address: Option<String>,
821    pub comment: Option<String>,
822    pub domain: Option<String>,
823    pub metadata: Option<String>,
824    pub processed_success_action: Option<SuccessActionProcessed>,
825    pub raw_success_action: Option<SuccessAction>,
826}
827
828/// Represents the withdraw LNURL info
829#[derive(Clone, Debug, Deserialize, Serialize)]
830#[cfg_attr(feature = "uniffi", derive(uniffi::Record))]
831pub struct LnurlWithdrawInfo {
832    pub withdraw_url: String,
833}
834
835impl LnurlPayInfo {
836    pub fn extract_description(&self) -> Option<String> {
837        let Some(metadata) = &self.metadata else {
838            return None;
839        };
840
841        let Ok(metadata) = serde_json::from_str::<Vec<Vec<Value>>>(metadata) else {
842            return None;
843        };
844
845        for arr in metadata {
846            if arr.len() != 2 {
847                continue;
848            }
849            if let (Some(key), Some(value)) = (arr[0].as_str(), arr[1].as_str())
850                && key == "text/plain"
851            {
852                return Some(value.to_string());
853            }
854        }
855
856        None
857    }
858}
859
860#[cfg_attr(feature = "uniffi", derive(uniffi::Enum))]
861#[derive(Debug, Clone, Serialize)]
862pub enum OnchainConfirmationSpeed {
863    Fast,
864    Medium,
865    Slow,
866}
867
868#[cfg_attr(feature = "uniffi", derive(uniffi::Record))]
869pub struct PrepareSendPaymentRequest {
870    pub payment_request: String,
871    /// Amount to send. By default is denominated in sats.
872    /// If a token identifier is provided, the amount will be denominated in the token base units.
873    #[cfg_attr(feature = "uniffi", uniffi(default=None))]
874    pub amount: Option<u128>,
875    /// If provided, the payment will be for a token.
876    /// May only be provided if the payment request is a spark address.
877    #[cfg_attr(feature = "uniffi", uniffi(default=None))]
878    pub token_identifier: Option<String>,
879    /// If provided, the payment will include a conversion step before sending the payment
880    #[cfg_attr(feature = "uniffi", uniffi(default=None))]
881    pub conversion_options: Option<ConversionOptions>,
882}
883
884#[derive(Debug, Clone, Serialize)]
885#[cfg_attr(feature = "uniffi", derive(uniffi::Record))]
886pub struct PrepareSendPaymentResponse {
887    pub payment_method: SendPaymentMethod,
888    /// Amount to send. By default is denominated in sats.
889    /// If a token identifier is provided, the amount will be denominated in the token base units.
890    pub amount: u128,
891    /// The presence of this field indicates that the payment is for a token.
892    /// If empty, it is a Bitcoin payment.
893    pub token_identifier: Option<String>,
894    /// When set, the payment will include a conversion step before sending the payment
895    pub conversion_estimate: Option<ConversionEstimate>,
896}
897
898#[cfg_attr(feature = "uniffi", derive(uniffi::Enum))]
899pub enum SendPaymentOptions {
900    BitcoinAddress {
901        confirmation_speed: OnchainConfirmationSpeed,
902    },
903    Bolt11Invoice {
904        prefer_spark: bool,
905
906        /// If set, the function will return the payment if it is still pending after this
907        /// number of seconds. If unset, the function will return immediately after initiating the payment.
908        completion_timeout_secs: Option<u32>,
909    },
910    SparkAddress {
911        /// Can only be provided for Bitcoin payments. If set, a Spark HTLC transfer will be created.
912        /// The receiver will need to provide the preimage to claim it.
913        htlc_options: Option<SparkHtlcOptions>,
914    },
915}
916
917#[cfg_attr(feature = "uniffi", derive(uniffi::Record))]
918pub struct SparkHtlcOptions {
919    /// The payment hash of the HTLC. The receiver will need to provide the associated preimage to claim it.
920    pub payment_hash: String,
921    /// The duration of the HTLC in seconds.
922    /// After this time, the HTLC will be returned.
923    pub expiry_duration_secs: u64,
924}
925
926#[cfg_attr(feature = "uniffi", derive(uniffi::Record))]
927pub struct SendPaymentRequest {
928    pub prepare_response: PrepareSendPaymentResponse,
929    #[cfg_attr(feature = "uniffi", uniffi(default=None))]
930    pub options: Option<SendPaymentOptions>,
931    /// The optional idempotency key for all Spark based transfers (excludes token payments).
932    /// If set, providing the same idempotency key for multiple requests will ensure that only one
933    /// payment is made. If an idempotency key is re-used, the same payment will be returned.
934    /// The idempotency key must be a valid UUID.
935    #[cfg_attr(feature = "uniffi", uniffi(default=None))]
936    pub idempotency_key: Option<String>,
937}
938
939#[derive(Debug, Clone, Serialize)]
940#[cfg_attr(feature = "uniffi", derive(uniffi::Record))]
941pub struct SendPaymentResponse {
942    pub payment: Payment,
943}
944
945#[derive(Debug, Clone)]
946#[cfg_attr(feature = "uniffi", derive(uniffi::Enum))]
947pub enum PaymentDetailsFilter {
948    Spark {
949        /// Filter specific Spark HTLC statuses
950        htlc_status: Option<Vec<SparkHtlcStatus>>,
951        /// Filter conversion payments with refund information
952        conversion_refund_needed: Option<bool>,
953    },
954    Token {
955        /// Filter conversion payments with refund information
956        conversion_refund_needed: Option<bool>,
957        /// Filter by transaction hash
958        tx_hash: Option<String>,
959    },
960}
961
962/// Request to list payments with optional filters and pagination
963#[derive(Debug, Clone, Default)]
964#[cfg_attr(feature = "uniffi", derive(uniffi::Record))]
965pub struct ListPaymentsRequest {
966    #[cfg_attr(feature = "uniffi", uniffi(default=None))]
967    pub type_filter: Option<Vec<PaymentType>>,
968    #[cfg_attr(feature = "uniffi", uniffi(default=None))]
969    pub status_filter: Option<Vec<PaymentStatus>>,
970    #[cfg_attr(feature = "uniffi", uniffi(default=None))]
971    pub asset_filter: Option<AssetFilter>,
972    /// Only include payments matching at least one of these payment details filters
973    #[cfg_attr(feature = "uniffi", uniffi(default=None))]
974    pub payment_details_filter: Option<Vec<PaymentDetailsFilter>>,
975    /// Only include payments created after this timestamp (inclusive)
976    #[cfg_attr(feature = "uniffi", uniffi(default=None))]
977    pub from_timestamp: Option<u64>,
978    /// Only include payments created before this timestamp (exclusive)
979    #[cfg_attr(feature = "uniffi", uniffi(default=None))]
980    pub to_timestamp: Option<u64>,
981    /// Number of records to skip
982    #[cfg_attr(feature = "uniffi", uniffi(default=None))]
983    pub offset: Option<u32>,
984    /// Maximum number of records to return
985    #[cfg_attr(feature = "uniffi", uniffi(default=None))]
986    pub limit: Option<u32>,
987    #[cfg_attr(feature = "uniffi", uniffi(default=None))]
988    pub sort_ascending: Option<bool>,
989}
990
991/// A field of [`ListPaymentsRequest`] when listing payments filtered by asset
992#[derive(Debug, Clone)]
993#[cfg_attr(feature = "uniffi", derive(uniffi::Enum))]
994pub enum AssetFilter {
995    Bitcoin,
996    Token {
997        /// Optional token identifier to filter by
998        token_identifier: Option<String>,
999    },
1000}
1001
1002impl FromStr for AssetFilter {
1003    type Err = String;
1004
1005    fn from_str(s: &str) -> Result<Self, Self::Err> {
1006        Ok(match s.to_lowercase().as_str() {
1007            "bitcoin" => AssetFilter::Bitcoin,
1008            "token" => AssetFilter::Token {
1009                token_identifier: None,
1010            },
1011            str if str.starts_with("token:") => AssetFilter::Token {
1012                token_identifier: Some(
1013                    str.split_once(':')
1014                        .ok_or(format!("Invalid asset filter '{s}'"))?
1015                        .1
1016                        .to_string(),
1017                ),
1018            },
1019            _ => return Err(format!("Invalid asset filter '{s}'")),
1020        })
1021    }
1022}
1023
1024/// Response from listing payments
1025#[derive(Debug, Clone, Serialize)]
1026#[cfg_attr(feature = "uniffi", derive(uniffi::Record))]
1027pub struct ListPaymentsResponse {
1028    /// The list of payments
1029    pub payments: Vec<Payment>,
1030}
1031
1032#[cfg_attr(feature = "uniffi", derive(uniffi::Record))]
1033pub struct GetPaymentRequest {
1034    pub payment_id: String,
1035}
1036
1037#[derive(Debug, Clone, Serialize)]
1038#[cfg_attr(feature = "uniffi", derive(uniffi::Record))]
1039pub struct GetPaymentResponse {
1040    pub payment: Payment,
1041}
1042
1043#[cfg_attr(feature = "uniffi", uniffi::export(callback_interface))]
1044pub trait Logger: Send + Sync {
1045    fn log(&self, l: LogEntry);
1046}
1047
1048#[derive(Debug, Clone, Serialize)]
1049#[cfg_attr(feature = "uniffi", derive(uniffi::Record))]
1050pub struct LogEntry {
1051    pub line: String,
1052    pub level: String,
1053}
1054
1055#[cfg_attr(feature = "uniffi", derive(uniffi::Record))]
1056#[derive(Debug, Clone, Serialize, Deserialize)]
1057pub struct CheckLightningAddressRequest {
1058    pub username: String,
1059}
1060
1061#[cfg_attr(feature = "uniffi", derive(uniffi::Record))]
1062#[derive(Debug, Clone, Serialize, Deserialize)]
1063pub struct RegisterLightningAddressRequest {
1064    pub username: String,
1065    #[cfg_attr(feature = "uniffi", uniffi(default=None))]
1066    pub description: Option<String>,
1067}
1068
1069#[cfg_attr(feature = "uniffi", derive(uniffi::Record))]
1070#[derive(Deserialize, Serialize)]
1071pub struct LightningAddressInfo {
1072    pub description: String,
1073    pub lightning_address: String,
1074    pub lnurl: String,
1075    pub username: String,
1076}
1077
1078impl From<RecoverLnurlPayResponse> for LightningAddressInfo {
1079    fn from(resp: RecoverLnurlPayResponse) -> Self {
1080        Self {
1081            description: resp.description,
1082            lightning_address: resp.lightning_address,
1083            lnurl: resp.lnurl,
1084            username: resp.username,
1085        }
1086    }
1087}
1088
1089#[cfg_attr(feature = "uniffi", derive(uniffi::Enum))]
1090#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
1091pub enum KeySetType {
1092    #[default]
1093    Default,
1094    Taproot,
1095    NativeSegwit,
1096    WrappedSegwit,
1097    Legacy,
1098}
1099
1100impl From<spark_wallet::KeySetType> for KeySetType {
1101    fn from(value: spark_wallet::KeySetType) -> Self {
1102        match value {
1103            spark_wallet::KeySetType::Default => KeySetType::Default,
1104            spark_wallet::KeySetType::Taproot => KeySetType::Taproot,
1105            spark_wallet::KeySetType::NativeSegwit => KeySetType::NativeSegwit,
1106            spark_wallet::KeySetType::WrappedSegwit => KeySetType::WrappedSegwit,
1107            spark_wallet::KeySetType::Legacy => KeySetType::Legacy,
1108        }
1109    }
1110}
1111
1112impl From<KeySetType> for spark_wallet::KeySetType {
1113    fn from(value: KeySetType) -> Self {
1114        match value {
1115            KeySetType::Default => spark_wallet::KeySetType::Default,
1116            KeySetType::Taproot => spark_wallet::KeySetType::Taproot,
1117            KeySetType::NativeSegwit => spark_wallet::KeySetType::NativeSegwit,
1118            KeySetType::WrappedSegwit => spark_wallet::KeySetType::WrappedSegwit,
1119            KeySetType::Legacy => spark_wallet::KeySetType::Legacy,
1120        }
1121    }
1122}
1123
1124/// Configuration for key set derivation.
1125///
1126/// This struct encapsulates the parameters needed for BIP32 key derivation.
1127#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
1128#[cfg_attr(feature = "uniffi", derive(uniffi::Record))]
1129pub struct KeySetConfig {
1130    /// The key set type which determines the derivation path
1131    pub key_set_type: KeySetType,
1132    /// Controls the structure of the BIP derivation path
1133    pub use_address_index: bool,
1134    /// Optional account number for key derivation
1135    pub account_number: Option<u32>,
1136}
1137
1138impl Default for KeySetConfig {
1139    fn default() -> Self {
1140        Self {
1141            key_set_type: KeySetType::Default,
1142            use_address_index: false,
1143            account_number: None,
1144        }
1145    }
1146}
1147
1148/// Response from listing fiat currencies
1149#[derive(Debug, Clone, Serialize)]
1150#[cfg_attr(feature = "uniffi", derive(uniffi::Record))]
1151pub struct ListFiatCurrenciesResponse {
1152    /// The list of fiat currencies
1153    pub currencies: Vec<FiatCurrency>,
1154}
1155
1156/// Response from listing fiat rates
1157#[derive(Debug, Clone, Serialize)]
1158#[cfg_attr(feature = "uniffi", derive(uniffi::Record))]
1159pub struct ListFiatRatesResponse {
1160    /// The list of fiat rates
1161    pub rates: Vec<Rate>,
1162}
1163
1164pub(crate) enum WaitForPaymentIdentifier {
1165    PaymentId(String),
1166    PaymentRequest(String),
1167}
1168
1169#[cfg_attr(feature = "uniffi", derive(uniffi::Record))]
1170pub struct GetTokensMetadataRequest {
1171    pub token_identifiers: Vec<String>,
1172}
1173
1174#[derive(Debug, Clone, Serialize)]
1175#[cfg_attr(feature = "uniffi", derive(uniffi::Record))]
1176pub struct GetTokensMetadataResponse {
1177    pub tokens_metadata: Vec<TokenMetadata>,
1178}
1179
1180#[cfg_attr(feature = "uniffi", derive(uniffi::Record))]
1181pub struct SignMessageRequest {
1182    pub message: String,
1183    /// If true, the signature will be encoded in compact format instead of DER format
1184    pub compact: bool,
1185}
1186
1187#[cfg_attr(feature = "uniffi", derive(uniffi::Record))]
1188pub struct SignMessageResponse {
1189    pub pubkey: String,
1190    /// The DER or compact hex encoded signature
1191    pub signature: String,
1192}
1193
1194#[cfg_attr(feature = "uniffi", derive(uniffi::Record))]
1195pub struct CheckMessageRequest {
1196    /// The message that was signed
1197    pub message: String,
1198    /// The public key that signed the message
1199    pub pubkey: String,
1200    /// The DER or compact hex encoded signature
1201    pub signature: String,
1202}
1203
1204#[cfg_attr(feature = "uniffi", derive(uniffi::Record))]
1205pub struct CheckMessageResponse {
1206    pub is_valid: bool,
1207}
1208
1209#[cfg_attr(feature = "uniffi", derive(uniffi::Record))]
1210#[derive(Debug, Clone, Serialize)]
1211pub struct UserSettings {
1212    pub spark_private_mode_enabled: bool,
1213}
1214
1215#[cfg_attr(feature = "uniffi", derive(uniffi::Record))]
1216pub struct UpdateUserSettingsRequest {
1217    pub spark_private_mode_enabled: Option<bool>,
1218}
1219
1220#[cfg_attr(feature = "uniffi", derive(uniffi::Record))]
1221pub struct ClaimHtlcPaymentRequest {
1222    pub preimage: String,
1223}
1224
1225#[cfg_attr(feature = "uniffi", derive(uniffi::Record))]
1226pub struct ClaimHtlcPaymentResponse {
1227    pub payment: Payment,
1228}
1229
1230#[derive(Debug, Clone, Deserialize, Serialize)]
1231#[cfg_attr(feature = "uniffi", derive(uniffi::Record))]
1232pub struct LnurlReceiveMetadata {
1233    pub nostr_zap_request: Option<String>,
1234    pub nostr_zap_receipt: Option<String>,
1235    pub sender_comment: Option<String>,
1236}
1237
1238#[cfg_attr(feature = "uniffi", derive(uniffi::Record))]
1239pub struct OptimizationProgress {
1240    pub is_running: bool,
1241    pub current_round: u32,
1242    pub total_rounds: u32,
1243}
1244
1245/// Response from estimating a conversion, used when preparing a payment that requires conversion
1246#[cfg_attr(feature = "uniffi", derive(uniffi::Record))]
1247#[derive(Debug, Clone, Serialize)]
1248pub struct ConversionEstimate {
1249    /// The conversion options used for the estimate
1250    pub options: ConversionOptions,
1251    /// The estimated amount to be received from the conversion
1252    /// Denominated in satoshis if converting from Bitcoin, otherwise in the token base units.
1253    pub amount: u128,
1254    /// The fee estimated for the conversion
1255    /// Denominated in satoshis if converting from Bitcoin, otherwise in the token base units.
1256    pub fee: u128,
1257}
1258
1259/// The purpose of the conversion, which is used to provide context for the conversion
1260/// if its related to an ongoing payment or a self-transfer.
1261#[cfg_attr(feature = "uniffi", derive(uniffi::Enum))]
1262#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
1263pub enum ConversionPurpose {
1264    /// Conversion is associated with an ongoing payment
1265    OngoingPayment {
1266        /// The payment request of the ongoing payment
1267        payment_request: String,
1268    },
1269    /// Conversion is for self-transfer
1270    SelfTransfer,
1271}
1272
1273/// The status of the conversion
1274#[cfg_attr(feature = "uniffi", derive(uniffi::Enum))]
1275#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
1276pub enum ConversionStatus {
1277    /// The conversion was successful
1278    Completed,
1279    /// The conversion failed and no refund was made yet, which requires action by the SDK to
1280    /// perform the refund. This can happen if there was a failure during the conversion process.
1281    RefundNeeded,
1282    /// The conversion failed and a refund was made
1283    Refunded,
1284}
1285
1286#[cfg_attr(feature = "uniffi", derive(uniffi::Record))]
1287#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
1288pub struct ConversionInfo {
1289    /// The pool id associated with the conversion
1290    pub pool_id: String,
1291    /// The conversion id shared by both sides of the conversion
1292    pub conversion_id: String,
1293    /// The status of the conversion
1294    pub status: ConversionStatus,
1295    /// The fee paid for the conversion
1296    /// Denominated in satoshis if converting from Bitcoin, otherwise in the token base units.
1297    pub fee: Option<u128>,
1298    /// The purpose of the conversion
1299    pub purpose: Option<ConversionPurpose>,
1300}
1301
1302pub(crate) struct TokenConversionPool {
1303    pub(crate) asset_in_address: String,
1304    pub(crate) asset_out_address: String,
1305    pub(crate) pool: Pool,
1306}
1307
1308pub(crate) struct TokenConversionResponse {
1309    /// The sent payment id for the conversion
1310    pub(crate) sent_payment_id: String,
1311    /// The received payment id for the conversion
1312    pub(crate) received_payment_id: String,
1313}
1314
1315/// Options for conversion when fulfilling a payment. When set, the SDK will
1316/// perform a conversion before fulfilling the payment. If not set, the payment
1317/// will only be fulfilled if the wallet has sufficient balance of the required asset.
1318#[derive(Debug, Clone, Serialize)]
1319#[cfg_attr(feature = "uniffi", derive(uniffi::Record))]
1320pub struct ConversionOptions {
1321    /// The type of conversion to perform when fulfilling the payment
1322    pub conversion_type: ConversionType,
1323    /// The optional maximum slippage in basis points (1/100 of a percent) allowed when
1324    /// a conversion is needed to fulfill the payment. Defaults to 50 bps (0.5%) if not set.
1325    /// The conversion will fail if the actual amount received is less than
1326    /// `estimated_amount * (1 - max_slippage_bps / 10_000)`.
1327    #[cfg_attr(feature = "uniffi", uniffi(default=None))]
1328    pub max_slippage_bps: Option<u32>,
1329    /// The optional timeout in seconds to wait for the conversion to complete
1330    /// when fulfilling the payment. This timeout only concerns waiting for the received
1331    /// payment of the conversion. If the timeout is reached before the conversion
1332    /// is complete, the payment will fail. Defaults to 30 seconds if not set.
1333    #[cfg_attr(feature = "uniffi", uniffi(default=None))]
1334    pub completion_timeout_secs: Option<u32>,
1335}
1336
1337#[derive(Debug, Clone, Serialize, PartialEq)]
1338#[cfg_attr(feature = "uniffi", derive(uniffi::Enum))]
1339pub enum ConversionType {
1340    /// Converting from Bitcoin to a token
1341    FromBitcoin,
1342    /// Converting from a token to Bitcoin
1343    ToBitcoin { from_token_identifier: String },
1344}
1345
1346impl ConversionType {
1347    /// Returns the asset addresses for the conversion type
1348    ///
1349    /// # Arguments
1350    ///
1351    /// * `token_identifier` - The token identifier when converting from Bitcoin to a token
1352    ///
1353    /// # Returns
1354    ///
1355    /// Result containing:
1356    /// * (String, String): A tuple containing the asset in address and asset out address
1357    /// * `SdkError`: If the token identifier is required but not provided
1358    pub(crate) fn as_asset_addresses(
1359        &self,
1360        token_identifier: Option<&String>,
1361    ) -> Result<(String, String), SdkError> {
1362        Ok(match self {
1363            ConversionType::FromBitcoin => (
1364                BTC_ASSET_ADDRESS.to_string(),
1365                token_identifier
1366                    .ok_or(SdkError::InvalidInput(
1367                        "Token identifier is required for from Bitcoin conversion".to_string(),
1368                    ))?
1369                    .clone(),
1370            ),
1371            ConversionType::ToBitcoin {
1372                from_token_identifier,
1373            } => (from_token_identifier.clone(), BTC_ASSET_ADDRESS.to_string()),
1374        })
1375    }
1376}
1377
1378#[cfg_attr(feature = "uniffi", derive(uniffi::Record))]
1379pub struct FetchConversionLimitsRequest {
1380    /// The type of conversion, either from or to Bitcoin.
1381    pub conversion_type: ConversionType,
1382    /// The token identifier when converting to a token.
1383    #[cfg_attr(feature = "uniffi", uniffi(default=None))]
1384    pub token_identifier: Option<String>,
1385}
1386
1387#[derive(Debug, Clone, Serialize)]
1388#[cfg_attr(feature = "uniffi", derive(uniffi::Record))]
1389pub struct FetchConversionLimitsResponse {
1390    /// The minimum amount to be converted.
1391    /// Denominated in satoshis if converting from Bitcoin, otherwise in the token base units.
1392    pub min_from_amount: Option<u128>,
1393    /// The minimum amount to be received from the conversion.
1394    /// Denominated in satoshis if converting to Bitcoin, otherwise in the token base units.
1395    pub min_to_amount: Option<u128>,
1396}