diff --git a/lib/bindings/langs/flutter/breez_sdk_liquid/include/breez_sdk_liquid.h b/lib/bindings/langs/flutter/breez_sdk_liquid/include/breez_sdk_liquid.h index 3edec0b14..01759fe78 100644 --- a/lib/bindings/langs/flutter/breez_sdk_liquid/include/breez_sdk_liquid.h +++ b/lib/bindings/langs/flutter/breez_sdk_liquid/include/breez_sdk_liquid.h @@ -689,22 +689,26 @@ typedef struct wire_cst_list_refundable_swap { int32_t len; } wire_cst_list_refundable_swap; -typedef struct wire_cst_blockchain_details { +typedef struct wire_cst_blockchain_info { uint32_t liquid_tip; uint32_t bitcoin_tip; -} wire_cst_blockchain_details; +} wire_cst_blockchain_info; typedef struct wire_cst_check_message_response { bool is_valid; } wire_cst_check_message_response; -typedef struct wire_cst_get_info_response { +typedef struct wire_cst_wallet_info { uint64_t balance_sat; uint64_t pending_send_sat; uint64_t pending_receive_sat; struct wire_cst_list_prim_u_8_strict *fingerprint; struct wire_cst_list_prim_u_8_strict *pubkey; - struct wire_cst_blockchain_details blockchain_details; +} wire_cst_wallet_info; + +typedef struct wire_cst_get_info_response { + struct wire_cst_wallet_info wallet_info; + struct wire_cst_blockchain_info blockchain_info; } wire_cst_get_info_response; typedef struct wire_cst_InputType_BitcoinAddress { diff --git a/lib/bindings/src/breez_sdk_liquid.udl b/lib/bindings/src/breez_sdk_liquid.udl index 9f85f20e1..8eb7c42bd 100644 --- a/lib/bindings/src/breez_sdk_liquid.udl +++ b/lib/bindings/src/breez_sdk_liquid.udl @@ -354,18 +354,22 @@ dictionary ConnectWithSignerRequest { Config config; }; -dictionary BlockchainDetails { +dictionary BlockchainInfo { u32 liquid_tip; u32 bitcoin_tip; }; -dictionary GetInfoResponse { +dictionary WalletInfo { u64 balance_sat; u64 pending_send_sat; u64 pending_receive_sat; string fingerprint; string pubkey; - BlockchainDetails blockchain_details; +}; + +dictionary GetInfoResponse { + WalletInfo wallet_info; + BlockchainInfo blockchain_info; }; dictionary SignMessageRequest { diff --git a/lib/core/src/frb_generated.rs b/lib/core/src/frb_generated.rs index 266dc8400..48587d2ef 100644 --- a/lib/core/src/frb_generated.rs +++ b/lib/core/src/frb_generated.rs @@ -2306,12 +2306,12 @@ impl SseDecode for crate::bindings::BitcoinAddressData { } } -impl SseDecode for crate::model::BlockchainDetails { +impl SseDecode for crate::model::BlockchainInfo { // Codec=Sse (Serialization based), see doc to use other codecs fn sse_decode(deserializer: &mut flutter_rust_bridge::for_generated::SseDeserializer) -> Self { let mut var_liquidTip = ::sse_decode(deserializer); let mut var_bitcoinTip = ::sse_decode(deserializer); - return crate::model::BlockchainDetails { + return crate::model::BlockchainInfo { liquid_tip: var_liquidTip, bitcoin_tip: var_bitcoinTip, }; @@ -2479,19 +2479,11 @@ impl SseDecode for crate::bindings::FiatCurrency { impl SseDecode for crate::model::GetInfoResponse { // Codec=Sse (Serialization based), see doc to use other codecs fn sse_decode(deserializer: &mut flutter_rust_bridge::for_generated::SseDeserializer) -> Self { - let mut var_balanceSat = ::sse_decode(deserializer); - let mut var_pendingSendSat = ::sse_decode(deserializer); - let mut var_pendingReceiveSat = ::sse_decode(deserializer); - let mut var_fingerprint = ::sse_decode(deserializer); - let mut var_pubkey = ::sse_decode(deserializer); - let mut var_blockchainDetails = ::sse_decode(deserializer); + let mut var_walletInfo = ::sse_decode(deserializer); + let mut var_blockchainInfo = ::sse_decode(deserializer); return crate::model::GetInfoResponse { - balance_sat: var_balanceSat, - pending_send_sat: var_pendingSendSat, - pending_receive_sat: var_pendingReceiveSat, - fingerprint: var_fingerprint, - pubkey: var_pubkey, - blockchain_details: var_blockchainDetails, + wallet_info: var_walletInfo, + blockchain_info: var_blockchainInfo, }; } } @@ -4394,6 +4386,24 @@ impl SseDecode for usize { } } +impl SseDecode for crate::model::WalletInfo { + // Codec=Sse (Serialization based), see doc to use other codecs + fn sse_decode(deserializer: &mut flutter_rust_bridge::for_generated::SseDeserializer) -> Self { + let mut var_balanceSat = ::sse_decode(deserializer); + let mut var_pendingSendSat = ::sse_decode(deserializer); + let mut var_pendingReceiveSat = ::sse_decode(deserializer); + let mut var_fingerprint = ::sse_decode(deserializer); + let mut var_pubkey = ::sse_decode(deserializer); + return crate::model::WalletInfo { + balance_sat: var_balanceSat, + pending_send_sat: var_pendingSendSat, + pending_receive_sat: var_pendingReceiveSat, + fingerprint: var_fingerprint, + pubkey: var_pubkey, + }; + } +} + fn pde_ffi_dispatcher_primary_impl( func_id: i32, port: flutter_rust_bridge::for_generated::MessagePort, @@ -4595,7 +4605,7 @@ impl flutter_rust_bridge::IntoIntoDart flutter_rust_bridge::for_generated::DartAbi { [ self.liquid_tip.into_into_dart().into_dart(), @@ -4604,14 +4614,11 @@ impl flutter_rust_bridge::IntoDart for crate::model::BlockchainDetails { .into_dart() } } -impl flutter_rust_bridge::for_generated::IntoDartExceptPrimitive - for crate::model::BlockchainDetails +impl flutter_rust_bridge::for_generated::IntoDartExceptPrimitive for crate::model::BlockchainInfo {} +impl flutter_rust_bridge::IntoIntoDart + for crate::model::BlockchainInfo { -} -impl flutter_rust_bridge::IntoIntoDart - for crate::model::BlockchainDetails -{ - fn into_into_dart(self) -> crate::model::BlockchainDetails { + fn into_into_dart(self) -> crate::model::BlockchainInfo { self } } @@ -4817,12 +4824,8 @@ impl flutter_rust_bridge::IntoIntoDart impl flutter_rust_bridge::IntoDart for crate::model::GetInfoResponse { fn into_dart(self) -> flutter_rust_bridge::for_generated::DartAbi { [ - self.balance_sat.into_into_dart().into_dart(), - self.pending_send_sat.into_into_dart().into_dart(), - self.pending_receive_sat.into_into_dart().into_dart(), - self.fingerprint.into_into_dart().into_dart(), - self.pubkey.into_into_dart().into_dart(), - self.blockchain_details.into_into_dart().into_dart(), + self.wallet_info.into_into_dart().into_dart(), + self.blockchain_info.into_into_dart().into_dart(), ] .into_dart() } @@ -6632,6 +6635,25 @@ impl flutter_rust_bridge::IntoIntoDart flutter_rust_bridge::for_generated::DartAbi { + [ + self.balance_sat.into_into_dart().into_dart(), + self.pending_send_sat.into_into_dart().into_dart(), + self.pending_receive_sat.into_into_dart().into_dart(), + self.fingerprint.into_into_dart().into_dart(), + self.pubkey.into_into_dart().into_dart(), + ] + .into_dart() + } +} +impl flutter_rust_bridge::for_generated::IntoDartExceptPrimitive for crate::model::WalletInfo {} +impl flutter_rust_bridge::IntoIntoDart for crate::model::WalletInfo { + fn into_into_dart(self) -> crate::model::WalletInfo { + self + } +} impl SseEncode for flutter_rust_bridge::for_generated::anyhow::Error { // Codec=Sse (Serialization based), see doc to use other codecs @@ -6767,7 +6789,7 @@ impl SseEncode for crate::bindings::BitcoinAddressData { } } -impl SseEncode for crate::model::BlockchainDetails { +impl SseEncode for crate::model::BlockchainInfo { // Codec=Sse (Serialization based), see doc to use other codecs fn sse_encode(self, serializer: &mut flutter_rust_bridge::for_generated::SseSerializer) { ::sse_encode(self.liquid_tip, serializer); @@ -6891,12 +6913,8 @@ impl SseEncode for crate::bindings::FiatCurrency { impl SseEncode for crate::model::GetInfoResponse { // Codec=Sse (Serialization based), see doc to use other codecs fn sse_encode(self, serializer: &mut flutter_rust_bridge::for_generated::SseSerializer) { - ::sse_encode(self.balance_sat, serializer); - ::sse_encode(self.pending_send_sat, serializer); - ::sse_encode(self.pending_receive_sat, serializer); - ::sse_encode(self.fingerprint, serializer); - ::sse_encode(self.pubkey, serializer); - ::sse_encode(self.blockchain_details, serializer); + ::sse_encode(self.wallet_info, serializer); + ::sse_encode(self.blockchain_info, serializer); } } @@ -8413,6 +8431,17 @@ impl SseEncode for usize { } } +impl SseEncode for crate::model::WalletInfo { + // Codec=Sse (Serialization based), see doc to use other codecs + fn sse_encode(self, serializer: &mut flutter_rust_bridge::for_generated::SseSerializer) { + ::sse_encode(self.balance_sat, serializer); + ::sse_encode(self.pending_send_sat, serializer); + ::sse_encode(self.pending_receive_sat, serializer); + ::sse_encode(self.fingerprint, serializer); + ::sse_encode(self.pubkey, serializer); + } +} + #[cfg(not(target_family = "wasm"))] mod io { // This file is automatically generated, so please do not edit it. @@ -8593,10 +8622,10 @@ mod io { } } } - impl CstDecode for wire_cst_blockchain_details { + impl CstDecode for wire_cst_blockchain_info { // Codec=Cst (C-struct based), see doc to use other codecs - fn cst_decode(self) -> crate::model::BlockchainDetails { - crate::model::BlockchainDetails { + fn cst_decode(self) -> crate::model::BlockchainInfo { + crate::model::BlockchainInfo { liquid_tip: self.liquid_tip.cst_decode(), bitcoin_tip: self.bitcoin_tip.cst_decode(), } @@ -9064,12 +9093,8 @@ mod io { // Codec=Cst (C-struct based), see doc to use other codecs fn cst_decode(self) -> crate::model::GetInfoResponse { crate::model::GetInfoResponse { - balance_sat: self.balance_sat.cst_decode(), - pending_send_sat: self.pending_send_sat.cst_decode(), - pending_receive_sat: self.pending_receive_sat.cst_decode(), - fingerprint: self.fingerprint.cst_decode(), - pubkey: self.pubkey.cst_decode(), - blockchain_details: self.blockchain_details.cst_decode(), + wallet_info: self.wallet_info.cst_decode(), + blockchain_info: self.blockchain_info.cst_decode(), } } } @@ -10366,6 +10391,18 @@ mod io { } } } + impl CstDecode for wire_cst_wallet_info { + // Codec=Cst (C-struct based), see doc to use other codecs + fn cst_decode(self) -> crate::model::WalletInfo { + crate::model::WalletInfo { + balance_sat: self.balance_sat.cst_decode(), + pending_send_sat: self.pending_send_sat.cst_decode(), + pending_receive_sat: self.pending_receive_sat.cst_decode(), + fingerprint: self.fingerprint.cst_decode(), + pubkey: self.pubkey.cst_decode(), + } + } + } impl NewWithNullPtr for wire_cst_aes_success_action_data { fn new_with_null_ptr() -> Self { Self { @@ -10459,7 +10496,7 @@ mod io { Self::new_with_null_ptr() } } - impl NewWithNullPtr for wire_cst_blockchain_details { + impl NewWithNullPtr for wire_cst_blockchain_info { fn new_with_null_ptr() -> Self { Self { liquid_tip: Default::default(), @@ -10467,7 +10504,7 @@ mod io { } } } - impl Default for wire_cst_blockchain_details { + impl Default for wire_cst_blockchain_info { fn default() -> Self { Self::new_with_null_ptr() } @@ -10596,12 +10633,8 @@ mod io { impl NewWithNullPtr for wire_cst_get_info_response { fn new_with_null_ptr() -> Self { Self { - balance_sat: Default::default(), - pending_send_sat: Default::default(), - pending_receive_sat: Default::default(), - fingerprint: core::ptr::null_mut(), - pubkey: core::ptr::null_mut(), - blockchain_details: Default::default(), + wallet_info: Default::default(), + blockchain_info: Default::default(), } } } @@ -11573,6 +11606,22 @@ mod io { Self::new_with_null_ptr() } } + impl NewWithNullPtr for wire_cst_wallet_info { + fn new_with_null_ptr() -> Self { + Self { + balance_sat: Default::default(), + pending_send_sat: Default::default(), + pending_receive_sat: Default::default(), + fingerprint: core::ptr::null_mut(), + pubkey: core::ptr::null_mut(), + } + } + } + impl Default for wire_cst_wallet_info { + fn default() -> Self { + Self::new_with_null_ptr() + } + } #[no_mangle] pub extern "C" fn frbgen_breez_liquid_wire__crate__bindings__BindingLiquidSdk_add_event_listener( @@ -12575,7 +12624,7 @@ mod io { } #[repr(C)] #[derive(Clone, Copy)] - pub struct wire_cst_blockchain_details { + pub struct wire_cst_blockchain_info { liquid_tip: u32, bitcoin_tip: u32, } @@ -12647,12 +12696,8 @@ mod io { #[repr(C)] #[derive(Clone, Copy)] pub struct wire_cst_get_info_response { - balance_sat: u64, - pending_send_sat: u64, - pending_receive_sat: u64, - fingerprint: *mut wire_cst_list_prim_u_8_strict, - pubkey: *mut wire_cst_list_prim_u_8_strict, - blockchain_details: wire_cst_blockchain_details, + wallet_info: wire_cst_wallet_info, + blockchain_info: wire_cst_blockchain_info, } #[repr(C)] #[derive(Clone, Copy)] @@ -13746,6 +13791,15 @@ mod io { url: *mut wire_cst_list_prim_u_8_strict, matches_callback_domain: bool, } + #[repr(C)] + #[derive(Clone, Copy)] + pub struct wire_cst_wallet_info { + balance_sat: u64, + pending_send_sat: u64, + pending_receive_sat: u64, + fingerprint: *mut wire_cst_list_prim_u_8_strict, + pubkey: *mut wire_cst_list_prim_u_8_strict, + } } #[cfg(not(target_family = "wasm"))] pub use io::*; diff --git a/lib/core/src/model.rs b/lib/core/src/model.rs index a653d5caf..9e46b64ca 100644 --- a/lib/core/src/model.rs +++ b/lib/core/src/model.rs @@ -530,14 +530,13 @@ pub struct RefundResponse { } #[derive(Debug, Serialize, Deserialize, Default)] -pub struct BlockchainDetails { +pub struct BlockchainInfo { pub liquid_tip: u32, pub bitcoin_tip: u32, } -/// Returned when calling [crate::sdk::LiquidSdk::get_info]. #[derive(Debug, Serialize, Deserialize)] -pub struct GetInfoResponse { +pub struct WalletInfo { /// Usable balance. This is the confirmed onchain balance minus `pending_send_sat`. pub balance_sat: u64, /// Amount that is being used for ongoing Send swaps @@ -548,9 +547,16 @@ pub struct GetInfoResponse { pub fingerprint: String, /// The wallet's pubkey. Used to verify signed messages. pub pubkey: String, +} + +/// Returned when calling [crate::sdk::LiquidSdk::get_info]. +#[derive(Debug, Serialize, Deserialize)] +pub struct GetInfoResponse { + /// The wallet information, such as the balance, fingerprint and public key + pub wallet_info: WalletInfo, + /// The latest synced blockchain information, such as the Liquid/Bitcoin tips #[serde(default)] - /// Details regarding onchain data, such as the current Liquid/Bitcoin tip - pub blockchain_details: BlockchainDetails, + pub blockchain_info: BlockchainInfo, } /// An argument when calling [crate::sdk::LiquidSdk::sign_message]. diff --git a/lib/core/src/persist/cache.rs b/lib/core/src/persist/cache.rs index 6f04e40ec..62d70a790 100644 --- a/lib/core/src/persist/cache.rs +++ b/lib/core/src/persist/cache.rs @@ -1,13 +1,14 @@ use anyhow::Result; -use rusqlite::{Transaction, TransactionBehavior}; +use rusqlite::{named_params, Transaction, TransactionBehavior}; use std::str::FromStr; use crate::model::GetInfoResponse; use crate::sync::model::{data::LAST_DERIVATION_INDEX_DATA_ID, RecordType}; -use super::Persister; +use super::{Persister, WalletInfo}; const KEY_WALLET_INFO: &str = "wallet_info"; +const KEY_BLOCKCHAIN_INFO: &str = "blockchain_info"; const KEY_SWAPPER_PROXY_URL: &str = "swapper_proxy_url"; const KEY_IS_FIRST_SYNC_COMPLETE: &str = "is_first_sync_complete"; const KEY_WEBHOOK_URL: &str = "webhook_url"; @@ -64,17 +65,81 @@ impl Persister { res } - pub fn set_wallet_info(&self, info: &GetInfoResponse) -> Result<()> { + pub fn set_wallet_info(&self, info: &WalletInfo) -> Result<()> { let serialized_info = serde_json::to_string(info)?; self.update_cached_item(KEY_WALLET_INFO, serialized_info) } - pub fn get_wallet_info(&self) -> Result> { - let info_str = self.get_cached_item(KEY_WALLET_INFO)?; - Ok(match info_str { - Some(str) => serde_json::from_str(str.as_str())?, - None => None, - }) + pub fn set_blockchain_info( + &self, + liquid_tip: Option, + bitcoin_tip: Option, + ) -> Result<()> { + let con = self.get_connection()?; + + let mut blockchain_info = serde_json::Map::new(); + if let Some(liquid_tip) = liquid_tip { + blockchain_info.insert("liquid_tip".to_string(), liquid_tip.into()); + } + if let Some(bitcoin_tip) = bitcoin_tip { + blockchain_info.insert("bitcoin_tip".to_string(), bitcoin_tip.into()); + } + + con.execute( + &format!( + " + INSERT OR REPLACE INTO cached_items (key, value) + VALUES ('{KEY_BLOCKCHAIN_INFO}', json_set( + COALESCE((SELECT value FROM cached_items WHERE key = '{KEY_BLOCKCHAIN_INFO}'), '{{}}'), + '$', + json(:value) + )) + " + ), + named_params! { + ":value": serde_json::to_string(&blockchain_info)?, + }, + )?; + + Ok(()) + } + + fn sql_row_to_info(row: &rusqlite::Row<'_>) -> Result> { + let wallet_info = match row.get::<_, Option>(0)? { + Some(info) => serde_json::from_str(&info)?, + None => return Ok(None), + }; + let blockchain_info = row + .get::<_, Option>(1)? + .map(|info| serde_json::from_str(&info)) + .and_then(|res| res.ok()) + .unwrap_or_default(); + + Ok(Some(GetInfoResponse { + wallet_info, + blockchain_info, + })) + } + + pub fn get_info(&self) -> Result> { + let con = self.get_connection()?; + + let info = con.query_row_and_then( + &format!( + " + SELECT + c1.value, -- wallet info + c2.value -- blockchain info + FROM cached_items c1 + JOIN cached_items c2 + ON c1.key = '{KEY_WALLET_INFO}' AND c2.key = '{KEY_BLOCKCHAIN_INFO}' + " + ), + [], + Self::sql_row_to_info, + )?; + + Ok(info) } pub fn set_swapper_proxy_url(&self, swapper_proxy_url: String) -> Result<()> { diff --git a/lib/core/src/sdk.rs b/lib/core/src/sdk.rs index 9c285bbcf..706504024 100644 --- a/lib/core/src/sdk.rs +++ b/lib/core/src/sdk.rs @@ -356,11 +356,11 @@ impl LiquidSdk { _ = interval.tick() => { // Get the Liquid tip and process a new block let liquid_tip_res = cloned.liquid_chain_service.lock().await.tip().await.map(|tip| tip.height); - let is_new_liquid_block = match liquid_tip_res { + let is_new_liquid_block = match &liquid_tip_res { Ok(height) => { debug!("Got Liquid tip: {height}"); - let is_new_liquid_block = height > current_liquid_block; - current_liquid_block = height; + let is_new_liquid_block = *height > current_liquid_block; + current_liquid_block = *height; is_new_liquid_block }, Err(e) => { @@ -370,11 +370,11 @@ impl LiquidSdk { }; // Get the Bitcoin tip and process a new block let bitcoin_tip_res = cloned.bitcoin_chain_service.lock().await.tip().map(|tip| tip.height as u32); - let is_new_bitcoin_block = match bitcoin_tip_res { + let is_new_bitcoin_block = match &bitcoin_tip_res { Ok(height) => { debug!("Got Bitcoin tip: {height}"); - let is_new_bitcoin_block = height > current_bitcoin_block; - current_bitcoin_block = height; + let is_new_bitcoin_block = *height > current_bitcoin_block; + current_bitcoin_block = *height; is_new_bitcoin_block }, Err(e) => { @@ -383,6 +383,10 @@ impl LiquidSdk { } }; + if let Err(e) = cloned.persister.set_blockchain_info(liquid_tip_res.ok(), bitcoin_tip_res.ok()) { + warn!("Could not persist blockchain information: {e:?}"); + } + // Only partial sync when there are no new Liquid or Bitcoin blocks let partial_sync = (is_new_liquid_block || is_new_bitcoin_block).not(); _ = cloned.sync(partial_sync).await; @@ -591,15 +595,15 @@ impl LiquidSdk { Ok(()) } - /// Get the wallet info from persistant storage + /// Get the wallet and blockchain info from local storage pub async fn get_info(&self) -> SdkResult { self.ensure_is_started().await?; - let maybe_wallet_info = self.persister.get_wallet_info()?; - match maybe_wallet_info { - Some(wallet_info) => Ok(wallet_info), + let maybe_info = self.persister.get_info()?; + match maybe_info { + Some(info) => Ok(info), None => { self.update_wallet_info().await?; - self.persister.get_wallet_info()?.ok_or(SdkError::Generic { + self.persister.get_info()?.ok_or(SdkError::Generic { err: "Info not found".into(), }) } @@ -901,8 +905,8 @@ impl LiquidSdk { (receiver_amount_sat, fees_sat) = match amount { PayAmount::Drain => { ensure_sdk!( - get_info_res.pending_receive_sat == 0 - && get_info_res.pending_send_sat == 0, + get_info_res.wallet_info.pending_receive_sat == 0 + && get_info_res.wallet_info.pending_send_sat == 0, PaymentError::Generic { err: "Cannot drain while there are pending payments".to_string(), } @@ -910,7 +914,8 @@ impl LiquidSdk { let drain_fees_sat = self .estimate_drain_tx_fee(None, Some(&liquid_address_data.address)) .await?; - let drain_amount_sat = get_info_res.balance_sat - drain_fees_sat; + let drain_amount_sat = + get_info_res.wallet_info.balance_sat - drain_fees_sat; info!("Drain amount: {drain_amount_sat} sat"); (drain_amount_sat, drain_fees_sat) } @@ -1007,7 +1012,7 @@ impl LiquidSdk { let payer_amount_sat = receiver_amount_sat + fees_sat; ensure_sdk!( - payer_amount_sat <= get_info_res.balance_sat, + payer_amount_sat <= get_info_res.wallet_info.balance_sat, PaymentError::InsufficientFunds ); @@ -1071,7 +1076,7 @@ impl LiquidSdk { let payer_amount_sat = amount_sat + fees_sat; ensure_sdk!( - payer_amount_sat <= self.get_info().await?.balance_sat, + payer_amount_sat <= self.get_info().await?.wallet_info.balance_sat, PaymentError::InsufficientFunds ); @@ -1105,7 +1110,7 @@ impl LiquidSdk { let amount_sat = get_invoice_amount!(invoice); let payer_amount_sat = amount_sat + fees_sat; ensure_sdk!( - payer_amount_sat <= self.get_info().await?.balance_sat, + payer_amount_sat <= self.get_info().await?.wallet_info.balance_sat, PaymentError::InsufficientFunds ); @@ -1162,7 +1167,7 @@ impl LiquidSdk { let receiver_amount_sat = invoice.amount_msats() / 1_000; let payer_amount_sat = receiver_amount_sat + fees_sat; ensure_sdk!( - payer_amount_sat <= self.get_info().await?.balance_sat, + payer_amount_sat <= self.get_info().await?.wallet_info.balance_sat, PaymentError::InsufficientFunds ); @@ -1473,12 +1478,13 @@ impl LiquidSdk { } PayAmount::Drain => { ensure_sdk!( - get_info_res.pending_receive_sat == 0 && get_info_res.pending_send_sat == 0, + get_info_res.wallet_info.pending_receive_sat == 0 + && get_info_res.wallet_info.pending_send_sat == 0, PaymentError::Generic { err: "Cannot drain while there are pending payments".to_string(), } ); - let payer_amount_sat = get_info_res.balance_sat; + let payer_amount_sat = get_info_res.wallet_info.balance_sat; let lockup_fees_sat = self.estimate_drain_tx_fee(None, None).await?; let user_lockup_amount_sat = payer_amount_sat - lockup_fees_sat; @@ -1500,7 +1506,7 @@ impl LiquidSdk { }; ensure_sdk!( - payer_amount_sat <= get_info_res.balance_sat, + payer_amount_sat <= get_info_res.wallet_info.balance_sat, PaymentError::InsufficientFunds ); @@ -1532,7 +1538,7 @@ impl LiquidSdk { info!("Paying onchain, request = {req:?}"); let claim_address = self.validate_bitcoin_address(&req.address).await?; - let balance_sat = self.get_info().await?.balance_sat; + let balance_sat = self.get_info().await?.wallet_info.balance_sat; let receiver_amount_sat = req.prepare_response.receiver_amount_sat; let pair = self.get_chain_pair(Direction::Outgoing)?; let claim_fees_sat = req.prepare_response.claim_fees_sat; @@ -2493,19 +2499,12 @@ impl LiquidSdk { } } - let liquid_tip = self.liquid_chain_service.lock().await.tip().await?.height; - let bitcoin_tip = self.bitcoin_chain_service.lock().await.tip()?.height as u32; - - let info_response = GetInfoResponse { + let info_response = WalletInfo { balance_sat: wallet_amount_sat as u64, pending_send_sat, pending_receive_sat, fingerprint: self.onchain_wallet.fingerprint()?, pubkey: self.onchain_wallet.pubkey()?, - blockchain_details: BlockchainDetails { - liquid_tip, - bitcoin_tip, - }, }; self.persister.set_wallet_info(&info_response) } diff --git a/packages/dart/lib/src/frb_generated.dart b/packages/dart/lib/src/frb_generated.dart index 6ff9d191c..53663318b 100644 --- a/packages/dart/lib/src/frb_generated.dart +++ b/packages/dart/lib/src/frb_generated.dart @@ -1345,11 +1345,11 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { } @protected - BlockchainDetails dco_decode_blockchain_details(dynamic raw) { + BlockchainInfo dco_decode_blockchain_info(dynamic raw) { // Codec=Dco (DartCObject based), see doc to use other codecs final arr = raw as List; if (arr.length != 2) throw Exception('unexpected arr length: expect 2 but see ${arr.length}'); - return BlockchainDetails( + return BlockchainInfo( liquidTip: dco_decode_u_32(arr[0]), bitcoinTip: dco_decode_u_32(arr[1]), ); @@ -1788,14 +1788,10 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { GetInfoResponse dco_decode_get_info_response(dynamic raw) { // Codec=Dco (DartCObject based), see doc to use other codecs final arr = raw as List; - if (arr.length != 6) throw Exception('unexpected arr length: expect 6 but see ${arr.length}'); + if (arr.length != 2) throw Exception('unexpected arr length: expect 2 but see ${arr.length}'); return GetInfoResponse( - balanceSat: dco_decode_u_64(arr[0]), - pendingSendSat: dco_decode_u_64(arr[1]), - pendingReceiveSat: dco_decode_u_64(arr[2]), - fingerprint: dco_decode_String(arr[3]), - pubkey: dco_decode_String(arr[4]), - blockchainDetails: dco_decode_blockchain_details(arr[5]), + walletInfo: dco_decode_wallet_info(arr[0]), + blockchainInfo: dco_decode_blockchain_info(arr[1]), ); } @@ -3189,6 +3185,20 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { return dcoDecodeU64(raw); } + @protected + WalletInfo dco_decode_wallet_info(dynamic raw) { + // Codec=Dco (DartCObject based), see doc to use other codecs + final arr = raw as List; + if (arr.length != 5) throw Exception('unexpected arr length: expect 5 but see ${arr.length}'); + return WalletInfo( + balanceSat: dco_decode_u_64(arr[0]), + pendingSendSat: dco_decode_u_64(arr[1]), + pendingReceiveSat: dco_decode_u_64(arr[2]), + fingerprint: dco_decode_String(arr[3]), + pubkey: dco_decode_String(arr[4]), + ); + } + @protected AnyhowException sse_decode_AnyhowException(SseDeserializer deserializer) { // Codec=Sse (Serialization based), see doc to use other codecs @@ -3324,11 +3334,11 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { } @protected - BlockchainDetails sse_decode_blockchain_details(SseDeserializer deserializer) { + BlockchainInfo sse_decode_blockchain_info(SseDeserializer deserializer) { // Codec=Sse (Serialization based), see doc to use other codecs var var_liquidTip = sse_decode_u_32(deserializer); var var_bitcoinTip = sse_decode_u_32(deserializer); - return BlockchainDetails(liquidTip: var_liquidTip, bitcoinTip: var_bitcoinTip); + return BlockchainInfo(liquidTip: var_liquidTip, bitcoinTip: var_bitcoinTip); } @protected @@ -3763,19 +3773,9 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { @protected GetInfoResponse sse_decode_get_info_response(SseDeserializer deserializer) { // Codec=Sse (Serialization based), see doc to use other codecs - var var_balanceSat = sse_decode_u_64(deserializer); - var var_pendingSendSat = sse_decode_u_64(deserializer); - var var_pendingReceiveSat = sse_decode_u_64(deserializer); - var var_fingerprint = sse_decode_String(deserializer); - var var_pubkey = sse_decode_String(deserializer); - var var_blockchainDetails = sse_decode_blockchain_details(deserializer); - return GetInfoResponse( - balanceSat: var_balanceSat, - pendingSendSat: var_pendingSendSat, - pendingReceiveSat: var_pendingReceiveSat, - fingerprint: var_fingerprint, - pubkey: var_pubkey, - blockchainDetails: var_blockchainDetails); + var var_walletInfo = sse_decode_wallet_info(deserializer); + var var_blockchainInfo = sse_decode_blockchain_info(deserializer); + return GetInfoResponse(walletInfo: var_walletInfo, blockchainInfo: var_blockchainInfo); } @protected @@ -5262,6 +5262,22 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { return deserializer.buffer.getBigUint64(); } + @protected + WalletInfo sse_decode_wallet_info(SseDeserializer deserializer) { + // Codec=Sse (Serialization based), see doc to use other codecs + var var_balanceSat = sse_decode_u_64(deserializer); + var var_pendingSendSat = sse_decode_u_64(deserializer); + var var_pendingReceiveSat = sse_decode_u_64(deserializer); + var var_fingerprint = sse_decode_String(deserializer); + var var_pubkey = sse_decode_String(deserializer); + return WalletInfo( + balanceSat: var_balanceSat, + pendingSendSat: var_pendingSendSat, + pendingReceiveSat: var_pendingReceiveSat, + fingerprint: var_fingerprint, + pubkey: var_pubkey); + } + @protected int cst_encode_Auto_Owned_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerBindingLiquidSdk( BindingLiquidSdk raw) { @@ -5491,7 +5507,7 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { } @protected - void sse_encode_blockchain_details(BlockchainDetails self, SseSerializer serializer) { + void sse_encode_blockchain_info(BlockchainInfo self, SseSerializer serializer) { // Codec=Sse (Serialization based), see doc to use other codecs sse_encode_u_32(self.liquidTip, serializer); sse_encode_u_32(self.bitcoinTip, serializer); @@ -5906,12 +5922,8 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { @protected void sse_encode_get_info_response(GetInfoResponse self, SseSerializer serializer) { // Codec=Sse (Serialization based), see doc to use other codecs - sse_encode_u_64(self.balanceSat, serializer); - sse_encode_u_64(self.pendingSendSat, serializer); - sse_encode_u_64(self.pendingReceiveSat, serializer); - sse_encode_String(self.fingerprint, serializer); - sse_encode_String(self.pubkey, serializer); - sse_encode_blockchain_details(self.blockchainDetails, serializer); + sse_encode_wallet_info(self.walletInfo, serializer); + sse_encode_blockchain_info(self.blockchainInfo, serializer); } @protected @@ -7152,6 +7164,16 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi { // Codec=Sse (Serialization based), see doc to use other codecs serializer.buffer.putBigUint64(self); } + + @protected + void sse_encode_wallet_info(WalletInfo self, SseSerializer serializer) { + // Codec=Sse (Serialization based), see doc to use other codecs + sse_encode_u_64(self.balanceSat, serializer); + sse_encode_u_64(self.pendingSendSat, serializer); + sse_encode_u_64(self.pendingReceiveSat, serializer); + sse_encode_String(self.fingerprint, serializer); + sse_encode_String(self.pubkey, serializer); + } } @sealed diff --git a/packages/dart/lib/src/frb_generated.io.dart b/packages/dart/lib/src/frb_generated.io.dart index d3191cc78..39bd27a36 100644 --- a/packages/dart/lib/src/frb_generated.io.dart +++ b/packages/dart/lib/src/frb_generated.io.dart @@ -72,7 +72,7 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl { BitcoinAddressData dco_decode_bitcoin_address_data(dynamic raw); @protected - BlockchainDetails dco_decode_blockchain_details(dynamic raw); + BlockchainInfo dco_decode_blockchain_info(dynamic raw); @protected bool dco_decode_bool(dynamic raw); @@ -602,6 +602,9 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl { @protected BigInt dco_decode_usize(dynamic raw); + @protected + WalletInfo dco_decode_wallet_info(dynamic raw); + @protected AnyhowException sse_decode_AnyhowException(SseDeserializer deserializer); @@ -650,7 +653,7 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl { BitcoinAddressData sse_decode_bitcoin_address_data(SseDeserializer deserializer); @protected - BlockchainDetails sse_decode_blockchain_details(SseDeserializer deserializer); + BlockchainInfo sse_decode_blockchain_info(SseDeserializer deserializer); @protected bool sse_decode_bool(SseDeserializer deserializer); @@ -1182,6 +1185,9 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl { @protected BigInt sse_decode_usize(SseDeserializer deserializer); + @protected + WalletInfo sse_decode_wallet_info(SseDeserializer deserializer); + @protected ffi.Pointer cst_encode_AnyhowException(AnyhowException raw) { // Codec=Cst (C-struct based), see doc to use other codecs @@ -1980,8 +1986,7 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl { } @protected - void cst_api_fill_to_wire_blockchain_details( - BlockchainDetails apiObj, wire_cst_blockchain_details wireObj) { + void cst_api_fill_to_wire_blockchain_info(BlockchainInfo apiObj, wire_cst_blockchain_info wireObj) { wireObj.liquid_tip = cst_encode_u_32(apiObj.liquidTip); wireObj.bitcoin_tip = cst_encode_u_32(apiObj.bitcoinTip); } @@ -2328,12 +2333,8 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl { @protected void cst_api_fill_to_wire_get_info_response(GetInfoResponse apiObj, wire_cst_get_info_response wireObj) { - wireObj.balance_sat = cst_encode_u_64(apiObj.balanceSat); - wireObj.pending_send_sat = cst_encode_u_64(apiObj.pendingSendSat); - wireObj.pending_receive_sat = cst_encode_u_64(apiObj.pendingReceiveSat); - wireObj.fingerprint = cst_encode_String(apiObj.fingerprint); - wireObj.pubkey = cst_encode_String(apiObj.pubkey); - cst_api_fill_to_wire_blockchain_details(apiObj.blockchainDetails, wireObj.blockchain_details); + cst_api_fill_to_wire_wallet_info(apiObj.walletInfo, wireObj.wallet_info); + cst_api_fill_to_wire_blockchain_info(apiObj.blockchainInfo, wireObj.blockchain_info); } @protected @@ -3353,6 +3354,15 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl { wireObj.matches_callback_domain = cst_encode_bool(apiObj.matchesCallbackDomain); } + @protected + void cst_api_fill_to_wire_wallet_info(WalletInfo apiObj, wire_cst_wallet_info wireObj) { + wireObj.balance_sat = cst_encode_u_64(apiObj.balanceSat); + wireObj.pending_send_sat = cst_encode_u_64(apiObj.pendingSendSat); + wireObj.pending_receive_sat = cst_encode_u_64(apiObj.pendingReceiveSat); + wireObj.fingerprint = cst_encode_String(apiObj.fingerprint); + wireObj.pubkey = cst_encode_String(apiObj.pubkey); + } + @protected int cst_encode_Auto_Owned_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerBindingLiquidSdk( BindingLiquidSdk raw); @@ -3451,7 +3461,7 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl { void sse_encode_bitcoin_address_data(BitcoinAddressData self, SseSerializer serializer); @protected - void sse_encode_blockchain_details(BlockchainDetails self, SseSerializer serializer); + void sse_encode_blockchain_info(BlockchainInfo self, SseSerializer serializer); @protected void sse_encode_bool(bool self, SseSerializer serializer); @@ -3991,6 +4001,9 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl { @protected void sse_encode_usize(BigInt self, SseSerializer serializer); + + @protected + void sse_encode_wallet_info(WalletInfo self, SseSerializer serializer); } // Section: wire_class @@ -6464,7 +6477,7 @@ final class wire_cst_list_refundable_swap extends ffi.Struct { external int len; } -final class wire_cst_blockchain_details extends ffi.Struct { +final class wire_cst_blockchain_info extends ffi.Struct { @ffi.Uint32() external int liquid_tip; @@ -6477,7 +6490,7 @@ final class wire_cst_check_message_response extends ffi.Struct { external bool is_valid; } -final class wire_cst_get_info_response extends ffi.Struct { +final class wire_cst_wallet_info extends ffi.Struct { @ffi.Uint64() external int balance_sat; @@ -6490,8 +6503,12 @@ final class wire_cst_get_info_response extends ffi.Struct { external ffi.Pointer fingerprint; external ffi.Pointer pubkey; +} + +final class wire_cst_get_info_response extends ffi.Struct { + external wire_cst_wallet_info wallet_info; - external wire_cst_blockchain_details blockchain_details; + external wire_cst_blockchain_info blockchain_info; } final class wire_cst_InputType_BitcoinAddress extends ffi.Struct { diff --git a/packages/dart/lib/src/model.dart b/packages/dart/lib/src/model.dart index 6cbbe8f96..28a2cb186 100644 --- a/packages/dart/lib/src/model.dart +++ b/packages/dart/lib/src/model.dart @@ -30,11 +30,11 @@ class BackupRequest { other is BackupRequest && runtimeType == other.runtimeType && backupPath == other.backupPath; } -class BlockchainDetails { +class BlockchainInfo { final int liquidTip; final int bitcoinTip; - const BlockchainDetails({ + const BlockchainInfo({ required this.liquidTip, required this.bitcoinTip, }); @@ -45,7 +45,7 @@ class BlockchainDetails { @override bool operator ==(Object other) => identical(this, other) || - other is BlockchainDetails && + other is BlockchainInfo && runtimeType == other.runtimeType && liquidTip == other.liquidTip && bitcoinTip == other.bitcoinTip; @@ -251,53 +251,27 @@ class ConnectRequest { /// Returned when calling [crate::sdk::LiquidSdk::get_info]. class GetInfoResponse { - /// Usable balance. This is the confirmed onchain balance minus `pending_send_sat`. - final BigInt balanceSat; + /// The wallet information, such as the balance, fingerprint and public key + final WalletInfo walletInfo; - /// Amount that is being used for ongoing Send swaps - final BigInt pendingSendSat; - - /// Incoming amount that is pending from ongoing Receive swaps - final BigInt pendingReceiveSat; - - /// The wallet's fingerprint. It is used to build the working directory in [Config::get_wallet_dir]. - final String fingerprint; - - /// The wallet's pubkey. Used to verify signed messages. - final String pubkey; - - /// Details regarding onchain data, such as the current Liquid/Bitcoin tip - final BlockchainDetails blockchainDetails; + /// The latest synced blockchain information, such as the Liquid/Bitcoin tips + final BlockchainInfo blockchainInfo; const GetInfoResponse({ - required this.balanceSat, - required this.pendingSendSat, - required this.pendingReceiveSat, - required this.fingerprint, - required this.pubkey, - required this.blockchainDetails, + required this.walletInfo, + required this.blockchainInfo, }); @override - int get hashCode => - balanceSat.hashCode ^ - pendingSendSat.hashCode ^ - pendingReceiveSat.hashCode ^ - fingerprint.hashCode ^ - pubkey.hashCode ^ - blockchainDetails.hashCode; + int get hashCode => walletInfo.hashCode ^ blockchainInfo.hashCode; @override bool operator ==(Object other) => identical(this, other) || other is GetInfoResponse && runtimeType == other.runtimeType && - balanceSat == other.balanceSat && - pendingSendSat == other.pendingSendSat && - pendingReceiveSat == other.pendingReceiveSat && - fingerprint == other.fingerprint && - pubkey == other.pubkey && - blockchainDetails == other.blockchainDetails; + walletInfo == other.walletInfo && + blockchainInfo == other.blockchainInfo; } @freezed @@ -1504,3 +1478,47 @@ class SignMessageResponse { identical(this, other) || other is SignMessageResponse && runtimeType == other.runtimeType && signature == other.signature; } + +class WalletInfo { + /// Usable balance. This is the confirmed onchain balance minus `pending_send_sat`. + final BigInt balanceSat; + + /// Amount that is being used for ongoing Send swaps + final BigInt pendingSendSat; + + /// Incoming amount that is pending from ongoing Receive swaps + final BigInt pendingReceiveSat; + + /// The wallet's fingerprint. It is used to build the working directory in [Config::get_wallet_dir]. + final String fingerprint; + + /// The wallet's pubkey. Used to verify signed messages. + final String pubkey; + + const WalletInfo({ + required this.balanceSat, + required this.pendingSendSat, + required this.pendingReceiveSat, + required this.fingerprint, + required this.pubkey, + }); + + @override + int get hashCode => + balanceSat.hashCode ^ + pendingSendSat.hashCode ^ + pendingReceiveSat.hashCode ^ + fingerprint.hashCode ^ + pubkey.hashCode; + + @override + bool operator ==(Object other) => + identical(this, other) || + other is WalletInfo && + runtimeType == other.runtimeType && + balanceSat == other.balanceSat && + pendingSendSat == other.pendingSendSat && + pendingReceiveSat == other.pendingReceiveSat && + fingerprint == other.fingerprint && + pubkey == other.pubkey; +} diff --git a/packages/flutter/lib/flutter_breez_liquid_bindings_generated.dart b/packages/flutter/lib/flutter_breez_liquid_bindings_generated.dart index ff6c7f14e..ff1706345 100644 --- a/packages/flutter/lib/flutter_breez_liquid_bindings_generated.dart +++ b/packages/flutter/lib/flutter_breez_liquid_bindings_generated.dart @@ -4860,7 +4860,7 @@ final class wire_cst_list_refundable_swap extends ffi.Struct { external int len; } -final class wire_cst_blockchain_details extends ffi.Struct { +final class wire_cst_blockchain_info extends ffi.Struct { @ffi.Uint32() external int liquid_tip; @@ -4873,7 +4873,7 @@ final class wire_cst_check_message_response extends ffi.Struct { external bool is_valid; } -final class wire_cst_get_info_response extends ffi.Struct { +final class wire_cst_wallet_info extends ffi.Struct { @ffi.Uint64() external int balance_sat; @@ -4886,8 +4886,12 @@ final class wire_cst_get_info_response extends ffi.Struct { external ffi.Pointer fingerprint; external ffi.Pointer pubkey; +} + +final class wire_cst_get_info_response extends ffi.Struct { + external wire_cst_wallet_info wallet_info; - external wire_cst_blockchain_details blockchain_details; + external wire_cst_blockchain_info blockchain_info; } final class wire_cst_InputType_BitcoinAddress extends ffi.Struct { diff --git a/packages/react-native/android/src/main/java/com/breezsdkliquid/BreezSDKLiquidMapper.kt b/packages/react-native/android/src/main/java/com/breezsdkliquid/BreezSDKLiquidMapper.kt index eb2ce9f2b..253fd9c60 100644 --- a/packages/react-native/android/src/main/java/com/breezsdkliquid/BreezSDKLiquidMapper.kt +++ b/packages/react-native/android/src/main/java/com/breezsdkliquid/BreezSDKLiquidMapper.kt @@ -139,9 +139,9 @@ fun asBitcoinAddressDataList(arr: ReadableArray): List { return list } -fun asBlockchainDetails(blockchainDetails: ReadableMap): BlockchainDetails? { +fun asBlockchainInfo(blockchainInfo: ReadableMap): BlockchainInfo? { if (!validateMandatoryFields( - blockchainDetails, + blockchainInfo, arrayOf( "liquidTip", "bitcoinTip", @@ -150,22 +150,22 @@ fun asBlockchainDetails(blockchainDetails: ReadableMap): BlockchainDetails? { ) { return null } - val liquidTip = blockchainDetails.getInt("liquidTip").toUInt() - val bitcoinTip = blockchainDetails.getInt("bitcoinTip").toUInt() - return BlockchainDetails(liquidTip, bitcoinTip) + val liquidTip = blockchainInfo.getInt("liquidTip").toUInt() + val bitcoinTip = blockchainInfo.getInt("bitcoinTip").toUInt() + return BlockchainInfo(liquidTip, bitcoinTip) } -fun readableMapOf(blockchainDetails: BlockchainDetails): ReadableMap = +fun readableMapOf(blockchainInfo: BlockchainInfo): ReadableMap = readableMapOf( - "liquidTip" to blockchainDetails.liquidTip, - "bitcoinTip" to blockchainDetails.bitcoinTip, + "liquidTip" to blockchainInfo.liquidTip, + "bitcoinTip" to blockchainInfo.bitcoinTip, ) -fun asBlockchainDetailsList(arr: ReadableArray): List { - val list = ArrayList() +fun asBlockchainInfoList(arr: ReadableArray): List { + val list = ArrayList() for (value in arr.toList()) { when (value) { - is ReadableMap -> list.add(asBlockchainDetails(value)!!) + is ReadableMap -> list.add(asBlockchainInfo(value)!!) else -> throw SdkException.Generic(errUnexpectedType(value)) } } @@ -543,34 +543,22 @@ fun asGetInfoResponse(getInfoResponse: ReadableMap): GetInfoResponse? { if (!validateMandatoryFields( getInfoResponse, arrayOf( - "balanceSat", - "pendingSendSat", - "pendingReceiveSat", - "fingerprint", - "pubkey", - "blockchainDetails", + "walletInfo", + "blockchainInfo", ), ) ) { return null } - val balanceSat = getInfoResponse.getDouble("balanceSat").toULong() - val pendingSendSat = getInfoResponse.getDouble("pendingSendSat").toULong() - val pendingReceiveSat = getInfoResponse.getDouble("pendingReceiveSat").toULong() - val fingerprint = getInfoResponse.getString("fingerprint")!! - val pubkey = getInfoResponse.getString("pubkey")!! - val blockchainDetails = getInfoResponse.getMap("blockchainDetails")?.let { asBlockchainDetails(it) }!! - return GetInfoResponse(balanceSat, pendingSendSat, pendingReceiveSat, fingerprint, pubkey, blockchainDetails) + val walletInfo = getInfoResponse.getMap("walletInfo")?.let { asWalletInfo(it) }!! + val blockchainInfo = getInfoResponse.getMap("blockchainInfo")?.let { asBlockchainInfo(it) }!! + return GetInfoResponse(walletInfo, blockchainInfo) } fun readableMapOf(getInfoResponse: GetInfoResponse): ReadableMap = readableMapOf( - "balanceSat" to getInfoResponse.balanceSat, - "pendingSendSat" to getInfoResponse.pendingSendSat, - "pendingReceiveSat" to getInfoResponse.pendingReceiveSat, - "fingerprint" to getInfoResponse.fingerprint, - "pubkey" to getInfoResponse.pubkey, - "blockchainDetails" to readableMapOf(getInfoResponse.blockchainDetails), + "walletInfo" to readableMapOf(getInfoResponse.walletInfo), + "blockchainInfo" to readableMapOf(getInfoResponse.blockchainInfo), ) fun asGetInfoResponseList(arr: ReadableArray): List { @@ -2620,6 +2608,48 @@ fun asUrlSuccessActionDataList(arr: ReadableArray): List { return list } +fun asWalletInfo(walletInfo: ReadableMap): WalletInfo? { + if (!validateMandatoryFields( + walletInfo, + arrayOf( + "balanceSat", + "pendingSendSat", + "pendingReceiveSat", + "fingerprint", + "pubkey", + ), + ) + ) { + return null + } + val balanceSat = walletInfo.getDouble("balanceSat").toULong() + val pendingSendSat = walletInfo.getDouble("pendingSendSat").toULong() + val pendingReceiveSat = walletInfo.getDouble("pendingReceiveSat").toULong() + val fingerprint = walletInfo.getString("fingerprint")!! + val pubkey = walletInfo.getString("pubkey")!! + return WalletInfo(balanceSat, pendingSendSat, pendingReceiveSat, fingerprint, pubkey) +} + +fun readableMapOf(walletInfo: WalletInfo): ReadableMap = + readableMapOf( + "balanceSat" to walletInfo.balanceSat, + "pendingSendSat" to walletInfo.pendingSendSat, + "pendingReceiveSat" to walletInfo.pendingReceiveSat, + "fingerprint" to walletInfo.fingerprint, + "pubkey" to walletInfo.pubkey, + ) + +fun asWalletInfoList(arr: ReadableArray): List { + val list = ArrayList() + for (value in arr.toList()) { + when (value) { + is ReadableMap -> list.add(asWalletInfo(value)!!) + else -> throw SdkException.Generic(errUnexpectedType(value)) + } + } + return list +} + fun asAesSuccessActionDataResult(aesSuccessActionDataResult: ReadableMap): AesSuccessActionDataResult? { val type = aesSuccessActionDataResult.getString("type") diff --git a/packages/react-native/ios/BreezSDKLiquidMapper.swift b/packages/react-native/ios/BreezSDKLiquidMapper.swift index a20e169ba..700e69ef2 100644 --- a/packages/react-native/ios/BreezSDKLiquidMapper.swift +++ b/packages/react-native/ios/BreezSDKLiquidMapper.swift @@ -172,39 +172,39 @@ enum BreezSDKLiquidMapper { return bitcoinAddressDataList.map { v -> [String: Any?] in return dictionaryOf(bitcoinAddressData: v) } } - static func asBlockchainDetails(blockchainDetails: [String: Any?]) throws -> BlockchainDetails { - guard let liquidTip = blockchainDetails["liquidTip"] as? UInt32 else { - throw SdkError.Generic(message: errMissingMandatoryField(fieldName: "liquidTip", typeName: "BlockchainDetails")) + static func asBlockchainInfo(blockchainInfo: [String: Any?]) throws -> BlockchainInfo { + guard let liquidTip = blockchainInfo["liquidTip"] as? UInt32 else { + throw SdkError.Generic(message: errMissingMandatoryField(fieldName: "liquidTip", typeName: "BlockchainInfo")) } - guard let bitcoinTip = blockchainDetails["bitcoinTip"] as? UInt32 else { - throw SdkError.Generic(message: errMissingMandatoryField(fieldName: "bitcoinTip", typeName: "BlockchainDetails")) + guard let bitcoinTip = blockchainInfo["bitcoinTip"] as? UInt32 else { + throw SdkError.Generic(message: errMissingMandatoryField(fieldName: "bitcoinTip", typeName: "BlockchainInfo")) } - return BlockchainDetails(liquidTip: liquidTip, bitcoinTip: bitcoinTip) + return BlockchainInfo(liquidTip: liquidTip, bitcoinTip: bitcoinTip) } - static func dictionaryOf(blockchainDetails: BlockchainDetails) -> [String: Any?] { + static func dictionaryOf(blockchainInfo: BlockchainInfo) -> [String: Any?] { return [ - "liquidTip": blockchainDetails.liquidTip, - "bitcoinTip": blockchainDetails.bitcoinTip, + "liquidTip": blockchainInfo.liquidTip, + "bitcoinTip": blockchainInfo.bitcoinTip, ] } - static func asBlockchainDetailsList(arr: [Any]) throws -> [BlockchainDetails] { - var list = [BlockchainDetails]() + static func asBlockchainInfoList(arr: [Any]) throws -> [BlockchainInfo] { + var list = [BlockchainInfo]() for value in arr { if let val = value as? [String: Any?] { - var blockchainDetails = try asBlockchainDetails(blockchainDetails: val) - list.append(blockchainDetails) + var blockchainInfo = try asBlockchainInfo(blockchainInfo: val) + list.append(blockchainInfo) } else { - throw SdkError.Generic(message: errUnexpectedType(typeName: "BlockchainDetails")) + throw SdkError.Generic(message: errUnexpectedType(typeName: "BlockchainInfo")) } } return list } - static func arrayOf(blockchainDetailsList: [BlockchainDetails]) -> [Any] { - return blockchainDetailsList.map { v -> [String: Any?] in return dictionaryOf(blockchainDetails: v) } + static func arrayOf(blockchainInfoList: [BlockchainInfo]) -> [Any] { + return blockchainInfoList.map { v -> [String: Any?] in return dictionaryOf(blockchainInfo: v) } } static func asBuyBitcoinRequest(buyBitcoinRequest: [String: Any?]) throws -> BuyBitcoinRequest { @@ -623,37 +623,23 @@ enum BreezSDKLiquidMapper { } static func asGetInfoResponse(getInfoResponse: [String: Any?]) throws -> GetInfoResponse { - guard let balanceSat = getInfoResponse["balanceSat"] as? UInt64 else { - throw SdkError.Generic(message: errMissingMandatoryField(fieldName: "balanceSat", typeName: "GetInfoResponse")) + guard let walletInfoTmp = getInfoResponse["walletInfo"] as? [String: Any?] else { + throw SdkError.Generic(message: errMissingMandatoryField(fieldName: "walletInfo", typeName: "GetInfoResponse")) } - guard let pendingSendSat = getInfoResponse["pendingSendSat"] as? UInt64 else { - throw SdkError.Generic(message: errMissingMandatoryField(fieldName: "pendingSendSat", typeName: "GetInfoResponse")) - } - guard let pendingReceiveSat = getInfoResponse["pendingReceiveSat"] as? UInt64 else { - throw SdkError.Generic(message: errMissingMandatoryField(fieldName: "pendingReceiveSat", typeName: "GetInfoResponse")) - } - guard let fingerprint = getInfoResponse["fingerprint"] as? String else { - throw SdkError.Generic(message: errMissingMandatoryField(fieldName: "fingerprint", typeName: "GetInfoResponse")) - } - guard let pubkey = getInfoResponse["pubkey"] as? String else { - throw SdkError.Generic(message: errMissingMandatoryField(fieldName: "pubkey", typeName: "GetInfoResponse")) - } - guard let blockchainDetailsTmp = getInfoResponse["blockchainDetails"] as? [String: Any?] else { - throw SdkError.Generic(message: errMissingMandatoryField(fieldName: "blockchainDetails", typeName: "GetInfoResponse")) + let walletInfo = try asWalletInfo(walletInfo: walletInfoTmp) + + guard let blockchainInfoTmp = getInfoResponse["blockchainInfo"] as? [String: Any?] else { + throw SdkError.Generic(message: errMissingMandatoryField(fieldName: "blockchainInfo", typeName: "GetInfoResponse")) } - let blockchainDetails = try asBlockchainDetails(blockchainDetails: blockchainDetailsTmp) + let blockchainInfo = try asBlockchainInfo(blockchainInfo: blockchainInfoTmp) - return GetInfoResponse(balanceSat: balanceSat, pendingSendSat: pendingSendSat, pendingReceiveSat: pendingReceiveSat, fingerprint: fingerprint, pubkey: pubkey, blockchainDetails: blockchainDetails) + return GetInfoResponse(walletInfo: walletInfo, blockchainInfo: blockchainInfo) } static func dictionaryOf(getInfoResponse: GetInfoResponse) -> [String: Any?] { return [ - "balanceSat": getInfoResponse.balanceSat, - "pendingSendSat": getInfoResponse.pendingSendSat, - "pendingReceiveSat": getInfoResponse.pendingReceiveSat, - "fingerprint": getInfoResponse.fingerprint, - "pubkey": getInfoResponse.pubkey, - "blockchainDetails": dictionaryOf(blockchainDetails: getInfoResponse.blockchainDetails), + "walletInfo": dictionaryOf(walletInfo: getInfoResponse.walletInfo), + "blockchainInfo": dictionaryOf(blockchainInfo: getInfoResponse.blockchainInfo), ] } @@ -3011,6 +2997,53 @@ enum BreezSDKLiquidMapper { return urlSuccessActionDataList.map { v -> [String: Any?] in return dictionaryOf(urlSuccessActionData: v) } } + static func asWalletInfo(walletInfo: [String: Any?]) throws -> WalletInfo { + guard let balanceSat = walletInfo["balanceSat"] as? UInt64 else { + throw SdkError.Generic(message: errMissingMandatoryField(fieldName: "balanceSat", typeName: "WalletInfo")) + } + guard let pendingSendSat = walletInfo["pendingSendSat"] as? UInt64 else { + throw SdkError.Generic(message: errMissingMandatoryField(fieldName: "pendingSendSat", typeName: "WalletInfo")) + } + guard let pendingReceiveSat = walletInfo["pendingReceiveSat"] as? UInt64 else { + throw SdkError.Generic(message: errMissingMandatoryField(fieldName: "pendingReceiveSat", typeName: "WalletInfo")) + } + guard let fingerprint = walletInfo["fingerprint"] as? String else { + throw SdkError.Generic(message: errMissingMandatoryField(fieldName: "fingerprint", typeName: "WalletInfo")) + } + guard let pubkey = walletInfo["pubkey"] as? String else { + throw SdkError.Generic(message: errMissingMandatoryField(fieldName: "pubkey", typeName: "WalletInfo")) + } + + return WalletInfo(balanceSat: balanceSat, pendingSendSat: pendingSendSat, pendingReceiveSat: pendingReceiveSat, fingerprint: fingerprint, pubkey: pubkey) + } + + static func dictionaryOf(walletInfo: WalletInfo) -> [String: Any?] { + return [ + "balanceSat": walletInfo.balanceSat, + "pendingSendSat": walletInfo.pendingSendSat, + "pendingReceiveSat": walletInfo.pendingReceiveSat, + "fingerprint": walletInfo.fingerprint, + "pubkey": walletInfo.pubkey, + ] + } + + static func asWalletInfoList(arr: [Any]) throws -> [WalletInfo] { + var list = [WalletInfo]() + for value in arr { + if let val = value as? [String: Any?] { + var walletInfo = try asWalletInfo(walletInfo: val) + list.append(walletInfo) + } else { + throw SdkError.Generic(message: errUnexpectedType(typeName: "WalletInfo")) + } + } + return list + } + + static func arrayOf(walletInfoList: [WalletInfo]) -> [Any] { + return walletInfoList.map { v -> [String: Any?] in return dictionaryOf(walletInfo: v) } + } + static func asAesSuccessActionDataResult(aesSuccessActionDataResult: [String: Any?]) throws -> AesSuccessActionDataResult { let type = aesSuccessActionDataResult["type"] as! String if type == "decrypted" { diff --git a/packages/react-native/src/index.ts b/packages/react-native/src/index.ts index ed7198515..b10de5d01 100644 --- a/packages/react-native/src/index.ts +++ b/packages/react-native/src/index.ts @@ -42,7 +42,7 @@ export interface BitcoinAddressData { message?: string } -export interface BlockchainDetails { +export interface BlockchainInfo { liquidTip: number bitcoinTip: number } @@ -109,12 +109,8 @@ export interface FiatCurrency { } export interface GetInfoResponse { - balanceSat: number - pendingSendSat: number - pendingReceiveSat: number - fingerprint: string - pubkey: string - blockchainDetails: BlockchainDetails + walletInfo: WalletInfo + blockchainInfo: BlockchainInfo } export interface LnInvoice { @@ -443,6 +439,14 @@ export interface UrlSuccessActionData { matchesCallbackDomain: boolean } +export interface WalletInfo { + balanceSat: number + pendingSendSat: number + pendingReceiveSat: number + fingerprint: string + pubkey: string +} + export enum AesSuccessActionDataResultVariant { DECRYPTED = "decrypted", ERROR_STATUS = "errorStatus"