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 remote,
319 try_lift: |val| val.parse::<u128>().map_err(uniffi::deps::anyhow::Error::msg),
320 lower: |obj| obj.to_string(),
321});
322
323#[allow(clippy::large_enum_variant)]
326#[derive(Debug, Clone, Serialize, Deserialize)]
327#[cfg_attr(feature = "uniffi", derive(uniffi::Enum))]
328pub enum PaymentDetails {
329 Spark {
330 invoice_details: Option<SparkInvoicePaymentDetails>,
332 htlc_details: Option<SparkHtlcDetails>,
334 conversion_info: Option<ConversionInfo>,
336 },
337 Token {
338 metadata: TokenMetadata,
339 tx_hash: String,
340 tx_type: TokenTransactionType,
341 invoice_details: Option<SparkInvoicePaymentDetails>,
343 conversion_info: Option<ConversionInfo>,
345 },
346 Lightning {
347 description: Option<String>,
349 invoice: String,
353
354 destination_pubkey: String,
356
357 htlc_details: SparkHtlcDetails,
359
360 lnurl_pay_info: Option<LnurlPayInfo>,
362
363 lnurl_withdraw_info: Option<LnurlWithdrawInfo>,
365
366 lnurl_receive_metadata: Option<LnurlReceiveMetadata>,
368 },
369 Withdraw {
370 tx_id: String,
371 },
372 Deposit {
373 tx_id: String,
374 },
375}
376
377#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
378#[cfg_attr(feature = "uniffi", derive(uniffi::Enum))]
379pub enum TokenTransactionType {
380 Transfer,
381 Mint,
382 Burn,
383}
384
385impl fmt::Display for TokenTransactionType {
386 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
387 match self {
388 TokenTransactionType::Transfer => write!(f, "transfer"),
389 TokenTransactionType::Mint => write!(f, "mint"),
390 TokenTransactionType::Burn => write!(f, "burn"),
391 }
392 }
393}
394
395impl FromStr for TokenTransactionType {
396 type Err = String;
397
398 fn from_str(s: &str) -> Result<Self, Self::Err> {
399 match s.to_lowercase().as_str() {
400 "transfer" => Ok(TokenTransactionType::Transfer),
401 "mint" => Ok(TokenTransactionType::Mint),
402 "burn" => Ok(TokenTransactionType::Burn),
403 _ => Err(format!("Invalid token transaction type '{s}'")),
404 }
405 }
406}
407
408#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
409#[cfg_attr(feature = "uniffi", derive(uniffi::Record))]
410pub struct SparkInvoicePaymentDetails {
411 pub description: Option<String>,
413 pub invoice: String,
415}
416
417#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
418#[cfg_attr(feature = "uniffi", derive(uniffi::Record))]
419pub struct SparkHtlcDetails {
420 pub payment_hash: String,
422 pub preimage: Option<String>,
424 pub expiry_time: u64,
426 pub status: SparkHtlcStatus,
428}
429
430#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq)]
431#[cfg_attr(feature = "uniffi", derive(uniffi::Enum))]
432pub enum SparkHtlcStatus {
433 WaitingForPreimage,
435 PreimageShared,
437 Returned,
439}
440
441impl fmt::Display for SparkHtlcStatus {
442 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
443 match self {
444 SparkHtlcStatus::WaitingForPreimage => write!(f, "WaitingForPreimage"),
445 SparkHtlcStatus::PreimageShared => write!(f, "PreimageShared"),
446 SparkHtlcStatus::Returned => write!(f, "Returned"),
447 }
448 }
449}
450
451impl FromStr for SparkHtlcStatus {
452 type Err = String;
453
454 fn from_str(s: &str) -> Result<Self, Self::Err> {
455 match s {
456 "WaitingForPreimage" => Ok(SparkHtlcStatus::WaitingForPreimage),
457 "PreimageShared" => Ok(SparkHtlcStatus::PreimageShared),
458 "Returned" => Ok(SparkHtlcStatus::Returned),
459 _ => Err("Invalid Spark HTLC status".to_string()),
460 }
461 }
462}
463
464#[derive(Debug, Clone, Copy)]
465#[cfg_attr(feature = "uniffi", derive(uniffi::Enum))]
466pub enum Network {
467 Mainnet,
468 Regtest,
469}
470
471impl std::fmt::Display for Network {
472 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
473 match self {
474 Network::Mainnet => write!(f, "Mainnet"),
475 Network::Regtest => write!(f, "Regtest"),
476 }
477 }
478}
479
480impl From<Network> for BitcoinNetwork {
481 fn from(network: Network) -> Self {
482 match network {
483 Network::Mainnet => BitcoinNetwork::Bitcoin,
484 Network::Regtest => BitcoinNetwork::Regtest,
485 }
486 }
487}
488
489impl From<Network> for breez_sdk_common::network::BitcoinNetwork {
490 fn from(network: Network) -> Self {
491 match network {
492 Network::Mainnet => breez_sdk_common::network::BitcoinNetwork::Bitcoin,
493 Network::Regtest => breez_sdk_common::network::BitcoinNetwork::Regtest,
494 }
495 }
496}
497
498impl From<Network> for bitcoin::Network {
499 fn from(network: Network) -> Self {
500 match network {
501 Network::Mainnet => bitcoin::Network::Bitcoin,
502 Network::Regtest => bitcoin::Network::Regtest,
503 }
504 }
505}
506
507impl FromStr for Network {
508 type Err = String;
509
510 fn from_str(s: &str) -> Result<Self, Self::Err> {
511 match s {
512 "mainnet" => Ok(Network::Mainnet),
513 "regtest" => Ok(Network::Regtest),
514 _ => Err("Invalid network".to_string()),
515 }
516 }
517}
518
519#[derive(Debug, Clone)]
520#[cfg_attr(feature = "uniffi", derive(uniffi::Record))]
521#[allow(clippy::struct_excessive_bools)]
522pub struct Config {
523 pub api_key: Option<String>,
524 pub network: Network,
525 pub sync_interval_secs: u32,
526
527 pub max_deposit_claim_fee: Option<MaxFee>,
530
531 pub lnurl_domain: Option<String>,
533
534 pub prefer_spark_over_lightning: bool,
538
539 pub external_input_parsers: Option<Vec<ExternalInputParser>>,
543 pub use_default_external_input_parsers: bool,
547
548 pub real_time_sync_server_url: Option<String>,
550
551 pub private_enabled_default: bool,
556
557 pub optimization_config: OptimizationConfig,
563
564 pub stable_balance_config: Option<StableBalanceConfig>,
569
570 pub max_concurrent_claims: u32,
574
575 pub support_lnurl_verify: bool,
578}
579
580#[derive(Debug, Clone)]
581#[cfg_attr(feature = "uniffi", derive(uniffi::Record))]
582pub struct OptimizationConfig {
583 pub auto_enabled: bool,
590 pub multiplicity: u8,
602}
603
604#[derive(Debug, Clone)]
616#[cfg_attr(feature = "uniffi", derive(uniffi::Record))]
617pub struct StableBalanceConfig {
618 pub token_identifier: String,
620
621 #[cfg_attr(feature = "uniffi", uniffi(default = None))]
626 pub threshold_sats: Option<u64>,
627
628 #[cfg_attr(feature = "uniffi", uniffi(default = None))]
632 pub max_slippage_bps: Option<u32>,
633
634 #[cfg_attr(feature = "uniffi", uniffi(default = None))]
639 pub reserved_sats: Option<u64>,
640}
641
642impl Config {
643 pub fn validate(&self) -> Result<(), SdkError> {
647 if self.max_concurrent_claims == 0 {
648 return Err(SdkError::InvalidInput(
649 "max_concurrent_claims must be greater than 0".to_string(),
650 ));
651 }
652 Ok(())
653 }
654
655 pub(crate) fn get_all_external_input_parsers(&self) -> Vec<ExternalInputParser> {
656 let mut external_input_parsers = Vec::new();
657 if self.use_default_external_input_parsers {
658 let default_parsers = DEFAULT_EXTERNAL_INPUT_PARSERS
659 .iter()
660 .map(|(id, regex, url)| ExternalInputParser {
661 provider_id: (*id).to_string(),
662 input_regex: (*regex).to_string(),
663 parser_url: (*url).to_string(),
664 })
665 .collect::<Vec<_>>();
666 external_input_parsers.extend(default_parsers);
667 }
668 external_input_parsers.extend(self.external_input_parsers.clone().unwrap_or_default());
669
670 external_input_parsers
671 }
672}
673
674#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
675#[cfg_attr(feature = "uniffi", derive(uniffi::Enum))]
676pub enum MaxFee {
677 Fixed { amount: u64 },
679 Rate { sat_per_vbyte: u64 },
681 NetworkRecommended { leeway_sat_per_vbyte: u64 },
683}
684
685impl MaxFee {
686 pub(crate) async fn to_fee(&self, client: &dyn BitcoinChainService) -> Result<Fee, SdkError> {
687 match self {
688 MaxFee::Fixed { amount } => Ok(Fee::Fixed { amount: *amount }),
689 MaxFee::Rate { sat_per_vbyte } => Ok(Fee::Rate {
690 sat_per_vbyte: *sat_per_vbyte,
691 }),
692 MaxFee::NetworkRecommended {
693 leeway_sat_per_vbyte,
694 } => {
695 let recommended_fees = client.recommended_fees().await?;
696 let max_fee_rate = recommended_fees
697 .fastest_fee
698 .saturating_add(*leeway_sat_per_vbyte);
699 Ok(Fee::Rate {
700 sat_per_vbyte: max_fee_rate,
701 })
702 }
703 }
704 }
705}
706
707#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
708#[cfg_attr(feature = "uniffi", derive(uniffi::Enum))]
709pub enum Fee {
710 Fixed { amount: u64 },
712 Rate { sat_per_vbyte: u64 },
714}
715
716impl Fee {
717 pub fn to_sats(&self, vbytes: u64) -> u64 {
718 match self {
719 Fee::Fixed { amount } => *amount,
720 Fee::Rate { sat_per_vbyte } => sat_per_vbyte.saturating_mul(vbytes),
721 }
722 }
723}
724
725#[derive(Debug, Clone, Serialize, Deserialize)]
726#[cfg_attr(feature = "uniffi", derive(uniffi::Record))]
727pub struct DepositInfo {
728 pub txid: String,
729 pub vout: u32,
730 pub amount_sats: u64,
731 pub refund_tx: Option<String>,
732 pub refund_tx_id: Option<String>,
733 pub claim_error: Option<DepositClaimError>,
734}
735
736#[cfg_attr(feature = "uniffi", derive(uniffi::Record))]
737pub struct ClaimDepositRequest {
738 pub txid: String,
739 pub vout: u32,
740 #[cfg_attr(feature = "uniffi", uniffi(default=None))]
741 pub max_fee: Option<MaxFee>,
742}
743
744#[derive(Debug, Clone, Serialize)]
745#[cfg_attr(feature = "uniffi", derive(uniffi::Record))]
746pub struct ClaimDepositResponse {
747 pub payment: Payment,
748}
749
750#[derive(Debug, Clone, Serialize)]
751#[cfg_attr(feature = "uniffi", derive(uniffi::Record))]
752pub struct RefundDepositRequest {
753 pub txid: String,
754 pub vout: u32,
755 pub destination_address: String,
756 pub fee: Fee,
757}
758
759#[derive(Debug, Clone, Serialize)]
760#[cfg_attr(feature = "uniffi", derive(uniffi::Record))]
761pub struct RefundDepositResponse {
762 pub tx_id: String,
763 pub tx_hex: String,
764}
765
766#[derive(Debug, Clone, Serialize)]
767#[cfg_attr(feature = "uniffi", derive(uniffi::Record))]
768pub struct ListUnclaimedDepositsRequest {}
769
770#[derive(Debug, Clone, Serialize)]
771#[cfg_attr(feature = "uniffi", derive(uniffi::Record))]
772pub struct ListUnclaimedDepositsResponse {
773 pub deposits: Vec<DepositInfo>,
774}
775
776#[derive(Debug, Clone, Default)]
778#[cfg_attr(feature = "uniffi", derive(uniffi::Record))]
779pub struct BuyBitcoinRequest {
780 #[cfg_attr(feature = "uniffi", uniffi(default=None))]
783 pub locked_amount_sat: Option<u64>,
784 #[cfg_attr(feature = "uniffi", uniffi(default=None))]
786 pub redirect_url: Option<String>,
787}
788
789#[derive(Debug, Clone, Serialize)]
791#[cfg_attr(feature = "uniffi", derive(uniffi::Record))]
792pub struct BuyBitcoinResponse {
793 pub url: String,
795}
796
797impl std::fmt::Display for MaxFee {
798 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
799 match self {
800 MaxFee::Fixed { amount } => write!(f, "Fixed: {amount}"),
801 MaxFee::Rate { sat_per_vbyte } => write!(f, "Rate: {sat_per_vbyte}"),
802 MaxFee::NetworkRecommended {
803 leeway_sat_per_vbyte,
804 } => write!(f, "NetworkRecommended: {leeway_sat_per_vbyte}"),
805 }
806 }
807}
808
809#[derive(Debug, Clone)]
810#[cfg_attr(feature = "uniffi", derive(uniffi::Record))]
811pub struct Credentials {
812 pub username: String,
813 pub password: String,
814}
815
816#[derive(Debug, Clone)]
818#[cfg_attr(feature = "uniffi", derive(uniffi::Record))]
819pub struct GetInfoRequest {
820 pub ensure_synced: Option<bool>,
821}
822
823#[derive(Debug, Clone, Serialize)]
825#[cfg_attr(feature = "uniffi", derive(uniffi::Record))]
826pub struct GetInfoResponse {
827 pub identity_pubkey: String,
829 pub balance_sats: u64,
831 pub token_balances: HashMap<String, TokenBalance>,
833}
834
835#[derive(Debug, Clone, Serialize, Deserialize)]
836#[cfg_attr(feature = "uniffi", derive(uniffi::Record))]
837pub struct TokenBalance {
838 pub balance: u128,
839 pub token_metadata: TokenMetadata,
840}
841
842#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
843#[cfg_attr(feature = "uniffi", derive(uniffi::Record))]
844pub struct TokenMetadata {
845 pub identifier: String,
846 pub issuer_public_key: String,
848 pub name: String,
849 pub ticker: String,
850 pub decimals: u32,
852 pub max_supply: u128,
853 pub is_freezable: bool,
854}
855
856#[derive(Debug, Clone)]
858#[cfg_attr(feature = "uniffi", derive(uniffi::Record))]
859pub struct SyncWalletRequest {}
860
861#[derive(Debug, Clone, Serialize)]
863#[cfg_attr(feature = "uniffi", derive(uniffi::Record))]
864pub struct SyncWalletResponse {}
865
866#[derive(Debug, Clone, Serialize)]
867#[cfg_attr(feature = "uniffi", derive(uniffi::Enum))]
868pub enum ReceivePaymentMethod {
869 SparkAddress,
870 SparkInvoice {
871 amount: Option<u128>,
873 token_identifier: Option<String>,
876 expiry_time: Option<u64>,
878 description: Option<String>,
880 sender_public_key: Option<String>,
882 },
883 BitcoinAddress,
884 Bolt11Invoice {
885 description: String,
886 amount_sats: Option<u64>,
887 expiry_secs: Option<u32>,
889 payment_hash: Option<String>,
893 },
894}
895
896#[derive(Debug, Clone, Serialize)]
897#[cfg_attr(feature = "uniffi", derive(uniffi::Enum))]
898pub enum SendPaymentMethod {
899 BitcoinAddress {
900 address: BitcoinAddressDetails,
901 fee_quote: SendOnchainFeeQuote,
902 },
903 Bolt11Invoice {
904 invoice_details: Bolt11InvoiceDetails,
905 spark_transfer_fee_sats: Option<u64>,
906 lightning_fee_sats: u64,
907 }, SparkAddress {
909 address: String,
910 fee: u128,
913 token_identifier: Option<String>,
916 },
917 SparkInvoice {
918 spark_invoice_details: SparkInvoiceDetails,
919 fee: u128,
922 token_identifier: Option<String>,
925 },
926}
927
928#[cfg_attr(feature = "uniffi", derive(uniffi::Record))]
929#[derive(Debug, Clone, Serialize)]
930pub struct SendOnchainFeeQuote {
931 pub id: String,
932 pub expires_at: u64,
933 pub speed_fast: SendOnchainSpeedFeeQuote,
934 pub speed_medium: SendOnchainSpeedFeeQuote,
935 pub speed_slow: SendOnchainSpeedFeeQuote,
936}
937
938#[cfg_attr(feature = "uniffi", derive(uniffi::Record))]
939#[derive(Debug, Clone, Serialize)]
940pub struct SendOnchainSpeedFeeQuote {
941 pub user_fee_sat: u64,
942 pub l1_broadcast_fee_sat: u64,
943}
944
945impl SendOnchainSpeedFeeQuote {
946 pub fn total_fee_sat(&self) -> u64 {
947 self.user_fee_sat.saturating_add(self.l1_broadcast_fee_sat)
948 }
949}
950
951#[cfg_attr(feature = "uniffi", derive(uniffi::Record))]
952pub struct ReceivePaymentRequest {
953 pub payment_method: ReceivePaymentMethod,
954}
955
956#[derive(Debug, Clone, Serialize)]
957#[cfg_attr(feature = "uniffi", derive(uniffi::Record))]
958pub struct ReceivePaymentResponse {
959 pub payment_request: String,
960 pub fee: u128,
963}
964
965#[cfg_attr(feature = "uniffi", derive(uniffi::Record))]
966pub struct PrepareLnurlPayRequest {
967 pub amount_sats: u64,
969 pub pay_request: LnurlPayRequestDetails,
970 #[cfg_attr(feature = "uniffi", uniffi(default=None))]
971 pub comment: Option<String>,
972 #[cfg_attr(feature = "uniffi", uniffi(default=None))]
973 pub validate_success_action_url: Option<bool>,
974 #[cfg_attr(feature = "uniffi", uniffi(default=None))]
976 pub conversion_options: Option<ConversionOptions>,
977 #[cfg_attr(feature = "uniffi", uniffi(default=None))]
979 pub fee_policy: Option<FeePolicy>,
980}
981
982#[derive(Debug, Clone)]
983#[cfg_attr(feature = "uniffi", derive(uniffi::Record))]
984pub struct PrepareLnurlPayResponse {
985 pub amount_sats: u64,
987 pub comment: Option<String>,
988 pub pay_request: LnurlPayRequestDetails,
989 pub fee_sats: u64,
992 pub invoice_details: Bolt11InvoiceDetails,
993 pub success_action: Option<SuccessAction>,
994 pub conversion_estimate: Option<ConversionEstimate>,
996 pub fee_policy: FeePolicy,
998}
999
1000#[cfg_attr(feature = "uniffi", derive(uniffi::Record))]
1001pub struct LnurlPayRequest {
1002 pub prepare_response: PrepareLnurlPayResponse,
1003 #[cfg_attr(feature = "uniffi", uniffi(default=None))]
1007 pub idempotency_key: Option<String>,
1008}
1009
1010#[derive(Debug, Serialize)]
1011#[cfg_attr(feature = "uniffi", derive(uniffi::Record))]
1012pub struct LnurlPayResponse {
1013 pub payment: Payment,
1014 pub success_action: Option<SuccessActionProcessed>,
1015}
1016
1017#[cfg_attr(feature = "uniffi", derive(uniffi::Record))]
1018pub struct LnurlWithdrawRequest {
1019 pub amount_sats: u64,
1022 pub withdraw_request: LnurlWithdrawRequestDetails,
1023 #[cfg_attr(feature = "uniffi", uniffi(default=None))]
1027 pub completion_timeout_secs: Option<u32>,
1028}
1029
1030#[derive(Debug, Serialize)]
1031#[cfg_attr(feature = "uniffi", derive(uniffi::Record))]
1032pub struct LnurlWithdrawResponse {
1033 pub payment_request: String,
1035 pub payment: Option<Payment>,
1036}
1037
1038#[derive(Clone, Debug, Default, Deserialize, PartialEq, Serialize)]
1040#[cfg_attr(feature = "uniffi", derive(uniffi::Record))]
1041pub struct LnurlPayInfo {
1042 pub ln_address: Option<String>,
1043 pub comment: Option<String>,
1044 pub domain: Option<String>,
1045 pub metadata: Option<String>,
1046 pub processed_success_action: Option<SuccessActionProcessed>,
1047 pub raw_success_action: Option<SuccessAction>,
1048}
1049
1050#[derive(Clone, Debug, Deserialize, Serialize)]
1052#[cfg_attr(feature = "uniffi", derive(uniffi::Record))]
1053pub struct LnurlWithdrawInfo {
1054 pub withdraw_url: String,
1055}
1056
1057impl LnurlPayInfo {
1058 pub fn extract_description(&self) -> Option<String> {
1059 let Some(metadata) = &self.metadata else {
1060 return None;
1061 };
1062
1063 let Ok(metadata) = serde_json::from_str::<Vec<Vec<Value>>>(metadata) else {
1064 return None;
1065 };
1066
1067 for arr in metadata {
1068 if arr.len() != 2 {
1069 continue;
1070 }
1071 if let (Some(key), Some(value)) = (arr[0].as_str(), arr[1].as_str())
1072 && key == "text/plain"
1073 {
1074 return Some(value.to_string());
1075 }
1076 }
1077
1078 None
1079 }
1080}
1081
1082#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, Serialize, Deserialize)]
1084#[cfg_attr(feature = "uniffi", derive(uniffi::Enum))]
1085pub enum FeePolicy {
1086 #[default]
1089 FeesExcluded,
1090 FeesIncluded,
1093}
1094
1095#[cfg_attr(feature = "uniffi", derive(uniffi::Enum))]
1096#[derive(Debug, Clone, Serialize)]
1097pub enum OnchainConfirmationSpeed {
1098 Fast,
1099 Medium,
1100 Slow,
1101}
1102
1103#[cfg_attr(feature = "uniffi", derive(uniffi::Record))]
1104pub struct PrepareSendPaymentRequest {
1105 pub payment_request: String,
1106 #[cfg_attr(feature = "uniffi", uniffi(default=None))]
1111 pub amount: Option<u128>,
1112 #[cfg_attr(feature = "uniffi", uniffi(default=None))]
1115 pub token_identifier: Option<String>,
1116 #[cfg_attr(feature = "uniffi", uniffi(default=None))]
1118 pub conversion_options: Option<ConversionOptions>,
1119 #[cfg_attr(feature = "uniffi", uniffi(default=None))]
1121 pub fee_policy: Option<FeePolicy>,
1122}
1123
1124#[derive(Debug, Clone, Serialize)]
1125#[cfg_attr(feature = "uniffi", derive(uniffi::Record))]
1126pub struct PrepareSendPaymentResponse {
1127 pub payment_method: SendPaymentMethod,
1128 pub amount: u128,
1131 pub token_identifier: Option<String>,
1134 pub conversion_estimate: Option<ConversionEstimate>,
1136 pub fee_policy: FeePolicy,
1138}
1139
1140#[cfg_attr(feature = "uniffi", derive(uniffi::Enum))]
1141pub enum SendPaymentOptions {
1142 BitcoinAddress {
1143 confirmation_speed: OnchainConfirmationSpeed,
1145 },
1146 Bolt11Invoice {
1147 prefer_spark: bool,
1148
1149 completion_timeout_secs: Option<u32>,
1152 },
1153 SparkAddress {
1154 htlc_options: Option<SparkHtlcOptions>,
1157 },
1158}
1159
1160#[cfg_attr(feature = "uniffi", derive(uniffi::Record))]
1161pub struct SparkHtlcOptions {
1162 pub payment_hash: String,
1164 pub expiry_duration_secs: u64,
1167}
1168
1169#[cfg_attr(feature = "uniffi", derive(uniffi::Record))]
1170pub struct SendPaymentRequest {
1171 pub prepare_response: PrepareSendPaymentResponse,
1172 #[cfg_attr(feature = "uniffi", uniffi(default=None))]
1173 pub options: Option<SendPaymentOptions>,
1174 #[cfg_attr(feature = "uniffi", uniffi(default=None))]
1179 pub idempotency_key: Option<String>,
1180}
1181
1182#[derive(Debug, Clone, Serialize)]
1183#[cfg_attr(feature = "uniffi", derive(uniffi::Record))]
1184pub struct SendPaymentResponse {
1185 pub payment: Payment,
1186}
1187
1188#[derive(Debug, Clone)]
1189#[cfg_attr(feature = "uniffi", derive(uniffi::Enum))]
1190pub enum PaymentDetailsFilter {
1191 Spark {
1192 htlc_status: Option<Vec<SparkHtlcStatus>>,
1194 conversion_refund_needed: Option<bool>,
1196 },
1197 Token {
1198 conversion_refund_needed: Option<bool>,
1200 tx_hash: Option<String>,
1202 tx_type: Option<TokenTransactionType>,
1204 },
1205 Lightning {
1206 htlc_status: Option<Vec<SparkHtlcStatus>>,
1208 },
1209}
1210
1211#[derive(Debug, Clone, Default)]
1213#[cfg_attr(feature = "uniffi", derive(uniffi::Record))]
1214pub struct ListPaymentsRequest {
1215 #[cfg_attr(feature = "uniffi", uniffi(default=None))]
1216 pub type_filter: Option<Vec<PaymentType>>,
1217 #[cfg_attr(feature = "uniffi", uniffi(default=None))]
1218 pub status_filter: Option<Vec<PaymentStatus>>,
1219 #[cfg_attr(feature = "uniffi", uniffi(default=None))]
1220 pub asset_filter: Option<AssetFilter>,
1221 #[cfg_attr(feature = "uniffi", uniffi(default=None))]
1223 pub payment_details_filter: Option<Vec<PaymentDetailsFilter>>,
1224 #[cfg_attr(feature = "uniffi", uniffi(default=None))]
1226 pub from_timestamp: Option<u64>,
1227 #[cfg_attr(feature = "uniffi", uniffi(default=None))]
1229 pub to_timestamp: Option<u64>,
1230 #[cfg_attr(feature = "uniffi", uniffi(default=None))]
1232 pub offset: Option<u32>,
1233 #[cfg_attr(feature = "uniffi", uniffi(default=None))]
1235 pub limit: Option<u32>,
1236 #[cfg_attr(feature = "uniffi", uniffi(default=None))]
1237 pub sort_ascending: Option<bool>,
1238}
1239
1240#[derive(Debug, Clone)]
1242#[cfg_attr(feature = "uniffi", derive(uniffi::Enum))]
1243pub enum AssetFilter {
1244 Bitcoin,
1245 Token {
1246 token_identifier: Option<String>,
1248 },
1249}
1250
1251impl FromStr for AssetFilter {
1252 type Err = String;
1253
1254 fn from_str(s: &str) -> Result<Self, Self::Err> {
1255 Ok(match s.to_lowercase().as_str() {
1256 "bitcoin" => AssetFilter::Bitcoin,
1257 "token" => AssetFilter::Token {
1258 token_identifier: None,
1259 },
1260 str if str.starts_with("token:") => AssetFilter::Token {
1261 token_identifier: Some(
1262 str.split_once(':')
1263 .ok_or(format!("Invalid asset filter '{s}'"))?
1264 .1
1265 .to_string(),
1266 ),
1267 },
1268 _ => return Err(format!("Invalid asset filter '{s}'")),
1269 })
1270 }
1271}
1272
1273#[derive(Debug, Clone, Serialize)]
1275#[cfg_attr(feature = "uniffi", derive(uniffi::Record))]
1276pub struct ListPaymentsResponse {
1277 pub payments: Vec<Payment>,
1279}
1280
1281#[cfg_attr(feature = "uniffi", derive(uniffi::Record))]
1282pub struct GetPaymentRequest {
1283 pub payment_id: String,
1284}
1285
1286#[derive(Debug, Clone, Serialize)]
1287#[cfg_attr(feature = "uniffi", derive(uniffi::Record))]
1288pub struct GetPaymentResponse {
1289 pub payment: Payment,
1290}
1291
1292#[cfg_attr(feature = "uniffi", uniffi::export(callback_interface))]
1293pub trait Logger: Send + Sync {
1294 fn log(&self, l: LogEntry);
1295}
1296
1297#[derive(Debug, Clone, Serialize)]
1298#[cfg_attr(feature = "uniffi", derive(uniffi::Record))]
1299pub struct LogEntry {
1300 pub line: String,
1301 pub level: String,
1302}
1303
1304#[cfg_attr(feature = "uniffi", derive(uniffi::Record))]
1305#[derive(Debug, Clone, Serialize, Deserialize)]
1306pub struct CheckLightningAddressRequest {
1307 pub username: String,
1308}
1309
1310#[cfg_attr(feature = "uniffi", derive(uniffi::Record))]
1311#[derive(Debug, Clone, Serialize, Deserialize)]
1312pub struct RegisterLightningAddressRequest {
1313 pub username: String,
1314 #[cfg_attr(feature = "uniffi", uniffi(default=None))]
1315 pub description: Option<String>,
1316}
1317
1318#[cfg_attr(feature = "uniffi", derive(uniffi::Record))]
1319#[derive(Debug, Clone, PartialEq, Deserialize, Serialize)]
1320pub struct LnurlInfo {
1321 pub url: String,
1322 pub bech32: String,
1323}
1324
1325impl LnurlInfo {
1326 pub fn new(url: String) -> Self {
1327 let bech32 =
1328 breez_sdk_common::lnurl::encode_lnurl_to_bech32(&url).unwrap_or_else(|_| url.clone());
1329 Self { url, bech32 }
1330 }
1331}
1332
1333#[cfg_attr(feature = "uniffi", derive(uniffi::Record))]
1334#[derive(Debug, Clone, PartialEq, Deserialize, Serialize)]
1335pub struct LightningAddressInfo {
1336 pub description: String,
1337 pub lightning_address: String,
1338 pub lnurl: LnurlInfo,
1339 pub username: String,
1340}
1341
1342impl From<RecoverLnurlPayResponse> for LightningAddressInfo {
1343 fn from(resp: RecoverLnurlPayResponse) -> Self {
1344 Self {
1345 description: resp.description,
1346 lightning_address: resp.lightning_address,
1347 lnurl: LnurlInfo::new(resp.lnurl),
1348 username: resp.username,
1349 }
1350 }
1351}
1352
1353#[cfg_attr(feature = "uniffi", derive(uniffi::Enum))]
1354#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
1355pub enum KeySetType {
1356 #[default]
1357 Default,
1358 Taproot,
1359 NativeSegwit,
1360 WrappedSegwit,
1361 Legacy,
1362}
1363
1364impl From<spark_wallet::KeySetType> for KeySetType {
1365 fn from(value: spark_wallet::KeySetType) -> Self {
1366 match value {
1367 spark_wallet::KeySetType::Default => KeySetType::Default,
1368 spark_wallet::KeySetType::Taproot => KeySetType::Taproot,
1369 spark_wallet::KeySetType::NativeSegwit => KeySetType::NativeSegwit,
1370 spark_wallet::KeySetType::WrappedSegwit => KeySetType::WrappedSegwit,
1371 spark_wallet::KeySetType::Legacy => KeySetType::Legacy,
1372 }
1373 }
1374}
1375
1376impl From<KeySetType> for spark_wallet::KeySetType {
1377 fn from(value: KeySetType) -> Self {
1378 match value {
1379 KeySetType::Default => spark_wallet::KeySetType::Default,
1380 KeySetType::Taproot => spark_wallet::KeySetType::Taproot,
1381 KeySetType::NativeSegwit => spark_wallet::KeySetType::NativeSegwit,
1382 KeySetType::WrappedSegwit => spark_wallet::KeySetType::WrappedSegwit,
1383 KeySetType::Legacy => spark_wallet::KeySetType::Legacy,
1384 }
1385 }
1386}
1387
1388#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
1392#[cfg_attr(feature = "uniffi", derive(uniffi::Record))]
1393pub struct KeySetConfig {
1394 pub key_set_type: KeySetType,
1396 pub use_address_index: bool,
1398 pub account_number: Option<u32>,
1400}
1401
1402impl Default for KeySetConfig {
1403 fn default() -> Self {
1404 Self {
1405 key_set_type: KeySetType::Default,
1406 use_address_index: false,
1407 account_number: None,
1408 }
1409 }
1410}
1411
1412#[derive(Debug, Clone, Serialize)]
1414#[cfg_attr(feature = "uniffi", derive(uniffi::Record))]
1415pub struct ListFiatCurrenciesResponse {
1416 pub currencies: Vec<FiatCurrency>,
1418}
1419
1420#[derive(Debug, Clone, Serialize)]
1422#[cfg_attr(feature = "uniffi", derive(uniffi::Record))]
1423pub struct ListFiatRatesResponse {
1424 pub rates: Vec<Rate>,
1426}
1427
1428#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)]
1430#[cfg_attr(feature = "uniffi", derive(uniffi::Enum))]
1431pub enum ServiceStatus {
1432 Operational,
1434 Degraded,
1436 Partial,
1438 Unknown,
1440 Major,
1442}
1443
1444#[derive(Debug, Clone, Serialize)]
1446#[cfg_attr(feature = "uniffi", derive(uniffi::Record))]
1447pub struct SparkStatus {
1448 pub status: ServiceStatus,
1450 pub last_updated: u64,
1452}
1453
1454pub(crate) enum WaitForPaymentIdentifier {
1455 PaymentId(String),
1456 PaymentRequest(String),
1457}
1458
1459#[cfg_attr(feature = "uniffi", derive(uniffi::Record))]
1460pub struct GetTokensMetadataRequest {
1461 pub token_identifiers: Vec<String>,
1462}
1463
1464#[derive(Debug, Clone, Serialize)]
1465#[cfg_attr(feature = "uniffi", derive(uniffi::Record))]
1466pub struct GetTokensMetadataResponse {
1467 pub tokens_metadata: Vec<TokenMetadata>,
1468}
1469
1470#[cfg_attr(feature = "uniffi", derive(uniffi::Record))]
1471pub struct SignMessageRequest {
1472 pub message: String,
1473 pub compact: bool,
1475}
1476
1477#[cfg_attr(feature = "uniffi", derive(uniffi::Record))]
1478pub struct SignMessageResponse {
1479 pub pubkey: String,
1480 pub signature: String,
1482}
1483
1484#[cfg_attr(feature = "uniffi", derive(uniffi::Record))]
1485pub struct CheckMessageRequest {
1486 pub message: String,
1488 pub pubkey: String,
1490 pub signature: String,
1492}
1493
1494#[cfg_attr(feature = "uniffi", derive(uniffi::Record))]
1495pub struct CheckMessageResponse {
1496 pub is_valid: bool,
1497}
1498
1499#[cfg_attr(feature = "uniffi", derive(uniffi::Record))]
1500#[derive(Debug, Clone, Serialize)]
1501pub struct UserSettings {
1502 pub spark_private_mode_enabled: bool,
1503}
1504
1505#[cfg_attr(feature = "uniffi", derive(uniffi::Record))]
1506pub struct UpdateUserSettingsRequest {
1507 pub spark_private_mode_enabled: Option<bool>,
1508}
1509
1510#[cfg_attr(feature = "uniffi", derive(uniffi::Record))]
1511pub struct ClaimHtlcPaymentRequest {
1512 pub preimage: String,
1513}
1514
1515#[cfg_attr(feature = "uniffi", derive(uniffi::Record))]
1516pub struct ClaimHtlcPaymentResponse {
1517 pub payment: Payment,
1518}
1519
1520#[derive(Debug, Clone, Deserialize, Serialize)]
1521#[cfg_attr(feature = "uniffi", derive(uniffi::Record))]
1522pub struct LnurlReceiveMetadata {
1523 pub nostr_zap_request: Option<String>,
1524 pub nostr_zap_receipt: Option<String>,
1525 pub sender_comment: Option<String>,
1526}
1527
1528#[cfg_attr(feature = "uniffi", derive(uniffi::Record))]
1529pub struct OptimizationProgress {
1530 pub is_running: bool,
1531 pub current_round: u32,
1532 pub total_rounds: u32,
1533}
1534
1535#[derive(Debug, Clone, Serialize, Deserialize)]
1537#[cfg_attr(feature = "uniffi", derive(uniffi::Record))]
1538pub struct Contact {
1539 pub id: String,
1540 pub name: String,
1541 pub payment_identifier: String,
1543 pub created_at: u64,
1544 pub updated_at: u64,
1545}
1546
1547#[cfg_attr(feature = "uniffi", derive(uniffi::Record))]
1549pub struct AddContactRequest {
1550 pub name: String,
1551 pub payment_identifier: String,
1553}
1554
1555#[cfg_attr(feature = "uniffi", derive(uniffi::Record))]
1557pub struct UpdateContactRequest {
1558 pub id: String,
1559 pub name: String,
1560 pub payment_identifier: String,
1562}
1563
1564#[derive(Debug, Clone, Default)]
1566#[cfg_attr(feature = "uniffi", derive(uniffi::Record))]
1567pub struct ListContactsRequest {
1568 #[cfg_attr(feature = "uniffi", uniffi(default=None))]
1569 pub offset: Option<u32>,
1570 #[cfg_attr(feature = "uniffi", uniffi(default=None))]
1571 pub limit: Option<u32>,
1572}