breez_sdk_core/
binding.rs

1//! Bindings for the Dart integration
2//!
3//! ### Error handling
4//!
5//! Since the integration requires the methods to return `anyhow::Result`, but the SDK service methods
6//! are being converted to return `SdkResult`, we have two ways to handle errors:
7//! - by using `Into::into`, which converts the `SdkError` enum to a generic `anyhow::Error`
8//! - by wrapping the `SdkError` in an `anyhow::Error`
9//!
10//! The first option loses the `SdkError` type. The second option keeps the type, which we can retrieve
11//! with `anyhow::Error::downcast_ref` (or equivalent Dart method). We therefore use the second approach.
12
13use std::future::Future;
14use std::sync::Arc;
15
16use anyhow::{anyhow, Result};
17use flutter_rust_bridge::StreamSink;
18use log::{Level, LevelFilter, Metadata, Record};
19use once_cell::sync::{Lazy, OnceCell};
20use sdk_common::invoice;
21pub use sdk_common::prelude::{
22    parse, AesSuccessActionDataDecrypted, AesSuccessActionDataResult, BitcoinAddressData,
23    CurrencyInfo, FiatCurrency, InputType, LNInvoice, LnUrlAuthRequestData, LnUrlCallbackStatus,
24    LnUrlError, LnUrlErrorData, LnUrlPayErrorData, LnUrlPayRequest, LnUrlPayRequestData,
25    LnUrlWithdrawRequest, LnUrlWithdrawRequestData, LnUrlWithdrawResult, LnUrlWithdrawSuccessData,
26    LocaleOverrides, LocalizedName, MessageSuccessActionData, Network, Rate, RouteHint,
27    RouteHintHop, SuccessActionProcessed, Symbol, UrlSuccessActionData,
28};
29use tokio::sync::Mutex;
30
31use crate::breez_services::{self, BreezEvent, BreezServices, EventListener};
32use crate::chain::RecommendedFees;
33use crate::error::{
34    ConnectError, ReceiveOnchainError, ReceivePaymentError, RedeemOnchainError, SdkError,
35    SendOnchainError, SendPaymentError,
36};
37use crate::lsp::LspInformation;
38use crate::models::{Config, LogEntry, NodeState, Payment, SwapInfo};
39use crate::{
40    BackupStatus, BuyBitcoinRequest, BuyBitcoinResponse, CheckMessageRequest, CheckMessageResponse,
41    ConfigureNodeRequest, ConnectRequest, EnvironmentType, ListPaymentsRequest, ListSwapsRequest,
42    LnUrlAuthError, NodeConfig, NodeCredentials, OnchainPaymentLimitsResponse,
43    OpenChannelFeeRequest, OpenChannelFeeResponse, PayOnchainRequest, PayOnchainResponse,
44    PrepareOnchainPaymentRequest, PrepareOnchainPaymentResponse, PrepareRedeemOnchainFundsRequest,
45    PrepareRedeemOnchainFundsResponse, PrepareRefundRequest, PrepareRefundResponse,
46    ReceiveOnchainRequest, ReceivePaymentRequest, ReceivePaymentResponse,
47    RedeemOnchainFundsRequest, RedeemOnchainFundsResponse, RefundRequest, RefundResponse,
48    ReportIssueRequest, ReverseSwapFeesRequest, ReverseSwapInfo, ReverseSwapPairInfo,
49    SendPaymentRequest, SendPaymentResponse, SendSpontaneousPaymentRequest,
50    ServiceHealthCheckResponse, SignMessageRequest, SignMessageResponse, StaticBackupRequest,
51    StaticBackupResponse,
52};
53
54// === FRB mirroring
55//
56// This section contains frb "mirroring" structs and enums.
57// These are needed by the flutter bridge in order to use structs defined in an external crate.
58// See <https://cjycode.com/flutter_rust_bridge/v1/feature/lang_external.html#types-in-other-crates>
59// Note: in addition to the docs above, the mirrored structs must derive the Clone trait
60
61use flutter_rust_bridge::frb;
62
63#[frb(mirror(LnUrlAuthRequestData))]
64pub struct _LnUrlAuthRequestData {
65    pub k1: String,
66    pub action: Option<String>,
67    pub domain: String,
68    pub url: String,
69}
70
71#[frb(mirror(LnUrlErrorData))]
72pub struct _LnUrlErrorData {
73    pub reason: String,
74}
75
76#[frb(mirror(LnUrlCallbackStatus))]
77pub enum _LnUrlCallbackStatus {
78    Ok,
79    ErrorStatus { data: LnUrlErrorData },
80}
81
82#[frb(mirror(Network))]
83pub enum _Network {
84    Bitcoin,
85    Testnet,
86    Signet,
87    Regtest,
88}
89
90#[frb(mirror(LNInvoice))]
91pub struct _LNInvoice {
92    pub bolt11: String,
93    pub network: Network,
94    pub payee_pubkey: String,
95    pub payment_hash: String,
96    pub description: Option<String>,
97    pub description_hash: Option<String>,
98    pub amount_msat: Option<u64>,
99    pub timestamp: u64,
100    pub expiry: u64,
101    pub routing_hints: Vec<RouteHint>,
102    pub payment_secret: Vec<u8>,
103    pub min_final_cltv_expiry_delta: u64,
104}
105
106#[frb(mirror(RouteHint))]
107pub struct _RouteHint {
108    pub hops: Vec<RouteHintHop>,
109}
110
111#[frb(mirror(RouteHintHop))]
112pub struct _RouteHintHop {
113    pub src_node_id: String,
114    pub short_channel_id: String,
115    pub fees_base_msat: u32,
116    pub fees_proportional_millionths: u32,
117    pub cltv_expiry_delta: u64,
118    pub htlc_minimum_msat: Option<u64>,
119    pub htlc_maximum_msat: Option<u64>,
120}
121
122#[frb(mirror(LnUrlPayRequest))]
123pub struct _LnUrlPayRequest {
124    pub data: LnUrlPayRequestData,
125    pub amount_msat: u64,
126    pub use_trampoline: bool,
127    pub comment: Option<String>,
128    pub payment_label: Option<String>,
129    pub validate_success_action_url: Option<bool>,
130}
131
132#[frb(mirror(LnUrlPayRequestData))]
133pub struct _LnUrlPayRequestData {
134    pub callback: String,
135    pub min_sendable: u64,
136    pub max_sendable: u64,
137    pub metadata_str: String,
138    pub comment_allowed: u16,
139    pub domain: String,
140    pub allows_nostr: bool,
141    pub nostr_pubkey: Option<String>,
142    pub ln_address: Option<String>,
143}
144
145#[frb(mirror(LnUrlWithdrawRequest))]
146pub struct _LnUrlWithdrawRequest {
147    pub data: LnUrlWithdrawRequestData,
148    pub amount_msat: u64,
149    pub description: Option<String>,
150}
151
152#[frb(mirror(LnUrlWithdrawRequestData))]
153pub struct _LnUrlWithdrawRequestData {
154    pub callback: String,
155    pub k1: String,
156    pub default_description: String,
157    pub min_withdrawable: u64,
158    pub max_withdrawable: u64,
159}
160
161#[frb(mirror(InputType))]
162pub enum _InputType {
163    BitcoinAddress {
164        address: BitcoinAddressData,
165    },
166    Bolt11 {
167        invoice: LNInvoice,
168    },
169    NodeId {
170        node_id: String,
171    },
172    Url {
173        url: String,
174    },
175    LnUrlPay {
176        data: LnUrlPayRequestData,
177        bip353_address: Option<String>,
178    },
179    LnUrlWithdraw {
180        data: LnUrlWithdrawRequestData,
181    },
182    LnUrlAuth {
183        data: LnUrlAuthRequestData,
184    },
185    LnUrlError {
186        data: LnUrlErrorData,
187    },
188}
189
190#[frb(mirror(BitcoinAddressData))]
191pub struct _BitcoinAddressData {
192    pub address: String,
193    pub network: Network,
194    pub amount_sat: Option<u64>,
195    pub label: Option<String>,
196    pub message: Option<String>,
197}
198
199#[frb(mirror(SuccessActionProcessed))]
200pub enum _SuccessActionProcessed {
201    Aes { result: AesSuccessActionDataResult },
202    Message { data: MessageSuccessActionData },
203    Url { data: UrlSuccessActionData },
204}
205
206#[frb(mirror(AesSuccessActionDataResult))]
207pub enum _AesSuccessActionDataResult {
208    Decrypted { data: AesSuccessActionDataDecrypted },
209    ErrorStatus { reason: String },
210}
211
212#[frb(mirror(AesSuccessActionDataDecrypted))]
213pub struct _AesSuccessActionDataDecrypted {
214    pub description: String,
215    pub plaintext: String,
216}
217
218#[frb(mirror(MessageSuccessActionData))]
219pub struct _MessageSuccessActionData {
220    pub message: String,
221}
222
223#[frb(mirror(UrlSuccessActionData))]
224pub struct _UrlSuccessActionData {
225    pub description: String,
226    pub url: String,
227}
228
229#[frb(mirror(LnUrlPayErrorData))]
230pub struct _LnUrlPayErrorData {
231    pub payment_hash: String,
232    pub reason: String,
233}
234
235#[frb(mirror(LnUrlPayError))]
236pub enum _LnUrlPayError {
237    AlreadyPaid,
238    Generic { err: String },
239    InvalidAmount { err: String },
240    InvalidInvoice { err: String },
241    InvalidNetwork { err: String },
242    InvalidUri { err: String },
243    InvoiceExpired { err: String },
244    PaymentFailed { err: String },
245    PaymentTimeout { err: String },
246    RouteNotFound { err: String },
247    RouteTooExpensive { err: String },
248    ServiceConnectivity { err: String },
249}
250
251#[frb(mirror(LnUrlWithdrawResult))]
252pub enum _LnUrlWithdrawResult {
253    Ok { data: LnUrlWithdrawSuccessData },
254    Timeout { data: LnUrlWithdrawSuccessData },
255    ErrorStatus { data: LnUrlErrorData },
256}
257
258#[frb(mirror(LnUrlWithdrawSuccessData))]
259pub struct _LnUrlWithdrawSuccessData {
260    pub invoice: LNInvoice,
261}
262
263#[frb(mirror(Rate))]
264pub struct _Rate {
265    pub coin: String,
266    pub value: f64,
267}
268
269#[frb(mirror(FiatCurrency))]
270pub struct _FiatCurrency {
271    pub id: String,
272    pub info: CurrencyInfo,
273}
274
275#[frb(mirror(CurrencyInfo))]
276pub struct _CurrencyInfo {
277    pub name: String,
278    pub fraction_size: u32,
279    pub spacing: Option<u32>,
280    pub symbol: Option<Symbol>,
281    pub uniq_symbol: Option<Symbol>,
282    pub localized_name: Vec<LocalizedName>,
283    pub locale_overrides: Vec<LocaleOverrides>,
284}
285
286#[frb(mirror(LocaleOverrides))]
287pub struct _LocaleOverrides {
288    pub locale: String,
289    pub spacing: Option<u32>,
290    pub symbol: Symbol,
291}
292
293#[frb(mirror(LocalizedName))]
294pub struct _LocalizedName {
295    pub locale: String,
296    pub name: String,
297}
298
299#[frb(mirror(Symbol))]
300pub struct _Symbol {
301    pub grapheme: Option<String>,
302    pub template: Option<String>,
303    pub rtl: Option<bool>,
304    pub position: Option<u32>,
305}
306
307/*
308The format Lazy<Mutex<Option<...>>> for the following variables allows them to be instance-global,
309meaning they can be set only once per instance, but calling disconnect() will unset them.
310 */
311static BREEZ_SERVICES_INSTANCE: Lazy<Mutex<Option<Arc<BreezServices>>>> =
312    Lazy::new(|| Mutex::new(None));
313static NOTIFICATION_STREAM: OnceCell<StreamSink<BreezEvent>> = OnceCell::new();
314static RT: Lazy<tokio::runtime::Runtime> = Lazy::new(|| tokio::runtime::Runtime::new().unwrap());
315static LOG_INIT: OnceCell<bool> = OnceCell::new();
316
317/*  Breez Services API's */
318
319/// Wrapper around [BreezServices::connect] which also initializes SDK logging
320pub fn connect(req: ConnectRequest) -> Result<()> {
321    block_on(async move {
322        let mut locked = BREEZ_SERVICES_INSTANCE.lock().await;
323        match *locked {
324            None => {
325                let breez_services =
326                    BreezServices::connect(req, Box::new(BindingEventListener {})).await?;
327
328                *locked = Some(breez_services);
329                Ok(())
330            }
331            Some(_) => Err(ConnectError::Generic {
332                err: "Static node services already set, please call disconnect() first".into(),
333            }),
334        }
335    })
336    .map_err(anyhow::Error::new::<ConnectError>)
337}
338
339/// Check whether node service is initialized or not
340pub fn is_initialized() -> bool {
341    block_on(async { get_breez_services().await.is_ok() })
342}
343
344/// See [BreezServices::sync]
345pub fn sync() -> Result<()> {
346    block_on(async { get_breez_services().await?.sync().await })
347        .map_err(anyhow::Error::new::<SdkError>)
348}
349
350/// See [BreezServices::node_credentials]
351pub fn node_credentials() -> Result<Option<NodeCredentials>> {
352    block_on(async { get_breez_services().await?.node_credentials().await })
353        .map_err(anyhow::Error::new::<SdkError>)
354}
355
356/// See [BreezServices::node_info]
357pub fn node_info() -> Result<NodeState> {
358    block_on(async {
359        get_breez_services()
360            .await?
361            .node_info()
362            .map_err(anyhow::Error::new::<SdkError>)
363    })
364}
365
366/// See [BreezServices::configure_node]
367pub fn configure_node(req: ConfigureNodeRequest) -> Result<()> {
368    block_on(async { get_breez_services().await?.configure_node(req).await })
369        .map_err(anyhow::Error::new::<SdkError>)
370}
371
372/// Cleanup node resources and stop the signer.
373pub fn disconnect() -> Result<()> {
374    block_on(async {
375        // To avoid deadlock: first disconnect SDK, then acquire lock and unset global instance
376        get_breez_services().await?.disconnect().await?;
377        let mut locked_sdk_instance = BREEZ_SERVICES_INSTANCE.lock().await;
378        *locked_sdk_instance = None;
379
380        Ok(())
381    })
382    .map_err(anyhow::Error::new::<SdkError>)
383}
384
385/// See [BreezServices::sign_message]
386pub fn sign_message(req: SignMessageRequest) -> Result<SignMessageResponse> {
387    block_on(async { get_breez_services().await?.sign_message(req).await })
388        .map_err(anyhow::Error::new::<SdkError>)
389}
390
391/// See [BreezServices::check_message]
392pub fn check_message(req: CheckMessageRequest) -> Result<CheckMessageResponse> {
393    block_on(async { get_breez_services().await?.check_message(req).await })
394        .map_err(anyhow::Error::new::<SdkError>)
395}
396
397/*  Breez Services Helper API's */
398
399/// See [breez_services::mnemonic_to_seed]
400pub fn mnemonic_to_seed(phrase: String) -> Result<Vec<u8>> {
401    breez_services::mnemonic_to_seed(phrase)
402}
403
404/// See [BreezServices::default_config]
405pub fn default_config(
406    env_type: EnvironmentType,
407    api_key: String,
408    node_config: NodeConfig,
409) -> Config {
410    BreezServices::default_config(env_type, api_key, node_config)
411}
412
413/// See [BreezServices::static_backup]
414pub fn static_backup(req: StaticBackupRequest) -> Result<StaticBackupResponse> {
415    BreezServices::static_backup(req).map_err(anyhow::Error::new::<SdkError>)
416}
417
418/// See [BreezServices::service_health_check]
419pub fn service_health_check(api_key: String) -> Result<ServiceHealthCheckResponse> {
420    block_on(async { BreezServices::service_health_check(api_key).await })
421        .map_err(anyhow::Error::new::<SdkError>)
422}
423
424/*  Stream API's */
425
426/// If used, this must be called before `connect`. It can only be called once.
427pub fn breez_events_stream(s: StreamSink<BreezEvent>) -> Result<()> {
428    NOTIFICATION_STREAM
429        .set(s)
430        .map_err(|_| anyhow!("Events stream already created"))?;
431    Ok(())
432}
433
434/// If used, this must be called before `connect`. It can only be called once.
435pub fn breez_log_stream(s: StreamSink<LogEntry>) -> Result<()> {
436    LOG_INIT
437        .set(true)
438        .map_err(|_| anyhow!("Log stream already created"))?;
439    BindingLogger::init(s);
440    Ok(())
441}
442
443/*  LSP API's */
444
445/// See [BreezServices::list_lsps]
446pub fn list_lsps() -> Result<Vec<LspInformation>> {
447    block_on(async { get_breez_services().await?.list_lsps().await })
448        .map_err(anyhow::Error::new::<SdkError>)
449}
450
451/// See [BreezServices::connect_lsp]
452pub fn connect_lsp(lsp_id: String) -> Result<()> {
453    block_on(async { get_breez_services().await?.connect_lsp(lsp_id).await })
454        .map_err(anyhow::Error::new::<SdkError>)
455}
456
457/// See [BreezServices::lsp_id]
458pub fn lsp_id() -> Result<Option<String>> {
459    block_on(async { get_breez_services().await?.lsp_id().await })
460        .map_err(anyhow::Error::new::<SdkError>)
461}
462
463/// See [BreezServices::fetch_lsp_info]
464pub fn fetch_lsp_info(id: String) -> Result<Option<LspInformation>> {
465    block_on(async { get_breez_services().await?.fetch_lsp_info(id).await })
466        .map_err(anyhow::Error::new::<SdkError>)
467}
468
469/// See [BreezServices::lsp_info]
470pub fn lsp_info() -> Result<LspInformation> {
471    block_on(async { get_breez_services().await?.lsp_info().await })
472        .map_err(anyhow::Error::new::<SdkError>)
473}
474
475/// See [BreezServices::close_lsp_channels]
476pub fn close_lsp_channels() -> Result<()> {
477    block_on(async {
478        _ = get_breez_services().await?.close_lsp_channels().await?;
479        Ok(())
480    })
481}
482
483pub fn register_webhook(webhook_url: String) -> Result<()> {
484    block_on(async {
485        get_breez_services()
486            .await?
487            .register_webhook(webhook_url)
488            .await
489    })
490    .map_err(anyhow::Error::new::<SdkError>)
491}
492
493pub fn unregister_webhook(webhook_url: String) -> Result<()> {
494    block_on(async {
495        get_breez_services()
496            .await?
497            .unregister_webhook(webhook_url)
498            .await
499    })
500    .map_err(anyhow::Error::new::<SdkError>)
501}
502
503/*  Backup API's */
504
505/// See [BreezServices::backup]
506pub fn backup() -> Result<()> {
507    block_on(async { get_breez_services().await?.backup().await })
508        .map_err(anyhow::Error::new::<SdkError>)
509}
510
511/// See [BreezServices::backup_status]
512pub fn backup_status() -> Result<BackupStatus> {
513    block_on(async { get_breez_services().await?.backup_status() })
514        .map_err(anyhow::Error::new::<SdkError>)
515}
516
517/*  Parse API's */
518
519pub fn parse_invoice(invoice: String) -> Result<LNInvoice> {
520    invoice::parse_invoice(&invoice).map_err(|e| anyhow::Error::new::<SdkError>(e.into()))
521}
522
523pub fn parse_input(input: String) -> Result<InputType> {
524    block_on(async { parse(&input, None).await })
525}
526
527/*  Payment API's */
528
529/// See [BreezServices::list_payments]
530pub fn list_payments(req: ListPaymentsRequest) -> Result<Vec<Payment>> {
531    block_on(async { get_breez_services().await?.list_payments(req).await })
532        .map_err(anyhow::Error::new::<SdkError>)
533}
534
535/// See [BreezServices::list_payments]
536pub fn payment_by_hash(hash: String) -> Result<Option<Payment>> {
537    block_on(async { get_breez_services().await?.payment_by_hash(hash).await })
538        .map_err(anyhow::Error::new::<SdkError>)
539}
540
541/// See [BreezServices::set_payment_metadata]
542pub fn set_payment_metadata(hash: String, metadata: String) -> Result<()> {
543    block_on(async {
544        get_breez_services()
545            .await?
546            .set_payment_metadata(hash, metadata)
547            .await
548    })
549    .map_err(anyhow::Error::new::<SdkError>)
550}
551
552/*  Lightning Payment API's */
553
554/// See [BreezServices::send_payment]
555pub fn send_payment(req: SendPaymentRequest) -> Result<SendPaymentResponse> {
556    block_on(async { get_breez_services().await?.send_payment(req).await })
557        .map_err(anyhow::Error::new::<SendPaymentError>)
558}
559
560/// See [BreezServices::send_spontaneous_payment]
561pub fn send_spontaneous_payment(req: SendSpontaneousPaymentRequest) -> Result<SendPaymentResponse> {
562    block_on(async {
563        get_breez_services()
564            .await?
565            .send_spontaneous_payment(req)
566            .await
567    })
568    .map_err(anyhow::Error::new::<SendPaymentError>)
569}
570
571/// See [BreezServices::receive_payment]
572pub fn receive_payment(req: ReceivePaymentRequest) -> Result<ReceivePaymentResponse> {
573    block_on(async { get_breez_services().await?.receive_payment(req).await })
574        .map_err(anyhow::Error::new::<ReceivePaymentError>)
575}
576
577/*  LNURL API's */
578
579/// See [BreezServices::lnurl_pay]
580pub fn lnurl_pay(req: LnUrlPayRequest) -> Result<crate::lnurl::pay::LnUrlPayResult> {
581    block_on(async { get_breez_services().await?.lnurl_pay(req).await })
582        .map_err(anyhow::Error::new::<crate::LnUrlPayError>)
583}
584
585/// See [BreezServices::lnurl_withdraw]
586pub fn lnurl_withdraw(req: LnUrlWithdrawRequest) -> Result<LnUrlWithdrawResult> {
587    block_on(async { get_breez_services().await?.lnurl_withdraw(req).await })
588        .map_err(anyhow::Error::new::<crate::LnUrlWithdrawError>)
589}
590
591/// See [BreezServices::lnurl_auth]
592pub fn lnurl_auth(req_data: crate::LnUrlAuthRequestData) -> Result<LnUrlCallbackStatus> {
593    block_on(async { get_breez_services().await?.lnurl_auth(req_data).await })
594        .map_err(anyhow::Error::new::<LnUrlAuthError>)
595}
596
597/*  Support API */
598
599/// See [BreezServices::report_issue]
600pub fn report_issue(req: ReportIssueRequest) -> Result<()> {
601    block_on(async { get_breez_services().await?.report_issue(req).await })
602        .map_err(anyhow::Error::new::<SdkError>)
603}
604
605/*  Fiat Currency API's */
606
607/// See [BreezServices::fetch_fiat_rates]
608pub fn fetch_fiat_rates() -> Result<Vec<Rate>> {
609    block_on(async { get_breez_services().await?.fetch_fiat_rates().await })
610        .map_err(anyhow::Error::new::<SdkError>)
611}
612
613/// See [BreezServices::list_fiat_currencies]
614pub fn list_fiat_currencies() -> Result<Vec<FiatCurrency>> {
615    block_on(async { get_breez_services().await?.list_fiat_currencies().await })
616        .map_err(anyhow::Error::new::<SdkError>)
617}
618
619/*  On-Chain Swap API's */
620
621/// See [BreezServices::pay_onchain]
622pub fn pay_onchain(req: PayOnchainRequest) -> Result<PayOnchainResponse> {
623    block_on(async { get_breez_services().await?.pay_onchain(req).await })
624        .map_err(anyhow::Error::new::<SendOnchainError>)
625}
626
627/// See [BreezServices::receive_onchain]
628pub fn receive_onchain(req: ReceiveOnchainRequest) -> Result<SwapInfo> {
629    block_on(async { get_breez_services().await?.receive_onchain(req).await })
630        .map_err(anyhow::Error::new::<ReceiveOnchainError>)
631}
632
633/// See [BreezServices::buy_bitcoin]
634pub fn buy_bitcoin(req: BuyBitcoinRequest) -> Result<BuyBitcoinResponse> {
635    block_on(async { get_breez_services().await?.buy_bitcoin(req).await })
636        .map_err(anyhow::Error::new::<ReceiveOnchainError>)
637}
638
639/// See [BreezServices::redeem_onchain_funds]
640pub fn redeem_onchain_funds(req: RedeemOnchainFundsRequest) -> Result<RedeemOnchainFundsResponse> {
641    block_on(async { get_breez_services().await?.redeem_onchain_funds(req).await })
642        .map_err(anyhow::Error::new::<RedeemOnchainError>)
643}
644
645/// See [BreezServices::prepare_redeem_onchain_funds]
646pub fn prepare_redeem_onchain_funds(
647    req: PrepareRedeemOnchainFundsRequest,
648) -> Result<PrepareRedeemOnchainFundsResponse> {
649    block_on(async {
650        get_breez_services()
651            .await?
652            .prepare_redeem_onchain_funds(req)
653            .await
654    })
655    .map_err(anyhow::Error::new::<RedeemOnchainError>)
656}
657
658/*  Refundables API's */
659
660/// See [BreezServices::list_refundables]
661pub fn list_refundables() -> Result<Vec<SwapInfo>> {
662    block_on(async { get_breez_services().await?.list_refundables().await })
663        .map_err(anyhow::Error::new::<SdkError>)
664}
665
666/// See [BreezServices::prepare_refund]
667pub fn prepare_refund(req: PrepareRefundRequest) -> Result<PrepareRefundResponse> {
668    block_on(async { get_breez_services().await?.prepare_refund(req).await })
669        .map_err(anyhow::Error::new::<SdkError>)
670}
671
672/// See [BreezServices::refund]
673pub fn refund(req: RefundRequest) -> Result<RefundResponse> {
674    block_on(async { get_breez_services().await?.refund(req).await })
675        .map_err(anyhow::Error::new::<SdkError>)
676}
677
678/// See [BreezServices::rescan_swaps]
679pub fn rescan_swaps() -> Result<()> {
680    block_on(async { get_breez_services().await?.rescan_swaps().await })
681        .map_err(anyhow::Error::new::<SdkError>)
682}
683
684/// See [BreezServices::redeem_swap]
685pub fn redeem_swap(swap_address: String) -> Result<()> {
686    block_on(async { get_breez_services().await?.redeem_swap(swap_address).await })
687        .map_err(anyhow::Error::new::<SdkError>)
688}
689
690/*  In Progress Swap API's */
691
692/// See [BreezServices::in_progress_swap]
693pub fn in_progress_swap() -> Result<Option<SwapInfo>> {
694    block_on(async { get_breez_services().await?.in_progress_swap().await })
695        .map_err(anyhow::Error::new::<SdkError>)
696}
697
698/// See [BreezServices::list_swaps]
699pub fn list_swaps(req: ListSwapsRequest) -> Result<Vec<SwapInfo>> {
700    block_on(async { get_breez_services().await?.list_swaps(req).await })
701        .map_err(anyhow::Error::new::<SdkError>)
702}
703
704/// See [BreezServices::claim_reverse_swap]
705pub fn claim_reverse_swap(lockup_address: String) -> Result<()> {
706    block_on(async {
707        get_breez_services()
708            .await?
709            .claim_reverse_swap(lockup_address)
710            .await
711    })
712    .map_err(anyhow::Error::new::<SdkError>)
713}
714
715/*  Swap Fee API's */
716
717/// See [BreezServices::open_channel_fee]
718pub fn open_channel_fee(req: OpenChannelFeeRequest) -> Result<OpenChannelFeeResponse> {
719    block_on(async { get_breez_services().await?.open_channel_fee(req).await })
720        .map_err(anyhow::Error::new::<SdkError>)
721}
722
723/// See [BreezServices::fetch_reverse_swap_fees]
724pub fn fetch_reverse_swap_fees(req: ReverseSwapFeesRequest) -> Result<ReverseSwapPairInfo> {
725    block_on(async {
726        get_breez_services()
727            .await?
728            .fetch_reverse_swap_fees(req)
729            .await
730    })
731    .map_err(anyhow::Error::new::<SdkError>)
732}
733
734/// See [BreezServices::onchain_payment_limits]
735pub fn onchain_payment_limits() -> Result<OnchainPaymentLimitsResponse> {
736    block_on(async { get_breez_services().await?.onchain_payment_limits().await })
737        .map_err(anyhow::Error::new::<SdkError>)
738}
739
740/// See [BreezServices::prepare_onchain_payment]
741pub fn prepare_onchain_payment(
742    req: PrepareOnchainPaymentRequest,
743) -> Result<PrepareOnchainPaymentResponse> {
744    block_on(async {
745        get_breez_services()
746            .await?
747            .prepare_onchain_payment(req)
748            .await
749            .map_err(anyhow::Error::new::<SendOnchainError>)
750    })
751}
752
753/// See [BreezServices::in_progress_onchain_payments]
754pub fn in_progress_onchain_payments() -> Result<Vec<ReverseSwapInfo>> {
755    block_on(async {
756        get_breez_services()
757            .await?
758            .in_progress_onchain_payments()
759            .await
760    })
761    .map_err(anyhow::Error::new::<SdkError>)
762}
763
764/// See [BreezServices::recommended_fees]
765pub fn recommended_fees() -> Result<RecommendedFees> {
766    block_on(async { get_breez_services().await?.recommended_fees().await })
767        .map_err(anyhow::Error::new::<SdkError>)
768}
769
770/*  CLI API's */
771
772/// See [BreezServices::execute_dev_command]
773pub fn execute_command(command: String) -> Result<String> {
774    block_on(async {
775        get_breez_services()
776            .await?
777            .execute_dev_command(command)
778            .await
779    })
780    .map_err(anyhow::Error::new::<SdkError>)
781}
782
783/// See [BreezServices::generate_diagnostic_data]
784pub fn generate_diagnostic_data() -> Result<String> {
785    block_on(async { get_breez_services().await?.generate_diagnostic_data().await })
786        .map_err(anyhow::Error::new::<SdkError>)
787}
788
789/*  Binding Related Logic */
790
791struct BindingEventListener;
792
793impl EventListener for BindingEventListener {
794    fn on_event(&self, e: BreezEvent) {
795        if let Some(stream) = NOTIFICATION_STREAM.get() {
796            stream.add(e);
797        }
798    }
799}
800
801struct BindingLogger {
802    log_stream: StreamSink<LogEntry>,
803}
804
805impl BindingLogger {
806    fn init(log_stream: StreamSink<LogEntry>) {
807        let binding_logger = BindingLogger { log_stream };
808        log::set_boxed_logger(Box::new(binding_logger)).unwrap();
809        log::set_max_level(LevelFilter::Trace);
810    }
811}
812
813impl log::Log for BindingLogger {
814    fn enabled(&self, m: &Metadata) -> bool {
815        m.level() <= Level::Trace
816    }
817
818    fn log(&self, record: &Record) {
819        if self.enabled(record.metadata()) {
820            self.log_stream.add(LogEntry {
821                line: record.args().to_string(),
822                level: record.level().as_str().to_string(),
823            });
824        }
825    }
826    fn flush(&self) {}
827}
828
829async fn get_breez_services() -> Result<Arc<BreezServices>, SdkError> {
830    match BREEZ_SERVICES_INSTANCE.lock().await.as_ref() {
831        None => Err(SdkError::Generic {
832            err: "Node service was not initialized".into(),
833        }),
834        Some(sdk) => Ok(sdk.clone()),
835    }
836}
837
838fn block_on<F: Future>(future: F) -> F::Output {
839    rt().block_on(future)
840}
841
842pub(crate) fn rt() -> &'static tokio::runtime::Runtime {
843    &RT
844}