1pub(crate) mod adaptors;
2pub mod payment_observer;
3pub use payment_observer::*;
4
5pub use crate::token_conversion::{
7 ConversionEstimate, ConversionInfo, ConversionOptions, ConversionPurpose, ConversionStatus,
8 ConversionType, FetchConversionLimitsRequest, FetchConversionLimitsResponse,
9};
10
11use core::fmt;
12use lnurl_models::RecoverLnurlPayResponse;
13use serde::{Deserialize, Serialize};
14use serde_json::Value;
15use std::{collections::HashMap, fmt::Display, str::FromStr};
16
17use crate::{
18 BitcoinAddressDetails, BitcoinChainService, BitcoinNetwork, Bolt11InvoiceDetails,
19 ExternalInputParser, FiatCurrency, LnurlPayRequestDetails, LnurlWithdrawRequestDetails, Rate,
20 SdkError, SparkInvoiceDetails, SuccessAction, SuccessActionProcessed, error::DepositClaimError,
21};
22
23pub const DEFAULT_EXTERNAL_INPUT_PARSERS: &[(&str, &str, &str)] = &[
26 (
27 "picknpay",
28 "(.*)(za.co.electrum.picknpay)(.*)",
29 "https://cryptoqr.net/.well-known/lnurlp/<input>",
30 ),
31 (
32 "bootleggers",
33 r"(.*)(wigroup\.co|yoyogroup\.co)(.*)",
34 "https://cryptoqr.net/.well-known/lnurlw/<input>",
35 ),
36];
37
38#[derive(Debug, Clone)]
41#[cfg_attr(feature = "uniffi", derive(uniffi::Enum))]
42pub enum Seed {
43 Mnemonic {
45 mnemonic: String,
47 passphrase: Option<String>,
49 },
50 Entropy(Vec<u8>),
52}
53
54impl Seed {
55 pub fn to_bytes(&self) -> Result<Vec<u8>, SdkError> {
56 match self {
57 Seed::Mnemonic {
58 mnemonic,
59 passphrase,
60 } => {
61 let mnemonic = bip39::Mnemonic::parse(mnemonic)
62 .map_err(|e| SdkError::Generic(e.to_string()))?;
63
64 Ok(mnemonic
65 .to_seed(passphrase.as_deref().unwrap_or(""))
66 .to_vec())
67 }
68 Seed::Entropy(entropy) => Ok(entropy.clone()),
69 }
70 }
71}
72
73#[cfg_attr(feature = "uniffi", derive(uniffi::Record))]
74pub struct ConnectRequest {
75 pub config: Config,
76 pub seed: Seed,
77 pub storage_dir: String,
78}
79
80#[cfg(not(all(target_family = "wasm", target_os = "unknown")))]
84#[cfg_attr(feature = "uniffi", derive(uniffi::Record))]
85pub struct ConnectWithSignerRequest {
86 pub config: Config,
87 pub signer: std::sync::Arc<dyn crate::signer::ExternalSigner>,
88 pub storage_dir: String,
89}
90
91#[derive(Debug, Clone, Copy, PartialEq, Serialize, Deserialize)]
93#[cfg_attr(feature = "uniffi", derive(uniffi::Enum))]
94pub enum PaymentType {
95 Send,
97 Receive,
99}
100
101impl fmt::Display for PaymentType {
102 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
103 match self {
104 PaymentType::Send => write!(f, "send"),
105 PaymentType::Receive => write!(f, "receive"),
106 }
107 }
108}
109
110impl FromStr for PaymentType {
111 type Err = String;
112
113 fn from_str(s: &str) -> Result<Self, Self::Err> {
114 Ok(match s.to_lowercase().as_str() {
115 "receive" => PaymentType::Receive,
116 "send" => PaymentType::Send,
117 _ => return Err(format!("invalid payment type '{s}'")),
118 })
119 }
120}
121
122#[derive(Debug, Clone, Copy, PartialEq, Serialize, Deserialize)]
124#[cfg_attr(feature = "uniffi", derive(uniffi::Enum))]
125pub enum PaymentStatus {
126 Completed,
128 Pending,
130 Failed,
132}
133
134impl PaymentStatus {
135 pub fn is_final(&self) -> bool {
137 matches!(self, PaymentStatus::Completed | PaymentStatus::Failed)
138 }
139}
140
141impl fmt::Display for PaymentStatus {
142 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
143 match self {
144 PaymentStatus::Completed => write!(f, "completed"),
145 PaymentStatus::Pending => write!(f, "pending"),
146 PaymentStatus::Failed => write!(f, "failed"),
147 }
148 }
149}
150
151impl FromStr for PaymentStatus {
152 type Err = String;
153
154 fn from_str(s: &str) -> Result<Self, Self::Err> {
155 Ok(match s.to_lowercase().as_str() {
156 "completed" => PaymentStatus::Completed,
157 "pending" => PaymentStatus::Pending,
158 "failed" => PaymentStatus::Failed,
159 _ => return Err(format!("Invalid payment status '{s}'")),
160 })
161 }
162}
163
164#[derive(Debug, Clone, Copy, PartialEq, Serialize, Deserialize)]
165#[cfg_attr(feature = "uniffi", derive(uniffi::Enum))]
166pub enum PaymentMethod {
167 Lightning,
168 Spark,
169 Token,
170 Deposit,
171 Withdraw,
172 Unknown,
173}
174
175impl Display for PaymentMethod {
176 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
177 match self {
178 PaymentMethod::Lightning => write!(f, "lightning"),
179 PaymentMethod::Spark => write!(f, "spark"),
180 PaymentMethod::Token => write!(f, "token"),
181 PaymentMethod::Deposit => write!(f, "deposit"),
182 PaymentMethod::Withdraw => write!(f, "withdraw"),
183 PaymentMethod::Unknown => write!(f, "unknown"),
184 }
185 }
186}
187
188impl FromStr for PaymentMethod {
189 type Err = ();
190
191 fn from_str(s: &str) -> Result<Self, Self::Err> {
192 match s {
193 "lightning" => Ok(PaymentMethod::Lightning),
194 "spark" => Ok(PaymentMethod::Spark),
195 "token" => Ok(PaymentMethod::Token),
196 "deposit" => Ok(PaymentMethod::Deposit),
197 "withdraw" => Ok(PaymentMethod::Withdraw),
198 "unknown" => Ok(PaymentMethod::Unknown),
199 _ => Err(()),
200 }
201 }
202}
203
204#[derive(Debug, Clone, Serialize, Deserialize)]
206#[cfg_attr(feature = "uniffi", derive(uniffi::Record))]
207pub struct Payment {
208 pub id: String,
210 pub payment_type: PaymentType,
212 pub status: PaymentStatus,
214 pub amount: u128,
216 pub fees: u128,
218 pub timestamp: u64,
220 pub method: PaymentMethod,
223 pub details: Option<PaymentDetails>,
225 pub conversion_details: Option<ConversionDetails>,
227}
228
229#[derive(Debug, Clone, Serialize, Deserialize)]
231#[cfg_attr(feature = "uniffi", derive(uniffi::Record))]
232pub struct ConversionDetails {
233 pub from: ConversionStep,
235 pub to: ConversionStep,
237}
238
239impl TryFrom<&Vec<Payment>> for ConversionDetails {
243 type Error = SdkError;
244 fn try_from(payments: &Vec<Payment>) -> Result<Self, Self::Error> {
245 let from = payments
246 .iter()
247 .find(|p| p.payment_type == PaymentType::Send)
248 .ok_or(SdkError::Generic(
249 "From step of conversion not found".to_string(),
250 ))?;
251 let to = payments
252 .iter()
253 .find(|p| p.payment_type == PaymentType::Receive)
254 .ok_or(SdkError::Generic(
255 "To step of conversion not found".to_string(),
256 ))?;
257 Ok(ConversionDetails {
258 from: from.try_into()?,
259 to: to.try_into()?,
260 })
261 }
262}
263
264#[derive(Debug, Clone, Serialize, Deserialize)]
266#[cfg_attr(feature = "uniffi", derive(uniffi::Record))]
267pub struct ConversionStep {
268 pub payment_id: String,
270 pub amount: u128,
272 pub fee: u128,
275 pub method: PaymentMethod,
277 pub token_metadata: Option<TokenMetadata>,
279}
280
281impl TryFrom<&Payment> for ConversionStep {
285 type Error = SdkError;
286 fn try_from(payment: &Payment) -> Result<Self, Self::Error> {
287 let (conversion_info, token_metadata) = match &payment.details {
288 Some(PaymentDetails::Spark {
289 conversion_info: Some(info),
290 ..
291 }) => (info, None),
292 Some(PaymentDetails::Token {
293 conversion_info: Some(info),
294 metadata,
295 ..
296 }) => (info, Some(metadata.clone())),
297 _ => {
298 return Err(SdkError::Generic(format!(
299 "No conversion info available for payment {}",
300 payment.id
301 )));
302 }
303 };
304 Ok(ConversionStep {
305 payment_id: payment.id.clone(),
306 amount: payment.amount,
307 fee: payment
308 .fees
309 .saturating_add(conversion_info.fee.unwrap_or(0)),
310 method: payment.method,
311 token_metadata,
312 })
313 }
314}
315
316#[cfg(feature = "uniffi")]
317uniffi::custom_type!(u128, String);
318
319#[cfg(feature = "uniffi")]
320impl crate::UniffiCustomTypeConverter for u128 {
321 type Builtin = String;
322
323 fn into_custom(val: Self::Builtin) -> ::uniffi::Result<Self>
324 where
325 Self: ::std::marker::Sized,
326 {
327 val.parse::<u128>()
328 .map_err(uniffi::deps::anyhow::Error::msg)
329 }
330
331 fn from_custom(obj: Self) -> Self::Builtin {
332 obj.to_string()
333 }
334}
335
336#[allow(clippy::large_enum_variant)]
339#[derive(Debug, Clone, Serialize, Deserialize)]
340#[cfg_attr(feature = "uniffi", derive(uniffi::Enum))]
341pub enum PaymentDetails {
342 Spark {
343 invoice_details: Option<SparkInvoicePaymentDetails>,
345 htlc_details: Option<SparkHtlcDetails>,
347 conversion_info: Option<ConversionInfo>,
349 },
350 Token {
351 metadata: TokenMetadata,
352 tx_hash: String,
353 tx_type: TokenTransactionType,
354 invoice_details: Option<SparkInvoicePaymentDetails>,
356 conversion_info: Option<ConversionInfo>,
358 },
359 Lightning {
360 description: Option<String>,
362 invoice: String,
366
367 destination_pubkey: String,
369
370 htlc_details: SparkHtlcDetails,
372
373 lnurl_pay_info: Option<LnurlPayInfo>,
375
376 lnurl_withdraw_info: Option<LnurlWithdrawInfo>,
378
379 lnurl_receive_metadata: Option<LnurlReceiveMetadata>,
381 },
382 Withdraw {
383 tx_id: String,
384 },
385 Deposit {
386 tx_id: String,
387 },
388}
389
390#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
391#[cfg_attr(feature = "uniffi", derive(uniffi::Enum))]
392pub enum TokenTransactionType {
393 Transfer,
394 Mint,
395 Burn,
396}
397
398impl fmt::Display for TokenTransactionType {
399 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
400 match self {
401 TokenTransactionType::Transfer => write!(f, "transfer"),
402 TokenTransactionType::Mint => write!(f, "mint"),
403 TokenTransactionType::Burn => write!(f, "burn"),
404 }
405 }
406}
407
408impl FromStr for TokenTransactionType {
409 type Err = String;
410
411 fn from_str(s: &str) -> Result<Self, Self::Err> {
412 match s.to_lowercase().as_str() {
413 "transfer" => Ok(TokenTransactionType::Transfer),
414 "mint" => Ok(TokenTransactionType::Mint),
415 "burn" => Ok(TokenTransactionType::Burn),
416 _ => Err(format!("Invalid token transaction type '{s}'")),
417 }
418 }
419}
420
421#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
422#[cfg_attr(feature = "uniffi", derive(uniffi::Record))]
423pub struct SparkInvoicePaymentDetails {
424 pub description: Option<String>,
426 pub invoice: String,
428}
429
430#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
431#[cfg_attr(feature = "uniffi", derive(uniffi::Record))]
432pub struct SparkHtlcDetails {
433 pub payment_hash: String,
435 pub preimage: Option<String>,
437 pub expiry_time: u64,
439 pub status: SparkHtlcStatus,
441}
442
443#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq)]
444#[cfg_attr(feature = "uniffi", derive(uniffi::Enum))]
445pub enum SparkHtlcStatus {
446 WaitingForPreimage,
448 PreimageShared,
450 Returned,
452}
453
454impl fmt::Display for SparkHtlcStatus {
455 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
456 match self {
457 SparkHtlcStatus::WaitingForPreimage => write!(f, "WaitingForPreimage"),
458 SparkHtlcStatus::PreimageShared => write!(f, "PreimageShared"),
459 SparkHtlcStatus::Returned => write!(f, "Returned"),
460 }
461 }
462}
463
464impl FromStr for SparkHtlcStatus {
465 type Err = String;
466
467 fn from_str(s: &str) -> Result<Self, Self::Err> {
468 match s {
469 "WaitingForPreimage" => Ok(SparkHtlcStatus::WaitingForPreimage),
470 "PreimageShared" => Ok(SparkHtlcStatus::PreimageShared),
471 "Returned" => Ok(SparkHtlcStatus::Returned),
472 _ => Err("Invalid Spark HTLC status".to_string()),
473 }
474 }
475}
476
477#[derive(Debug, Clone, Copy)]
478#[cfg_attr(feature = "uniffi", derive(uniffi::Enum))]
479pub enum Network {
480 Mainnet,
481 Regtest,
482}
483
484impl std::fmt::Display for Network {
485 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
486 match self {
487 Network::Mainnet => write!(f, "Mainnet"),
488 Network::Regtest => write!(f, "Regtest"),
489 }
490 }
491}
492
493impl From<Network> for BitcoinNetwork {
494 fn from(network: Network) -> Self {
495 match network {
496 Network::Mainnet => BitcoinNetwork::Bitcoin,
497 Network::Regtest => BitcoinNetwork::Regtest,
498 }
499 }
500}
501
502impl From<Network> for breez_sdk_common::network::BitcoinNetwork {
503 fn from(network: Network) -> Self {
504 match network {
505 Network::Mainnet => breez_sdk_common::network::BitcoinNetwork::Bitcoin,
506 Network::Regtest => breez_sdk_common::network::BitcoinNetwork::Regtest,
507 }
508 }
509}
510
511impl From<Network> for bitcoin::Network {
512 fn from(network: Network) -> Self {
513 match network {
514 Network::Mainnet => bitcoin::Network::Bitcoin,
515 Network::Regtest => bitcoin::Network::Regtest,
516 }
517 }
518}
519
520impl FromStr for Network {
521 type Err = String;
522
523 fn from_str(s: &str) -> Result<Self, Self::Err> {
524 match s {
525 "mainnet" => Ok(Network::Mainnet),
526 "regtest" => Ok(Network::Regtest),
527 _ => Err("Invalid network".to_string()),
528 }
529 }
530}
531
532#[derive(Debug, Clone)]
533#[cfg_attr(feature = "uniffi", derive(uniffi::Record))]
534#[allow(clippy::struct_excessive_bools)]
535pub struct Config {
536 pub api_key: Option<String>,
537 pub network: Network,
538 pub sync_interval_secs: u32,
539
540 pub max_deposit_claim_fee: Option<MaxFee>,
543
544 pub lnurl_domain: Option<String>,
546
547 pub prefer_spark_over_lightning: bool,
551
552 pub external_input_parsers: Option<Vec<ExternalInputParser>>,
556 pub use_default_external_input_parsers: bool,
560
561 pub real_time_sync_server_url: Option<String>,
563
564 pub private_enabled_default: bool,
569
570 pub optimization_config: OptimizationConfig,
576
577 pub stable_balance_config: Option<StableBalanceConfig>,
582
583 pub max_concurrent_claims: u32,
587
588 pub support_lnurl_verify: bool,
591}
592
593#[derive(Debug, Clone)]
594#[cfg_attr(feature = "uniffi", derive(uniffi::Record))]
595pub struct OptimizationConfig {
596 pub auto_enabled: bool,
603 pub multiplicity: u8,
615}
616
617#[derive(Debug, Clone)]
629#[cfg_attr(feature = "uniffi", derive(uniffi::Record))]
630pub struct StableBalanceConfig {
631 pub token_identifier: String,
633
634 #[cfg_attr(feature = "uniffi", uniffi(default = None))]
639 pub threshold_sats: Option<u64>,
640
641 #[cfg_attr(feature = "uniffi", uniffi(default = None))]
645 pub max_slippage_bps: Option<u32>,
646
647 #[cfg_attr(feature = "uniffi", uniffi(default = None))]
652 pub reserved_sats: Option<u64>,
653}
654
655impl Config {
656 pub fn validate(&self) -> Result<(), SdkError> {
660 if self.max_concurrent_claims == 0 {
661 return Err(SdkError::InvalidInput(
662 "max_concurrent_claims must be greater than 0".to_string(),
663 ));
664 }
665 Ok(())
666 }
667
668 pub(crate) fn get_all_external_input_parsers(&self) -> Vec<ExternalInputParser> {
669 let mut external_input_parsers = Vec::new();
670 if self.use_default_external_input_parsers {
671 let default_parsers = DEFAULT_EXTERNAL_INPUT_PARSERS
672 .iter()
673 .map(|(id, regex, url)| ExternalInputParser {
674 provider_id: (*id).to_string(),
675 input_regex: (*regex).to_string(),
676 parser_url: (*url).to_string(),
677 })
678 .collect::<Vec<_>>();
679 external_input_parsers.extend(default_parsers);
680 }
681 external_input_parsers.extend(self.external_input_parsers.clone().unwrap_or_default());
682
683 external_input_parsers
684 }
685}
686
687#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
688#[cfg_attr(feature = "uniffi", derive(uniffi::Enum))]
689pub enum MaxFee {
690 Fixed { amount: u64 },
692 Rate { sat_per_vbyte: u64 },
694 NetworkRecommended { leeway_sat_per_vbyte: u64 },
696}
697
698impl MaxFee {
699 pub(crate) async fn to_fee(&self, client: &dyn BitcoinChainService) -> Result<Fee, SdkError> {
700 match self {
701 MaxFee::Fixed { amount } => Ok(Fee::Fixed { amount: *amount }),
702 MaxFee::Rate { sat_per_vbyte } => Ok(Fee::Rate {
703 sat_per_vbyte: *sat_per_vbyte,
704 }),
705 MaxFee::NetworkRecommended {
706 leeway_sat_per_vbyte,
707 } => {
708 let recommended_fees = client.recommended_fees().await?;
709 let max_fee_rate = recommended_fees
710 .fastest_fee
711 .saturating_add(*leeway_sat_per_vbyte);
712 Ok(Fee::Rate {
713 sat_per_vbyte: max_fee_rate,
714 })
715 }
716 }
717 }
718}
719
720#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
721#[cfg_attr(feature = "uniffi", derive(uniffi::Enum))]
722pub enum Fee {
723 Fixed { amount: u64 },
725 Rate { sat_per_vbyte: u64 },
727}
728
729impl Fee {
730 pub fn to_sats(&self, vbytes: u64) -> u64 {
731 match self {
732 Fee::Fixed { amount } => *amount,
733 Fee::Rate { sat_per_vbyte } => sat_per_vbyte.saturating_mul(vbytes),
734 }
735 }
736}
737
738#[derive(Debug, Clone, Serialize, Deserialize)]
739#[cfg_attr(feature = "uniffi", derive(uniffi::Record))]
740pub struct DepositInfo {
741 pub txid: String,
742 pub vout: u32,
743 pub amount_sats: u64,
744 pub refund_tx: Option<String>,
745 pub refund_tx_id: Option<String>,
746 pub claim_error: Option<DepositClaimError>,
747}
748
749#[cfg_attr(feature = "uniffi", derive(uniffi::Record))]
750pub struct ClaimDepositRequest {
751 pub txid: String,
752 pub vout: u32,
753 #[cfg_attr(feature = "uniffi", uniffi(default=None))]
754 pub max_fee: Option<MaxFee>,
755}
756
757#[derive(Debug, Clone, Serialize)]
758#[cfg_attr(feature = "uniffi", derive(uniffi::Record))]
759pub struct ClaimDepositResponse {
760 pub payment: Payment,
761}
762
763#[derive(Debug, Clone, Serialize)]
764#[cfg_attr(feature = "uniffi", derive(uniffi::Record))]
765pub struct RefundDepositRequest {
766 pub txid: String,
767 pub vout: u32,
768 pub destination_address: String,
769 pub fee: Fee,
770}
771
772#[derive(Debug, Clone, Serialize)]
773#[cfg_attr(feature = "uniffi", derive(uniffi::Record))]
774pub struct RefundDepositResponse {
775 pub tx_id: String,
776 pub tx_hex: String,
777}
778
779#[derive(Debug, Clone, Serialize)]
780#[cfg_attr(feature = "uniffi", derive(uniffi::Record))]
781pub struct ListUnclaimedDepositsRequest {}
782
783#[derive(Debug, Clone, Serialize)]
784#[cfg_attr(feature = "uniffi", derive(uniffi::Record))]
785pub struct ListUnclaimedDepositsResponse {
786 pub deposits: Vec<DepositInfo>,
787}
788
789#[derive(Debug, Clone, Default)]
791#[cfg_attr(feature = "uniffi", derive(uniffi::Record))]
792pub struct BuyBitcoinRequest {
793 #[cfg_attr(feature = "uniffi", uniffi(default=None))]
796 pub locked_amount_sat: Option<u64>,
797 #[cfg_attr(feature = "uniffi", uniffi(default=None))]
799 pub redirect_url: Option<String>,
800}
801
802#[derive(Debug, Clone, Serialize)]
804#[cfg_attr(feature = "uniffi", derive(uniffi::Record))]
805pub struct BuyBitcoinResponse {
806 pub url: String,
808}
809
810impl std::fmt::Display for MaxFee {
811 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
812 match self {
813 MaxFee::Fixed { amount } => write!(f, "Fixed: {amount}"),
814 MaxFee::Rate { sat_per_vbyte } => write!(f, "Rate: {sat_per_vbyte}"),
815 MaxFee::NetworkRecommended {
816 leeway_sat_per_vbyte,
817 } => write!(f, "NetworkRecommended: {leeway_sat_per_vbyte}"),
818 }
819 }
820}
821
822#[derive(Debug, Clone)]
823#[cfg_attr(feature = "uniffi", derive(uniffi::Record))]
824pub struct Credentials {
825 pub username: String,
826 pub password: String,
827}
828
829#[derive(Debug, Clone)]
831#[cfg_attr(feature = "uniffi", derive(uniffi::Record))]
832pub struct GetInfoRequest {
833 pub ensure_synced: Option<bool>,
834}
835
836#[derive(Debug, Clone, Serialize)]
838#[cfg_attr(feature = "uniffi", derive(uniffi::Record))]
839pub struct GetInfoResponse {
840 pub identity_pubkey: String,
842 pub balance_sats: u64,
844 pub token_balances: HashMap<String, TokenBalance>,
846}
847
848#[derive(Debug, Clone, Serialize, Deserialize)]
849#[cfg_attr(feature = "uniffi", derive(uniffi::Record))]
850pub struct TokenBalance {
851 pub balance: u128,
852 pub token_metadata: TokenMetadata,
853}
854
855#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
856#[cfg_attr(feature = "uniffi", derive(uniffi::Record))]
857pub struct TokenMetadata {
858 pub identifier: String,
859 pub issuer_public_key: String,
861 pub name: String,
862 pub ticker: String,
863 pub decimals: u32,
865 pub max_supply: u128,
866 pub is_freezable: bool,
867}
868
869#[derive(Debug, Clone)]
871#[cfg_attr(feature = "uniffi", derive(uniffi::Record))]
872pub struct SyncWalletRequest {}
873
874#[derive(Debug, Clone, Serialize)]
876#[cfg_attr(feature = "uniffi", derive(uniffi::Record))]
877pub struct SyncWalletResponse {}
878
879#[derive(Debug, Clone, Serialize)]
880#[cfg_attr(feature = "uniffi", derive(uniffi::Enum))]
881pub enum ReceivePaymentMethod {
882 SparkAddress,
883 SparkInvoice {
884 amount: Option<u128>,
886 token_identifier: Option<String>,
889 expiry_time: Option<u64>,
891 description: Option<String>,
893 sender_public_key: Option<String>,
895 },
896 BitcoinAddress,
897 Bolt11Invoice {
898 description: String,
899 amount_sats: Option<u64>,
900 expiry_secs: Option<u32>,
902 payment_hash: Option<String>,
906 },
907}
908
909#[derive(Debug, Clone, Serialize)]
910#[cfg_attr(feature = "uniffi", derive(uniffi::Enum))]
911pub enum SendPaymentMethod {
912 BitcoinAddress {
913 address: BitcoinAddressDetails,
914 fee_quote: SendOnchainFeeQuote,
915 },
916 Bolt11Invoice {
917 invoice_details: Bolt11InvoiceDetails,
918 spark_transfer_fee_sats: Option<u64>,
919 lightning_fee_sats: u64,
920 }, SparkAddress {
922 address: String,
923 fee: u128,
926 token_identifier: Option<String>,
929 },
930 SparkInvoice {
931 spark_invoice_details: SparkInvoiceDetails,
932 fee: u128,
935 token_identifier: Option<String>,
938 },
939}
940
941#[cfg_attr(feature = "uniffi", derive(uniffi::Record))]
942#[derive(Debug, Clone, Serialize)]
943pub struct SendOnchainFeeQuote {
944 pub id: String,
945 pub expires_at: u64,
946 pub speed_fast: SendOnchainSpeedFeeQuote,
947 pub speed_medium: SendOnchainSpeedFeeQuote,
948 pub speed_slow: SendOnchainSpeedFeeQuote,
949}
950
951#[cfg_attr(feature = "uniffi", derive(uniffi::Record))]
952#[derive(Debug, Clone, Serialize)]
953pub struct SendOnchainSpeedFeeQuote {
954 pub user_fee_sat: u64,
955 pub l1_broadcast_fee_sat: u64,
956}
957
958impl SendOnchainSpeedFeeQuote {
959 pub fn total_fee_sat(&self) -> u64 {
960 self.user_fee_sat.saturating_add(self.l1_broadcast_fee_sat)
961 }
962}
963
964#[cfg_attr(feature = "uniffi", derive(uniffi::Record))]
965pub struct ReceivePaymentRequest {
966 pub payment_method: ReceivePaymentMethod,
967}
968
969#[derive(Debug, Clone, Serialize)]
970#[cfg_attr(feature = "uniffi", derive(uniffi::Record))]
971pub struct ReceivePaymentResponse {
972 pub payment_request: String,
973 pub fee: u128,
976}
977
978#[cfg_attr(feature = "uniffi", derive(uniffi::Record))]
979pub struct PrepareLnurlPayRequest {
980 pub amount_sats: u64,
982 pub pay_request: LnurlPayRequestDetails,
983 #[cfg_attr(feature = "uniffi", uniffi(default=None))]
984 pub comment: Option<String>,
985 #[cfg_attr(feature = "uniffi", uniffi(default=None))]
986 pub validate_success_action_url: Option<bool>,
987 #[cfg_attr(feature = "uniffi", uniffi(default=None))]
989 pub conversion_options: Option<ConversionOptions>,
990 #[cfg_attr(feature = "uniffi", uniffi(default=None))]
992 pub fee_policy: Option<FeePolicy>,
993}
994
995#[derive(Debug, Clone)]
996#[cfg_attr(feature = "uniffi", derive(uniffi::Record))]
997pub struct PrepareLnurlPayResponse {
998 pub amount_sats: u64,
1000 pub comment: Option<String>,
1001 pub pay_request: LnurlPayRequestDetails,
1002 pub fee_sats: u64,
1005 pub invoice_details: Bolt11InvoiceDetails,
1006 pub success_action: Option<SuccessAction>,
1007 pub conversion_estimate: Option<ConversionEstimate>,
1009 pub fee_policy: FeePolicy,
1011}
1012
1013#[cfg_attr(feature = "uniffi", derive(uniffi::Record))]
1014pub struct LnurlPayRequest {
1015 pub prepare_response: PrepareLnurlPayResponse,
1016 #[cfg_attr(feature = "uniffi", uniffi(default=None))]
1020 pub idempotency_key: Option<String>,
1021}
1022
1023#[derive(Debug, Serialize)]
1024#[cfg_attr(feature = "uniffi", derive(uniffi::Record))]
1025pub struct LnurlPayResponse {
1026 pub payment: Payment,
1027 pub success_action: Option<SuccessActionProcessed>,
1028}
1029
1030#[cfg_attr(feature = "uniffi", derive(uniffi::Record))]
1031pub struct LnurlWithdrawRequest {
1032 pub amount_sats: u64,
1035 pub withdraw_request: LnurlWithdrawRequestDetails,
1036 #[cfg_attr(feature = "uniffi", uniffi(default=None))]
1040 pub completion_timeout_secs: Option<u32>,
1041}
1042
1043#[derive(Debug, Serialize)]
1044#[cfg_attr(feature = "uniffi", derive(uniffi::Record))]
1045pub struct LnurlWithdrawResponse {
1046 pub payment_request: String,
1048 pub payment: Option<Payment>,
1049}
1050
1051#[derive(Clone, Debug, Default, Deserialize, PartialEq, Serialize)]
1053#[cfg_attr(feature = "uniffi", derive(uniffi::Record))]
1054pub struct LnurlPayInfo {
1055 pub ln_address: Option<String>,
1056 pub comment: Option<String>,
1057 pub domain: Option<String>,
1058 pub metadata: Option<String>,
1059 pub processed_success_action: Option<SuccessActionProcessed>,
1060 pub raw_success_action: Option<SuccessAction>,
1061}
1062
1063#[derive(Clone, Debug, Deserialize, Serialize)]
1065#[cfg_attr(feature = "uniffi", derive(uniffi::Record))]
1066pub struct LnurlWithdrawInfo {
1067 pub withdraw_url: String,
1068}
1069
1070impl LnurlPayInfo {
1071 pub fn extract_description(&self) -> Option<String> {
1072 let Some(metadata) = &self.metadata else {
1073 return None;
1074 };
1075
1076 let Ok(metadata) = serde_json::from_str::<Vec<Vec<Value>>>(metadata) else {
1077 return None;
1078 };
1079
1080 for arr in metadata {
1081 if arr.len() != 2 {
1082 continue;
1083 }
1084 if let (Some(key), Some(value)) = (arr[0].as_str(), arr[1].as_str())
1085 && key == "text/plain"
1086 {
1087 return Some(value.to_string());
1088 }
1089 }
1090
1091 None
1092 }
1093}
1094
1095#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, Serialize, Deserialize)]
1097#[cfg_attr(feature = "uniffi", derive(uniffi::Enum))]
1098pub enum FeePolicy {
1099 #[default]
1102 FeesExcluded,
1103 FeesIncluded,
1106}
1107
1108#[cfg_attr(feature = "uniffi", derive(uniffi::Enum))]
1109#[derive(Debug, Clone, Serialize)]
1110pub enum OnchainConfirmationSpeed {
1111 Fast,
1112 Medium,
1113 Slow,
1114}
1115
1116#[cfg_attr(feature = "uniffi", derive(uniffi::Record))]
1117pub struct PrepareSendPaymentRequest {
1118 pub payment_request: String,
1119 #[cfg_attr(feature = "uniffi", uniffi(default=None))]
1124 pub amount: Option<u128>,
1125 #[cfg_attr(feature = "uniffi", uniffi(default=None))]
1128 pub token_identifier: Option<String>,
1129 #[cfg_attr(feature = "uniffi", uniffi(default=None))]
1131 pub conversion_options: Option<ConversionOptions>,
1132 #[cfg_attr(feature = "uniffi", uniffi(default=None))]
1134 pub fee_policy: Option<FeePolicy>,
1135}
1136
1137#[derive(Debug, Clone, Serialize)]
1138#[cfg_attr(feature = "uniffi", derive(uniffi::Record))]
1139pub struct PrepareSendPaymentResponse {
1140 pub payment_method: SendPaymentMethod,
1141 pub amount: u128,
1144 pub token_identifier: Option<String>,
1147 pub conversion_estimate: Option<ConversionEstimate>,
1149 pub fee_policy: FeePolicy,
1151}
1152
1153#[cfg_attr(feature = "uniffi", derive(uniffi::Enum))]
1154pub enum SendPaymentOptions {
1155 BitcoinAddress {
1156 confirmation_speed: OnchainConfirmationSpeed,
1158 },
1159 Bolt11Invoice {
1160 prefer_spark: bool,
1161
1162 completion_timeout_secs: Option<u32>,
1165 },
1166 SparkAddress {
1167 htlc_options: Option<SparkHtlcOptions>,
1170 },
1171}
1172
1173#[cfg_attr(feature = "uniffi", derive(uniffi::Record))]
1174pub struct SparkHtlcOptions {
1175 pub payment_hash: String,
1177 pub expiry_duration_secs: u64,
1180}
1181
1182#[cfg_attr(feature = "uniffi", derive(uniffi::Record))]
1183pub struct SendPaymentRequest {
1184 pub prepare_response: PrepareSendPaymentResponse,
1185 #[cfg_attr(feature = "uniffi", uniffi(default=None))]
1186 pub options: Option<SendPaymentOptions>,
1187 #[cfg_attr(feature = "uniffi", uniffi(default=None))]
1192 pub idempotency_key: Option<String>,
1193}
1194
1195#[derive(Debug, Clone, Serialize)]
1196#[cfg_attr(feature = "uniffi", derive(uniffi::Record))]
1197pub struct SendPaymentResponse {
1198 pub payment: Payment,
1199}
1200
1201#[derive(Debug, Clone)]
1202#[cfg_attr(feature = "uniffi", derive(uniffi::Enum))]
1203pub enum PaymentDetailsFilter {
1204 Spark {
1205 htlc_status: Option<Vec<SparkHtlcStatus>>,
1207 conversion_refund_needed: Option<bool>,
1209 },
1210 Token {
1211 conversion_refund_needed: Option<bool>,
1213 tx_hash: Option<String>,
1215 tx_type: Option<TokenTransactionType>,
1217 },
1218 Lightning {
1219 htlc_status: Option<Vec<SparkHtlcStatus>>,
1221 },
1222}
1223
1224#[derive(Debug, Clone, Default)]
1226#[cfg_attr(feature = "uniffi", derive(uniffi::Record))]
1227pub struct ListPaymentsRequest {
1228 #[cfg_attr(feature = "uniffi", uniffi(default=None))]
1229 pub type_filter: Option<Vec<PaymentType>>,
1230 #[cfg_attr(feature = "uniffi", uniffi(default=None))]
1231 pub status_filter: Option<Vec<PaymentStatus>>,
1232 #[cfg_attr(feature = "uniffi", uniffi(default=None))]
1233 pub asset_filter: Option<AssetFilter>,
1234 #[cfg_attr(feature = "uniffi", uniffi(default=None))]
1236 pub payment_details_filter: Option<Vec<PaymentDetailsFilter>>,
1237 #[cfg_attr(feature = "uniffi", uniffi(default=None))]
1239 pub from_timestamp: Option<u64>,
1240 #[cfg_attr(feature = "uniffi", uniffi(default=None))]
1242 pub to_timestamp: Option<u64>,
1243 #[cfg_attr(feature = "uniffi", uniffi(default=None))]
1245 pub offset: Option<u32>,
1246 #[cfg_attr(feature = "uniffi", uniffi(default=None))]
1248 pub limit: Option<u32>,
1249 #[cfg_attr(feature = "uniffi", uniffi(default=None))]
1250 pub sort_ascending: Option<bool>,
1251}
1252
1253#[derive(Debug, Clone)]
1255#[cfg_attr(feature = "uniffi", derive(uniffi::Enum))]
1256pub enum AssetFilter {
1257 Bitcoin,
1258 Token {
1259 token_identifier: Option<String>,
1261 },
1262}
1263
1264impl FromStr for AssetFilter {
1265 type Err = String;
1266
1267 fn from_str(s: &str) -> Result<Self, Self::Err> {
1268 Ok(match s.to_lowercase().as_str() {
1269 "bitcoin" => AssetFilter::Bitcoin,
1270 "token" => AssetFilter::Token {
1271 token_identifier: None,
1272 },
1273 str if str.starts_with("token:") => AssetFilter::Token {
1274 token_identifier: Some(
1275 str.split_once(':')
1276 .ok_or(format!("Invalid asset filter '{s}'"))?
1277 .1
1278 .to_string(),
1279 ),
1280 },
1281 _ => return Err(format!("Invalid asset filter '{s}'")),
1282 })
1283 }
1284}
1285
1286#[derive(Debug, Clone, Serialize)]
1288#[cfg_attr(feature = "uniffi", derive(uniffi::Record))]
1289pub struct ListPaymentsResponse {
1290 pub payments: Vec<Payment>,
1292}
1293
1294#[cfg_attr(feature = "uniffi", derive(uniffi::Record))]
1295pub struct GetPaymentRequest {
1296 pub payment_id: String,
1297}
1298
1299#[derive(Debug, Clone, Serialize)]
1300#[cfg_attr(feature = "uniffi", derive(uniffi::Record))]
1301pub struct GetPaymentResponse {
1302 pub payment: Payment,
1303}
1304
1305#[cfg_attr(feature = "uniffi", uniffi::export(callback_interface))]
1306pub trait Logger: Send + Sync {
1307 fn log(&self, l: LogEntry);
1308}
1309
1310#[derive(Debug, Clone, Serialize)]
1311#[cfg_attr(feature = "uniffi", derive(uniffi::Record))]
1312pub struct LogEntry {
1313 pub line: String,
1314 pub level: String,
1315}
1316
1317#[cfg_attr(feature = "uniffi", derive(uniffi::Record))]
1318#[derive(Debug, Clone, Serialize, Deserialize)]
1319pub struct CheckLightningAddressRequest {
1320 pub username: String,
1321}
1322
1323#[cfg_attr(feature = "uniffi", derive(uniffi::Record))]
1324#[derive(Debug, Clone, Serialize, Deserialize)]
1325pub struct RegisterLightningAddressRequest {
1326 pub username: String,
1327 #[cfg_attr(feature = "uniffi", uniffi(default=None))]
1328 pub description: Option<String>,
1329}
1330
1331#[cfg_attr(feature = "uniffi", derive(uniffi::Record))]
1332#[derive(Debug, Clone, Deserialize, Serialize)]
1333pub struct LnurlInfo {
1334 pub url: String,
1335 pub bech32: String,
1336}
1337
1338impl LnurlInfo {
1339 pub fn new(url: String) -> Self {
1340 let bech32 =
1341 breez_sdk_common::lnurl::encode_lnurl_to_bech32(&url).unwrap_or_else(|_| url.clone());
1342 Self { url, bech32 }
1343 }
1344}
1345
1346#[cfg_attr(feature = "uniffi", derive(uniffi::Record))]
1347#[derive(Deserialize, Serialize)]
1348pub struct LightningAddressInfo {
1349 pub description: String,
1350 pub lightning_address: String,
1351 pub lnurl: LnurlInfo,
1352 pub username: String,
1353}
1354
1355impl From<RecoverLnurlPayResponse> for LightningAddressInfo {
1356 fn from(resp: RecoverLnurlPayResponse) -> Self {
1357 Self {
1358 description: resp.description,
1359 lightning_address: resp.lightning_address,
1360 lnurl: LnurlInfo::new(resp.lnurl),
1361 username: resp.username,
1362 }
1363 }
1364}
1365
1366#[cfg_attr(feature = "uniffi", derive(uniffi::Enum))]
1367#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
1368pub enum KeySetType {
1369 #[default]
1370 Default,
1371 Taproot,
1372 NativeSegwit,
1373 WrappedSegwit,
1374 Legacy,
1375}
1376
1377impl From<spark_wallet::KeySetType> for KeySetType {
1378 fn from(value: spark_wallet::KeySetType) -> Self {
1379 match value {
1380 spark_wallet::KeySetType::Default => KeySetType::Default,
1381 spark_wallet::KeySetType::Taproot => KeySetType::Taproot,
1382 spark_wallet::KeySetType::NativeSegwit => KeySetType::NativeSegwit,
1383 spark_wallet::KeySetType::WrappedSegwit => KeySetType::WrappedSegwit,
1384 spark_wallet::KeySetType::Legacy => KeySetType::Legacy,
1385 }
1386 }
1387}
1388
1389impl From<KeySetType> for spark_wallet::KeySetType {
1390 fn from(value: KeySetType) -> Self {
1391 match value {
1392 KeySetType::Default => spark_wallet::KeySetType::Default,
1393 KeySetType::Taproot => spark_wallet::KeySetType::Taproot,
1394 KeySetType::NativeSegwit => spark_wallet::KeySetType::NativeSegwit,
1395 KeySetType::WrappedSegwit => spark_wallet::KeySetType::WrappedSegwit,
1396 KeySetType::Legacy => spark_wallet::KeySetType::Legacy,
1397 }
1398 }
1399}
1400
1401#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
1405#[cfg_attr(feature = "uniffi", derive(uniffi::Record))]
1406pub struct KeySetConfig {
1407 pub key_set_type: KeySetType,
1409 pub use_address_index: bool,
1411 pub account_number: Option<u32>,
1413}
1414
1415impl Default for KeySetConfig {
1416 fn default() -> Self {
1417 Self {
1418 key_set_type: KeySetType::Default,
1419 use_address_index: false,
1420 account_number: None,
1421 }
1422 }
1423}
1424
1425#[derive(Debug, Clone, Serialize)]
1427#[cfg_attr(feature = "uniffi", derive(uniffi::Record))]
1428pub struct ListFiatCurrenciesResponse {
1429 pub currencies: Vec<FiatCurrency>,
1431}
1432
1433#[derive(Debug, Clone, Serialize)]
1435#[cfg_attr(feature = "uniffi", derive(uniffi::Record))]
1436pub struct ListFiatRatesResponse {
1437 pub rates: Vec<Rate>,
1439}
1440
1441#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)]
1443#[cfg_attr(feature = "uniffi", derive(uniffi::Enum))]
1444pub enum ServiceStatus {
1445 Operational,
1447 Degraded,
1449 Partial,
1451 Unknown,
1453 Major,
1455}
1456
1457#[derive(Debug, Clone, Serialize)]
1459#[cfg_attr(feature = "uniffi", derive(uniffi::Record))]
1460pub struct SparkStatus {
1461 pub status: ServiceStatus,
1463 pub last_updated: u64,
1465}
1466
1467pub(crate) enum WaitForPaymentIdentifier {
1468 PaymentId(String),
1469 PaymentRequest(String),
1470}
1471
1472#[cfg_attr(feature = "uniffi", derive(uniffi::Record))]
1473pub struct GetTokensMetadataRequest {
1474 pub token_identifiers: Vec<String>,
1475}
1476
1477#[derive(Debug, Clone, Serialize)]
1478#[cfg_attr(feature = "uniffi", derive(uniffi::Record))]
1479pub struct GetTokensMetadataResponse {
1480 pub tokens_metadata: Vec<TokenMetadata>,
1481}
1482
1483#[cfg_attr(feature = "uniffi", derive(uniffi::Record))]
1484pub struct SignMessageRequest {
1485 pub message: String,
1486 pub compact: bool,
1488}
1489
1490#[cfg_attr(feature = "uniffi", derive(uniffi::Record))]
1491pub struct SignMessageResponse {
1492 pub pubkey: String,
1493 pub signature: String,
1495}
1496
1497#[cfg_attr(feature = "uniffi", derive(uniffi::Record))]
1498pub struct CheckMessageRequest {
1499 pub message: String,
1501 pub pubkey: String,
1503 pub signature: String,
1505}
1506
1507#[cfg_attr(feature = "uniffi", derive(uniffi::Record))]
1508pub struct CheckMessageResponse {
1509 pub is_valid: bool,
1510}
1511
1512#[cfg_attr(feature = "uniffi", derive(uniffi::Record))]
1513#[derive(Debug, Clone, Serialize)]
1514pub struct UserSettings {
1515 pub spark_private_mode_enabled: bool,
1516}
1517
1518#[cfg_attr(feature = "uniffi", derive(uniffi::Record))]
1519pub struct UpdateUserSettingsRequest {
1520 pub spark_private_mode_enabled: Option<bool>,
1521}
1522
1523#[cfg_attr(feature = "uniffi", derive(uniffi::Record))]
1524pub struct ClaimHtlcPaymentRequest {
1525 pub preimage: String,
1526}
1527
1528#[cfg_attr(feature = "uniffi", derive(uniffi::Record))]
1529pub struct ClaimHtlcPaymentResponse {
1530 pub payment: Payment,
1531}
1532
1533#[derive(Debug, Clone, Deserialize, Serialize)]
1534#[cfg_attr(feature = "uniffi", derive(uniffi::Record))]
1535pub struct LnurlReceiveMetadata {
1536 pub nostr_zap_request: Option<String>,
1537 pub nostr_zap_receipt: Option<String>,
1538 pub sender_comment: Option<String>,
1539}
1540
1541#[cfg_attr(feature = "uniffi", derive(uniffi::Record))]
1542pub struct OptimizationProgress {
1543 pub is_running: bool,
1544 pub current_round: u32,
1545 pub total_rounds: u32,
1546}