1use std::{
2 collections::HashMap,
3 ops::ControlFlow,
4 sync::{Arc, Mutex},
5 time::Duration,
6};
7
8use crate::{
9 blockdata::address::BitcoinAddress, Address, Bolt11Invoice, ElectrumClient, EsploraClient,
10 LightningPayment, LwkError, Mnemonic, Network,
11};
12use log::{Level, Metadata, Record};
13use lwk_boltz::{
14 ChainSwapDataSerializable, ChainSwapStates, InvoiceDataSerializable,
15 PreparePayDataSerializable, RevSwapStates, SubSwapStates,
16};
17use std::fmt;
18
19#[derive(uniffi::Enum)]
21pub enum LogLevel {
22 Debug,
24 Info,
26 Warn,
28 Error,
30}
31
32#[uniffi::export(with_foreign)]
36pub trait Logging: Send + Sync {
37 fn log(&self, level: LogLevel, message: String);
39}
40
41#[derive(uniffi::Object)]
43pub struct LoggingLink {
44 #[allow(dead_code)]
45 pub(crate) inner: Arc<dyn Logging>,
46}
47
48#[uniffi::export]
49impl LoggingLink {
50 #[uniffi::constructor]
52 pub fn new(logging: Arc<dyn Logging>) -> Self {
53 Self { inner: logging }
54 }
55}
56
57struct LoggingBridge {
59 inner: Arc<dyn Logging>,
60}
61
62impl log::Log for LoggingBridge {
63 fn enabled(&self, _metadata: &Metadata) -> bool {
64 true
65 }
66
67 fn log(&self, record: &Record) {
68 let level = match record.level() {
69 Level::Error => LogLevel::Error,
70 Level::Warn => LogLevel::Warn,
71 Level::Info => LogLevel::Info,
72 Level::Debug => LogLevel::Debug,
73 Level::Trace => LogLevel::Debug, };
75
76 let message = format!("{}", record.args());
77 self.inner.log(level, message);
78 }
79
80 fn flush(&self) {}
81}
82
83#[derive(uniffi::Record)]
85pub struct BoltzSessionBuilder {
86 network: Arc<Network>,
87 client: Arc<AnyClient>,
88 #[uniffi(default = None)]
89 timeout: Option<u64>,
90 #[uniffi(default = None)]
91 mnemonic: Option<Arc<Mnemonic>>,
92 #[uniffi(default = None)]
93 logging: Option<Arc<dyn Logging>>,
94 #[uniffi(default = false)]
95 polling: bool,
96 #[uniffi(default = None)]
97 timeout_advance: Option<u64>,
98 #[uniffi(default = None)]
99 next_index_to_use: Option<u32>,
100 #[uniffi(default = None)]
101 referral_id: Option<String>,
102 #[uniffi(default = None)]
103 bitcoin_electrum_client_url: Option<String>,
104 #[uniffi(default = false)]
105 random_preimages: bool,
106}
107
108#[derive(uniffi::Object)]
114pub struct BoltzSession {
115 inner: lwk_boltz::blocking::BoltzSession,
116 #[allow(dead_code)]
117 logging: Option<Arc<dyn Logging>>,
118}
119
120#[derive(uniffi::Object)]
121pub struct PreparePayResponse {
122 inner: Mutex<Option<lwk_boltz::blocking::PreparePayResponse>>,
124}
125
126#[derive(uniffi::Object)]
127pub struct WebHook {
128 url: String,
129 status: Vec<String>,
130}
131
132#[derive(uniffi::Object)]
133pub struct InvoiceResponse {
134 inner: Mutex<Option<lwk_boltz::blocking::InvoiceResponse>>,
136}
137
138#[derive(uniffi::Object)]
139#[uniffi::export(Display)]
140pub struct SwapList {
141 inner: Vec<lwk_boltz::SwapRestoreResponse>,
142}
143
144#[derive(uniffi::Object)]
145pub struct LockupResponse {
146 inner: Mutex<Option<lwk_boltz::blocking::LockupResponse>>,
147}
148
149impl fmt::Display for SwapList {
150 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
151 let json = serde_json::to_string(&self.inner).map_err(|_| fmt::Error)?;
152 write!(f, "{json}")
153 }
154}
155
156#[derive(uniffi::Enum)]
157pub enum PaymentState {
158 Continue,
159 Success,
160 Failed,
161}
162
163#[derive(uniffi::Object)]
164pub enum AnyClient {
165 Electrum(Arc<ElectrumClient>),
166 Esplora(Arc<EsploraClient>),
167}
168
169#[uniffi::export]
170impl AnyClient {
171 #[uniffi::constructor]
172 pub fn from_electrum(client: Arc<ElectrumClient>) -> Self {
173 AnyClient::Electrum(client)
174 }
175
176 #[uniffi::constructor]
177 pub fn from_esplora(client: Arc<EsploraClient>) -> Self {
178 AnyClient::Esplora(client)
179 }
180}
181
182#[uniffi::export]
183impl BoltzSession {
184 #[uniffi::constructor]
189 pub fn new(network: &Network, client: &AnyClient) -> Result<Self, LwkError> {
190 let client_arc = match client {
191 AnyClient::Electrum(c) => Arc::new(AnyClient::Electrum(c.clone())),
192 AnyClient::Esplora(c) => Arc::new(AnyClient::Esplora(c.clone())),
193 };
194 let builder = BoltzSessionBuilder {
195 network: Arc::new(*network),
196 client: client_arc,
197 timeout: None,
198 mnemonic: None,
199 logging: None,
200 polling: false,
201 timeout_advance: None,
202 next_index_to_use: None,
203 referral_id: None,
204 bitcoin_electrum_client_url: None,
205 random_preimages: false,
206 };
207 Self::from_builder(builder)
208 }
209
210 #[uniffi::constructor]
212 pub fn from_builder(builder: BoltzSessionBuilder) -> Result<Self, LwkError> {
213 if let Some(ref logger_impl) = builder.logging {
215 std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {
217 logger_impl.log(LogLevel::Debug, "Logger validation test".to_string());
218 })).map_err(|_| LwkError::Generic {
219 msg: "Logger validation failed. Please ensure you pass an instance of a class that implements the Logging trait, not the class itself.".to_string(),
220 })?;
221 }
222
223 if let Some(ref logger_impl) = builder.logging {
225 let bridge = LoggingBridge {
226 inner: logger_impl.clone(),
227 };
228 let _ = log::set_boxed_logger(Box::new(bridge))
231 .map(|()| log::set_max_level(log::LevelFilter::Trace));
232 }
233 log::info!("Creating lightning session from builder");
234
235 let network_value = builder.network.as_ref().into();
236
237 let client = match builder.client.as_ref() {
238 AnyClient::Electrum(client) => {
239 let boltz_client = lwk_boltz::clients::ElectrumClient::from_client(
240 client.clone_client().expect("TODO"),
241 network_value,
242 );
243 lwk_boltz::clients::AnyClient::Electrum(Arc::new(boltz_client))
244 }
245 AnyClient::Esplora(client) => {
246 let boltz_client = lwk_boltz::clients::EsploraClient::from_client(
247 Arc::new(client.clone_async_client().expect("TODO")),
248 network_value,
249 );
250 lwk_boltz::clients::AnyClient::Esplora(Arc::new(boltz_client))
251 }
252 };
253
254 let mut lwk_builder = lwk_boltz::BoltzSession::builder(network_value, client);
255 if let Some(timeout_secs) = builder.timeout {
256 lwk_builder = lwk_builder.create_swap_timeout(Duration::from_secs(timeout_secs));
257 }
258 if let Some(mnemonic) = builder.mnemonic {
259 lwk_builder = lwk_builder.mnemonic(mnemonic.inner());
260 }
261 lwk_builder = lwk_builder.polling(builder.polling);
262 if let Some(timeout_advance_secs) = builder.timeout_advance {
263 lwk_builder = lwk_builder.timeout_advance(Duration::from_secs(timeout_advance_secs));
264 }
265 if let Some(next_index_to_use) = builder.next_index_to_use {
266 lwk_builder = lwk_builder.next_index_to_use(next_index_to_use);
267 }
268 if let Some(referral_id) = builder.referral_id {
269 lwk_builder = lwk_builder.referral_id(referral_id);
270 }
271 lwk_builder = lwk_builder.random_preimages(builder.random_preimages);
272
273 let inner = lwk_builder
274 .build_blocking()
275 .map_err(|e| LwkError::Generic {
276 msg: format!("Failed to create blocking lightning session: {e:?}"),
277 })?;
278 Ok(Self {
279 inner,
280 logging: builder.logging,
281 })
282 }
283
284 pub fn prepare_pay(
286 &self,
287 lightning_payment: &LightningPayment,
288 refund_address: &Address,
289 webhook: Option<Arc<WebHook>>,
290 ) -> Result<PreparePayResponse, LwkError> {
291 let status = webhook
292 .as_ref()
293 .filter(|w| !w.status.is_empty())
294 .map(|w| {
295 w.status
296 .iter()
297 .map(|s| s.parse::<SubSwapStates>())
298 .collect::<Result<Vec<_>, _>>()
299 })
300 .transpose()
301 .map_err(|_| LwkError::Generic {
302 msg: "Invalid status".to_string(),
303 })?;
304 let webhook = webhook
305 .as_ref()
306 .map(|w| lwk_boltz::Webhook::<SubSwapStates> {
307 url: w.url.to_string(),
308 hash_swap_id: None,
309 status,
310 });
311 let response =
312 self.inner
313 .prepare_pay(lightning_payment.as_ref(), refund_address.as_ref(), webhook)?;
314
315 Ok(PreparePayResponse {
316 inner: Mutex::new(Some(response)),
317 })
318 }
319
320 pub fn restore_prepare_pay(&self, data: &str) -> Result<PreparePayResponse, LwkError> {
322 let data = PreparePayDataSerializable::deserialize(data)?;
323 let response = self.inner.restore_prepare_pay(data)?;
324 Ok(PreparePayResponse {
325 inner: Mutex::new(Some(response)),
326 })
327 }
328
329 pub fn invoice(
331 &self,
332 amount: u64,
333 description: Option<String>,
334 claim_address: &Address,
335 webhook: Option<Arc<WebHook>>,
336 ) -> Result<InvoiceResponse, LwkError> {
337 let status = webhook
338 .as_ref()
339 .filter(|w| !w.status.is_empty())
340 .map(|w| {
341 w.status
342 .iter()
343 .map(|s| s.parse::<RevSwapStates>())
344 .collect::<Result<Vec<_>, _>>()
345 })
346 .transpose()
347 .map_err(|_| LwkError::Generic {
348 msg: "Invalid status".to_string(),
349 })?;
350 let webhook = webhook
351 .as_ref()
352 .map(|w| lwk_boltz::Webhook::<RevSwapStates> {
353 url: w.url.to_string(),
354 hash_swap_id: None,
355 status,
356 });
357 let response = self
358 .inner
359 .invoice(amount, description, claim_address.as_ref(), webhook)
360 .map_err(|e| LwkError::Generic {
361 msg: format!("Invoice failed: {e:?}"),
362 })?;
363
364 Ok(InvoiceResponse {
365 inner: Mutex::new(Some(response)),
366 })
367 }
368
369 pub fn restore_invoice(&self, data: &str) -> Result<InvoiceResponse, LwkError> {
371 let data: InvoiceDataSerializable = serde_json::from_str(data)?;
372 let response = self.inner.restore_invoice(data)?;
373 Ok(InvoiceResponse {
374 inner: Mutex::new(Some(response)),
375 })
376 }
377
378 pub fn btc_to_lbtc(
380 &self,
381 amount: u64,
382 refund_address: &BitcoinAddress,
383 claim_address: &Address,
384 webhook: Option<Arc<WebHook>>,
385 ) -> Result<LockupResponse, LwkError> {
386 let webhook = webhook
387 .as_ref()
388 .map(|w| lwk_boltz::Webhook::<ChainSwapStates> {
389 url: w.url.to_string(),
390 hash_swap_id: None,
391 status: None,
392 });
393 let response = self.inner.btc_to_lbtc(
394 amount,
395 refund_address.as_ref(),
396 claim_address.as_ref(),
397 webhook,
398 )?;
399 Ok(LockupResponse {
400 inner: Mutex::new(Some(response)),
401 })
402 }
403
404 pub fn lbtc_to_btc(
406 &self,
407 amount: u64,
408 refund_address: &Address,
409 claim_address: &BitcoinAddress,
410 webhook: Option<Arc<WebHook>>,
411 ) -> Result<LockupResponse, LwkError> {
412 let webhook = webhook
413 .as_ref()
414 .map(|w| lwk_boltz::Webhook::<ChainSwapStates> {
415 url: w.url.to_string(),
416 hash_swap_id: None,
417 status: None,
418 });
419
420 let response = self.inner.lbtc_to_btc(
421 amount,
422 refund_address.as_ref(),
423 claim_address.as_ref(),
424 webhook,
425 )?;
426 Ok(LockupResponse {
427 inner: Mutex::new(Some(response)),
428 })
429 }
430
431 pub fn restore_lockup(&self, data: &str) -> Result<LockupResponse, LwkError> {
433 let data = ChainSwapDataSerializable::deserialize(data)?;
434 let response = self.inner.restore_lockup(data)?;
435 Ok(LockupResponse {
436 inner: Mutex::new(Some(response)),
437 })
438 }
439
440 pub fn rescue_file(&self) -> Result<String, LwkError> {
445 let rescue_file = self.inner.rescue_file();
446 let rescue_file_json = serde_json::to_string(&rescue_file)?;
447 Ok(rescue_file_json)
448 }
449
450 pub fn swap_restore(&self) -> Result<SwapList, LwkError> {
454 let response = self.inner.swap_restore()?;
455 Ok(SwapList { inner: response })
456 }
457
458 pub fn restorable_reverse_swaps(
460 &self,
461 swap_list: &SwapList,
462 claim_address: &Address,
463 ) -> Result<Vec<String>, LwkError> {
464 let response = self
465 .inner
466 .restorable_reverse_swaps(&swap_list.inner, claim_address.as_ref())?;
467 let data = response
468 .into_iter()
469 .map(|e| self.inner.restore_invoice(e.into()))
470 .map(|e| e.and_then(|e| e.serialize()))
471 .collect::<Result<Vec<_>, _>>()?;
472
473 Ok(data)
474 }
475
476 pub fn restorable_submarine_swaps(
478 &self,
479 swap_list: &SwapList,
480 refund_address: &Address,
481 ) -> Result<Vec<String>, LwkError> {
482 let response = self
483 .inner
484 .restorable_submarine_swaps(&swap_list.inner, refund_address.as_ref())?;
485 let data = response
486 .into_iter()
487 .map(|e| self.inner.restore_prepare_pay(e.into()))
488 .map(|e| e.and_then(|e| e.serialize()))
489 .collect::<Result<Vec<_>, _>>()?;
490 Ok(data)
491 }
492
493 pub fn restorable_btc_to_lbtc_swaps(
495 &self,
496 swap_list: &SwapList,
497 claim_address: &Address,
498 refund_address: &BitcoinAddress,
499 ) -> Result<Vec<String>, LwkError> {
500 let response = self.inner.restorable_btc_to_lbtc_swaps(
501 &swap_list.inner,
502 claim_address.as_ref(),
503 refund_address.as_ref(),
504 )?;
505 let data = response
506 .into_iter()
507 .map(|e| self.inner.restore_lockup(e.into()))
508 .map(|e| e.and_then(|e| e.serialize()))
509 .collect::<Result<Vec<_>, _>>()?;
510 Ok(data)
511 }
512
513 pub fn restorable_lbtc_to_btc_swaps(
515 &self,
516 swap_list: &SwapList,
517 claim_address: &BitcoinAddress,
518 refund_address: &Address,
519 ) -> Result<Vec<String>, LwkError> {
520 let response = self.inner.restorable_lbtc_to_btc_swaps(
521 &swap_list.inner,
522 claim_address.as_ref(),
523 refund_address.as_ref(),
524 )?;
525 let data = response
526 .into_iter()
527 .map(|e| self.inner.restore_lockup(e.into()))
528 .map(|e| e.and_then(|e| e.serialize()))
529 .collect::<Result<Vec<_>, _>>()?;
530 Ok(data)
531 }
532
533 pub fn fetch_swaps_info(&self) -> Result<String, LwkError> {
535 let (reverse, submarine, chain) = self.inner.fetch_swaps_info()?;
536 let reverse_json = serde_json::to_value(&reverse)?;
537 let submarine_json = serde_json::to_value(&submarine)?;
538 let chain_json = serde_json::to_value(&chain)?;
539 let mut result = HashMap::new();
540 result.insert("reverse".to_string(), reverse_json);
541 result.insert("submarine".to_string(), submarine_json);
542 result.insert("chain".to_string(), chain_json);
543 let result_json = serde_json::to_string(&result)?;
544 Ok(result_json)
545 }
546
547 pub fn next_index_to_use(&self) -> u32 {
549 self.inner.next_index_to_use()
550 }
551
552 pub fn set_next_index_to_use(&self, next_index_to_use: u32) {
556 self.inner.set_next_index_to_use(next_index_to_use);
557 }
558}
559
560#[uniffi::export]
561impl PreparePayResponse {
562 pub fn complete_pay(&self) -> Result<bool, LwkError> {
563 let mut lock = self.inner.lock()?;
564 let response = lock.take().ok_or(LwkError::ObjectConsumed)?;
565 Ok(response.complete_pay()?)
566 }
567
568 pub fn swap_id(&self) -> Result<String, LwkError> {
569 Ok(self
570 .inner
571 .lock()?
572 .as_ref()
573 .ok_or(LwkError::ObjectConsumed)?
574 .swap_id())
575 }
576
577 pub fn serialize(&self) -> Result<String, LwkError> {
581 Ok(self
582 .inner
583 .lock()?
584 .as_ref()
585 .ok_or(LwkError::ObjectConsumed)?
586 .serialize()?)
587 }
588
589 pub fn uri(&self) -> Result<String, LwkError> {
590 Ok(self
591 .inner
592 .lock()?
593 .as_ref()
594 .ok_or(LwkError::ObjectConsumed)?
595 .uri())
596 }
597
598 pub fn uri_address(&self) -> Result<Arc<Address>, LwkError> {
599 let uri_address = self
600 .inner
601 .lock()?
602 .as_ref()
603 .ok_or(LwkError::ObjectConsumed)?
604 .uri_address()?;
605 Ok(Arc::new(uri_address.into()))
606 }
607 pub fn uri_amount(&self) -> Result<u64, LwkError> {
608 Ok(self
609 .inner
610 .lock()?
611 .as_ref()
612 .ok_or(LwkError::ObjectConsumed)?
613 .uri_amount())
614 }
615
616 pub fn fee(&self) -> Result<Option<u64>, LwkError> {
620 Ok(self
621 .inner
622 .lock()?
623 .as_ref()
624 .ok_or(LwkError::ObjectConsumed)?
625 .fee())
626 }
627
628 pub fn boltz_fee(&self) -> Result<Option<u64>, LwkError> {
633 Ok(self
634 .inner
635 .lock()?
636 .as_ref()
637 .ok_or(LwkError::ObjectConsumed)?
638 .boltz_fee())
639 }
640
641 pub fn advance(&self) -> Result<PaymentState, LwkError> {
642 let mut lock = self.inner.lock()?;
643 let mut response = lock.take().ok_or(LwkError::ObjectConsumed)?;
644 Ok(match response.advance() {
645 Ok(ControlFlow::Continue(_update)) => {
646 *lock = Some(response);
647 PaymentState::Continue
648 }
649 Ok(ControlFlow::Break(update)) => {
650 *lock = Some(response);
651 if update {
652 PaymentState::Success
653 } else {
654 PaymentState::Failed
655 }
656 }
657 Err(lwk_boltz::Error::NoBoltzUpdate) => {
658 *lock = Some(response);
659 return Err(LwkError::NoBoltzUpdate);
660 }
661 Err(e) => return Err(e.into()),
662 })
663 }
664}
665
666#[uniffi::export]
667impl InvoiceResponse {
668 pub fn bolt11_invoice(&self) -> Result<Bolt11Invoice, LwkError> {
669 let bolt11_invoice = self
670 .inner
671 .lock()?
672 .as_ref()
673 .ok_or(LwkError::ObjectConsumed)?
674 .bolt11_invoice();
675 Ok(Bolt11Invoice::from(bolt11_invoice))
676 }
677
678 pub fn swap_id(&self) -> Result<String, LwkError> {
679 Ok(self
680 .inner
681 .lock()?
682 .as_ref()
683 .ok_or(LwkError::ObjectConsumed)?
684 .swap_id())
685 }
686
687 pub fn fee(&self) -> Result<Option<u64>, LwkError> {
691 Ok(self
692 .inner
693 .lock()?
694 .as_ref()
695 .ok_or(LwkError::ObjectConsumed)?
696 .fee())
697 }
698
699 pub fn boltz_fee(&self) -> Result<Option<u64>, LwkError> {
704 Ok(self
705 .inner
706 .lock()?
707 .as_ref()
708 .ok_or(LwkError::ObjectConsumed)?
709 .boltz_fee())
710 }
711
712 pub fn claim_txid(&self) -> Result<Option<String>, LwkError> {
714 Ok(self
715 .inner
716 .lock()?
717 .as_ref()
718 .ok_or(LwkError::ObjectConsumed)?
719 .claim_txid()
720 .map(|txid| txid.to_string()))
721 }
722
723 pub fn serialize(&self) -> Result<String, LwkError> {
727 Ok(self
728 .inner
729 .lock()?
730 .as_ref()
731 .ok_or(LwkError::ObjectConsumed)?
732 .serialize()?)
733 }
734
735 pub fn complete_pay(&self) -> Result<bool, LwkError> {
736 let mut lock = self.inner.lock()?;
737 let response = lock.take().ok_or(LwkError::ObjectConsumed)?;
738 Ok(response.complete_pay()?)
739 }
740
741 pub fn advance(&self) -> Result<PaymentState, LwkError> {
742 let mut lock = self.inner.lock()?;
743 let mut response = lock.take().ok_or(LwkError::ObjectConsumed)?;
744 Ok(match response.advance() {
745 Ok(ControlFlow::Continue(_update)) => {
746 *lock = Some(response);
747 PaymentState::Continue
748 }
749 Ok(ControlFlow::Break(update)) => {
750 *lock = Some(response);
751 if update {
752 PaymentState::Success
753 } else {
754 PaymentState::Failed
755 }
756 }
757 Err(lwk_boltz::Error::NoBoltzUpdate) => {
758 *lock = Some(response);
759 return Err(LwkError::NoBoltzUpdate);
760 }
761 Err(e) => return Err(e.into()),
762 })
763 }
764}
765
766#[uniffi::export]
767impl LockupResponse {
768 pub fn swap_id(&self) -> Result<String, LwkError> {
769 Ok(self
770 .inner
771 .lock()?
772 .as_ref()
773 .ok_or(LwkError::ObjectConsumed)?
774 .swap_id())
775 }
776
777 pub fn lockup_address(&self) -> Result<String, LwkError> {
778 Ok(self
779 .inner
780 .lock()?
781 .as_ref()
782 .ok_or(LwkError::ObjectConsumed)?
783 .lockup_address()
784 .to_string())
785 }
786
787 pub fn expected_amount(&self) -> Result<u64, LwkError> {
788 Ok(self
789 .inner
790 .lock()?
791 .as_ref()
792 .ok_or(LwkError::ObjectConsumed)?
793 .expected_amount())
794 }
795
796 pub fn chain_from(&self) -> Result<String, LwkError> {
797 Ok(self
798 .inner
799 .lock()?
800 .as_ref()
801 .ok_or(LwkError::ObjectConsumed)?
802 .chain_from()
803 .to_string())
804 }
805
806 pub fn chain_to(&self) -> Result<String, LwkError> {
807 Ok(self
808 .inner
809 .lock()?
810 .as_ref()
811 .ok_or(LwkError::ObjectConsumed)?
812 .chain_to()
813 .to_string())
814 }
815
816 pub fn fee(&self) -> Result<Option<u64>, LwkError> {
820 Ok(self
821 .inner
822 .lock()?
823 .as_ref()
824 .ok_or(LwkError::ObjectConsumed)?
825 .fee())
826 }
827
828 pub fn boltz_fee(&self) -> Result<Option<u64>, LwkError> {
832 Ok(self
833 .inner
834 .lock()?
835 .as_ref()
836 .ok_or(LwkError::ObjectConsumed)?
837 .boltz_fee())
838 }
839
840 pub fn advance(&self) -> Result<PaymentState, LwkError> {
841 let mut lock = self.inner.lock()?;
842 let mut response = lock.take().ok_or(LwkError::ObjectConsumed)?;
843 Ok(match response.advance() {
844 Ok(ControlFlow::Continue(_update)) => {
845 *lock = Some(response);
846 PaymentState::Continue
847 }
848 Ok(ControlFlow::Break(update)) => {
849 if update {
850 PaymentState::Success
851 } else {
852 PaymentState::Failed
853 }
854 }
855 Err(lwk_boltz::Error::NoBoltzUpdate) => {
856 *lock = Some(response);
857 return Err(LwkError::NoBoltzUpdate);
858 }
859 Err(e) => return Err(e.into()),
860 })
861 }
862
863 pub fn complete(&self) -> Result<bool, LwkError> {
864 let mut lock = self.inner.lock()?;
865 let response = lock.take().ok_or(LwkError::ObjectConsumed)?;
866 Ok(response.complete()?)
867 }
868
869 pub fn serialize(&self) -> Result<String, LwkError> {
870 Ok(self
871 .inner
872 .lock()?
873 .as_ref()
874 .ok_or(LwkError::ObjectConsumed)?
875 .serialize()?)
876 }
877}
878
879#[uniffi::export]
880impl WebHook {
881 #[uniffi::constructor]
882 pub fn new(url: String, status: Vec<String>) -> Arc<Self> {
883 Arc::new(Self { url, status })
884 }
885}