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