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
18pub 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#[derive(Debug, Clone)]
36#[cfg_attr(feature = "uniffi", derive(uniffi::Enum))]
37pub enum Seed {
38 Mnemonic {
40 mnemonic: String,
42 passphrase: Option<String>,
44 },
45 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#[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#[derive(Debug, Clone, Copy, PartialEq, Serialize, Deserialize)]
88#[cfg_attr(feature = "uniffi", derive(uniffi::Enum))]
89pub enum PaymentType {
90 Send,
92 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#[derive(Debug, Clone, Copy, PartialEq, Serialize, Deserialize)]
119#[cfg_attr(feature = "uniffi", derive(uniffi::Enum))]
120pub enum PaymentStatus {
121 Completed,
123 Pending,
125 Failed,
127}
128
129impl PaymentStatus {
130 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#[derive(Debug, Clone, Serialize, Deserialize)]
201#[cfg_attr(feature = "uniffi", derive(uniffi::Record))]
202pub struct Payment {
203 pub id: String,
205 pub payment_type: PaymentType,
207 pub status: PaymentStatus,
209 pub amount: u128,
211 pub fees: u128,
213 pub timestamp: u64,
215 pub method: PaymentMethod,
218 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#[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 invoice_details: Option<SparkInvoicePaymentDetails>,
251 htlc_details: Option<SparkHtlcDetails>,
253 conversion_info: Option<ConversionInfo>,
255 },
256 Token {
257 metadata: TokenMetadata,
258 tx_hash: String,
259 invoice_details: Option<SparkInvoicePaymentDetails>,
261 conversion_info: Option<ConversionInfo>,
263 },
264 Lightning {
265 description: Option<String>,
267 preimage: Option<String>,
269 invoice: String,
273
274 payment_hash: String,
276
277 destination_pubkey: String,
279
280 lnurl_pay_info: Option<LnurlPayInfo>,
282
283 lnurl_withdraw_info: Option<LnurlWithdrawInfo>,
285
286 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 pub description: Option<String>,
302 pub invoice: String,
304}
305
306#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
307#[cfg_attr(feature = "uniffi", derive(uniffi::Record))]
308pub struct SparkHtlcDetails {
309 pub payment_hash: String,
311 pub preimage: Option<String>,
313 pub expiry_time: u64,
315 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 WaitingForPreimage,
324 PreimageShared,
326 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 pub max_deposit_claim_fee: Option<MaxFee>,
418
419 pub lnurl_domain: Option<String>,
421
422 pub prefer_spark_over_lightning: bool,
426
427 pub external_input_parsers: Option<Vec<ExternalInputParser>>,
431 pub use_default_external_input_parsers: bool,
435
436 pub real_time_sync_server_url: Option<String>,
438
439 pub private_enabled_default: bool,
444
445 pub optimization_config: OptimizationConfig,
451}
452
453#[derive(Debug, Clone)]
454#[cfg_attr(feature = "uniffi", derive(uniffi::Record))]
455pub struct OptimizationConfig {
456 pub auto_enabled: bool,
463 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 { amount: u64 },
498 Rate { sat_per_vbyte: u64 },
500 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 { amount: u64 },
531 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#[derive(Debug, Clone)]
616#[cfg_attr(feature = "uniffi", derive(uniffi::Record))]
617pub struct GetInfoRequest {
618 pub ensure_synced: Option<bool>,
619}
620
621#[derive(Debug, Clone, Serialize)]
623#[cfg_attr(feature = "uniffi", derive(uniffi::Record))]
624pub struct GetInfoResponse {
625 pub balance_sats: u64,
627 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 pub issuer_public_key: String,
644 pub name: String,
645 pub ticker: String,
646 pub decimals: u32,
648 pub max_supply: u128,
649 pub is_freezable: bool,
650}
651
652#[derive(Debug, Clone)]
654#[cfg_attr(feature = "uniffi", derive(uniffi::Record))]
655pub struct SyncWalletRequest {}
656
657#[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: Option<u128>,
669 token_identifier: Option<String>,
672 expiry_time: Option<u64>,
674 description: Option<String>,
676 sender_public_key: Option<String>,
678 },
679 BitcoinAddress,
680 Bolt11Invoice {
681 description: String,
682 amount_sats: Option<u64>,
683 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 }, SparkAddress {
701 address: String,
702 fee: u128,
705 token_identifier: Option<String>,
708 },
709 SparkInvoice {
710 spark_invoice_details: SparkInvoiceDetails,
711 fee: u128,
714 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 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 #[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 pub amount_sats: u64,
800 pub withdraw_request: LnurlWithdrawRequestDetails,
801 #[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 pub payment_request: String,
813 pub payment: Option<Payment>,
814}
815
816#[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#[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 #[cfg_attr(feature = "uniffi", uniffi(default=None))]
874 pub amount: Option<u128>,
875 #[cfg_attr(feature = "uniffi", uniffi(default=None))]
878 pub token_identifier: Option<String>,
879 #[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 pub amount: u128,
891 pub token_identifier: Option<String>,
894 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 completion_timeout_secs: Option<u32>,
909 },
910 SparkAddress {
911 htlc_options: Option<SparkHtlcOptions>,
914 },
915}
916
917#[cfg_attr(feature = "uniffi", derive(uniffi::Record))]
918pub struct SparkHtlcOptions {
919 pub payment_hash: String,
921 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 #[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 htlc_status: Option<Vec<SparkHtlcStatus>>,
951 conversion_refund_needed: Option<bool>,
953 },
954 Token {
955 conversion_refund_needed: Option<bool>,
957 tx_hash: Option<String>,
959 },
960}
961
962#[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 #[cfg_attr(feature = "uniffi", uniffi(default=None))]
974 pub payment_details_filter: Option<Vec<PaymentDetailsFilter>>,
975 #[cfg_attr(feature = "uniffi", uniffi(default=None))]
977 pub from_timestamp: Option<u64>,
978 #[cfg_attr(feature = "uniffi", uniffi(default=None))]
980 pub to_timestamp: Option<u64>,
981 #[cfg_attr(feature = "uniffi", uniffi(default=None))]
983 pub offset: Option<u32>,
984 #[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#[derive(Debug, Clone)]
993#[cfg_attr(feature = "uniffi", derive(uniffi::Enum))]
994pub enum AssetFilter {
995 Bitcoin,
996 Token {
997 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#[derive(Debug, Clone, Serialize)]
1026#[cfg_attr(feature = "uniffi", derive(uniffi::Record))]
1027pub struct ListPaymentsResponse {
1028 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#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
1128#[cfg_attr(feature = "uniffi", derive(uniffi::Record))]
1129pub struct KeySetConfig {
1130 pub key_set_type: KeySetType,
1132 pub use_address_index: bool,
1134 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#[derive(Debug, Clone, Serialize)]
1150#[cfg_attr(feature = "uniffi", derive(uniffi::Record))]
1151pub struct ListFiatCurrenciesResponse {
1152 pub currencies: Vec<FiatCurrency>,
1154}
1155
1156#[derive(Debug, Clone, Serialize)]
1158#[cfg_attr(feature = "uniffi", derive(uniffi::Record))]
1159pub struct ListFiatRatesResponse {
1160 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 pub compact: bool,
1185}
1186
1187#[cfg_attr(feature = "uniffi", derive(uniffi::Record))]
1188pub struct SignMessageResponse {
1189 pub pubkey: String,
1190 pub signature: String,
1192}
1193
1194#[cfg_attr(feature = "uniffi", derive(uniffi::Record))]
1195pub struct CheckMessageRequest {
1196 pub message: String,
1198 pub pubkey: String,
1200 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#[cfg_attr(feature = "uniffi", derive(uniffi::Record))]
1247#[derive(Debug, Clone, Serialize)]
1248pub struct ConversionEstimate {
1249 pub options: ConversionOptions,
1251 pub amount: u128,
1254 pub fee: u128,
1257}
1258
1259#[cfg_attr(feature = "uniffi", derive(uniffi::Enum))]
1262#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
1263pub enum ConversionPurpose {
1264 OngoingPayment {
1266 payment_request: String,
1268 },
1269 SelfTransfer,
1271}
1272
1273#[cfg_attr(feature = "uniffi", derive(uniffi::Enum))]
1275#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
1276pub enum ConversionStatus {
1277 Completed,
1279 RefundNeeded,
1282 Refunded,
1284}
1285
1286#[cfg_attr(feature = "uniffi", derive(uniffi::Record))]
1287#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
1288pub struct ConversionInfo {
1289 pub pool_id: String,
1291 pub conversion_id: String,
1293 pub status: ConversionStatus,
1295 pub fee: Option<u128>,
1298 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 pub(crate) sent_payment_id: String,
1311 pub(crate) received_payment_id: String,
1313}
1314
1315#[derive(Debug, Clone, Serialize)]
1319#[cfg_attr(feature = "uniffi", derive(uniffi::Record))]
1320pub struct ConversionOptions {
1321 pub conversion_type: ConversionType,
1323 #[cfg_attr(feature = "uniffi", uniffi(default=None))]
1328 pub max_slippage_bps: Option<u32>,
1329 #[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 FromBitcoin,
1342 ToBitcoin { from_token_identifier: String },
1344}
1345
1346impl ConversionType {
1347 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 pub conversion_type: ConversionType,
1382 #[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 pub min_from_amount: Option<u128>,
1393 pub min_to_amount: Option<u128>,
1396}