diff --git a/lib/Cargo.lock b/lib/Cargo.lock index 6339064..245109d 100644 --- a/lib/Cargo.lock +++ b/lib/Cargo.lock @@ -812,6 +812,7 @@ dependencies = [ "env_logger 0.11.5", "flutter_rust_bridge", "futures-util", + "getrandom 0.2.14", "glob", "hex", "lazy_static", @@ -823,6 +824,7 @@ dependencies = [ "mockall", "paste", "prost 0.13.4", + "rand 0.8.5", "reqwest 0.12.13", "rusqlite", "rusqlite_migration", diff --git a/lib/core/Cargo.toml b/lib/core/Cargo.toml index ab7dbbb..f7b2515 100644 --- a/lib/core/Cargo.toml +++ b/lib/core/Cargo.toml @@ -85,11 +85,16 @@ uuid = { version = "1.8.0", features = ["v4", "js"] } [dev-dependencies] sdk-common = { workspace = true, features = ["test-utils"] } paste = "1.0.15" + +# Non-WASM dev dependencies +[target.'cfg(not(all(target_family = "wasm", target_os = "unknown")))'.dev-dependencies] tempdir = "0.3.7" # WASM dev dependencies [target.'cfg(all(target_family = "wasm", target_os = "unknown"))'.dev-dependencies] wasm-bindgen-test = "0.3.33" +rand = "0.8" +getrandom = { version = "0.2", features = ["js"] } [build-dependencies] anyhow = { version = "1.0.79", features = ["backtrace"] } diff --git a/lib/core/src/lib.rs b/lib/core/src/lib.rs index 6c5296c..05b2cac 100644 --- a/lib/core/src/lib.rs +++ b/lib/core/src/lib.rs @@ -174,6 +174,7 @@ pub(crate) mod event; #[cfg(not(all(target_family = "wasm", target_os = "unknown")))] pub(crate) mod frb_generated; pub(crate) mod lnurl; +#[cfg(not(all(target_family = "wasm", target_os = "unknown")))] pub mod logger; pub mod model; pub mod persist; @@ -186,7 +187,7 @@ pub(crate) mod swapper; pub(crate) mod sync; pub(crate) mod test_utils; pub(crate) mod utils; -pub(crate) mod wallet; +pub mod wallet; pub use sdk_common::prelude::*; diff --git a/lib/core/src/model.rs b/lib/core/src/model.rs index 4bdb293..3d6a074 100644 --- a/lib/core/src/model.rs +++ b/lib/core/src/model.rs @@ -189,6 +189,10 @@ impl Config { LiquidNetwork::Regtest => BOLTZ_REGTEST, } } + + pub fn sync_enabled(&self) -> bool { + self.sync_service_url.is_some() + } } /// Network chosen for this Liquid SDK instance. Note that it represents both the Liquid and the @@ -821,7 +825,7 @@ pub(crate) trait BlockListener: Send + Sync { // A swap enum variant #[derive(Clone, Debug)] -pub(crate) enum Swap { +pub enum Swap { Chain(ChainSwap), Send(SendSwap), Receive(ReceiveSwap), @@ -946,7 +950,7 @@ pub(crate) struct SwapMetadata { /// See #[derive(Clone, Debug, Derivative)] #[derivative(PartialEq)] -pub(crate) struct ChainSwap { +pub struct ChainSwap { pub(crate) id: String, pub(crate) direction: Direction, /// The Bitcoin claim address is only set for Outgoing Chain Swaps @@ -1113,7 +1117,7 @@ pub(crate) struct ChainSwapUpdate { /// A submarine swap, used for Send #[derive(Clone, Debug, Derivative)] #[derivative(PartialEq)] -pub(crate) struct SendSwap { +pub struct SendSwap { pub(crate) id: String, /// Bolt11 or Bolt12 invoice. This is determined by whether `bolt12_offer` is set or not. pub(crate) invoice: String, @@ -1206,7 +1210,7 @@ impl SendSwap { /// A reverse swap, used for Receive #[derive(Clone, Debug, Derivative)] #[derivative(PartialEq)] -pub(crate) struct ReceiveSwap { +pub struct ReceiveSwap { pub(crate) id: String, pub(crate) preimage: String, /// JSON representation of [crate::persist::receive::InternalCreateReverseResponse] diff --git a/lib/core/src/persist/backup.rs b/lib/core/src/persist/backup.rs index 019ac57..5db0ee4 100644 --- a/lib/core/src/persist/backup.rs +++ b/lib/core/src/persist/backup.rs @@ -44,10 +44,7 @@ mod tests { test_utils::persist::{create_persister, new_receive_swap, new_send_swap}, }; - #[cfg(all(target_family = "wasm", target_os = "unknown"))] - wasm_bindgen_test::wasm_bindgen_test_configure!(run_in_browser); - - #[sdk_macros::test_all] + #[sdk_macros::test_not_wasm] fn test_backup_and_restore() -> Result<()> { create_persister!(local); diff --git a/lib/core/src/persist/mod.rs b/lib/core/src/persist/mod.rs index cd1fb98..4d18fe3 100644 --- a/lib/core/src/persist/mod.rs +++ b/lib/core/src/persist/mod.rs @@ -11,7 +11,7 @@ pub(crate) mod sync; use std::collections::{HashMap, HashSet}; use std::ops::Not; -use std::{fs::create_dir_all, path::PathBuf, str::FromStr}; +use std::{path::PathBuf, str::FromStr}; use crate::lightning_invoice::{Bolt11Invoice, Bolt11InvoiceDescription}; use crate::model::*; @@ -32,7 +32,7 @@ use tokio::sync::broadcast::{self, Sender}; const DEFAULT_DB_FILENAME: &str = "storage.sql"; -pub(crate) struct Persister { +pub struct Persister { main_db_dir: PathBuf, network: LiquidNetwork, pub(crate) sync_trigger: Option>, @@ -60,21 +60,58 @@ fn where_clauses_to_string(where_clauses: Vec) -> String { } impl Persister { - pub fn new(working_dir: &str, network: LiquidNetwork, sync_enabled: bool) -> Result { + /// Creates a new Persister that stores data on the provided `working_dir`. + #[cfg(not(all(target_family = "wasm", target_os = "unknown")))] + pub fn new_using_fs( + working_dir: &str, + network: LiquidNetwork, + sync_enabled: bool, + asset_metadata: Option>, + ) -> Result { let main_db_dir = PathBuf::from_str(working_dir)?; if !main_db_dir.exists() { - create_dir_all(&main_db_dir)?; + std::fs::create_dir_all(&main_db_dir)?; } + Self::new_inner(main_db_dir, network, sync_enabled, asset_metadata) + } + + /// Creates a new Persister that only keeps data in memory. + /// + /// Multiple persisters accessing the same in-memory data can be created by providing the + /// same `database_id`. + #[cfg(all(target_family = "wasm", target_os = "unknown"))] + pub fn new_in_memory( + database_id: &str, + network: LiquidNetwork, + sync_enabled: bool, + asset_metadata: Option>, + ) -> Result { + let main_db_dir = PathBuf::from_str(database_id)?; + Self::new_inner(main_db_dir, network, sync_enabled, asset_metadata) + } + + fn new_inner( + main_db_dir: PathBuf, + network: LiquidNetwork, + sync_enabled: bool, + asset_metadata: Option>, + ) -> Result { let mut sync_trigger = None; if sync_enabled { let (events_notifier, _) = broadcast::channel::<()>(1); sync_trigger = Some(events_notifier); } - Ok(Persister { + + let persister = Persister { main_db_dir, network, sync_trigger, - }) + }; + + persister.init()?; + persister.replace_asset_metadata(asset_metadata)?; + + Ok(persister) } pub(crate) fn get_connection(&self) -> Result { diff --git a/lib/core/src/recover/recoverer.rs b/lib/core/src/recover/recoverer.rs index 94ba1c8..16ae437 100644 --- a/lib/core/src/recover/recoverer.rs +++ b/lib/core/src/recover/recoverer.rs @@ -27,7 +27,7 @@ use crate::{ const LIQUID_TIP_LEEWAY: u32 = 3; -pub(crate) struct Recoverer { +pub struct Recoverer { master_blinding_key: MasterBlindingKey, swapper: Arc, onchain_wallet: Arc, diff --git a/lib/core/src/sdk.rs b/lib/core/src/sdk.rs index d766a4d..07a80b0 100644 --- a/lib/core/src/sdk.rs +++ b/lib/core/src/sdk.rs @@ -1,6 +1,6 @@ use std::collections::{BTreeMap, HashMap, HashSet}; use std::ops::Not as _; -use std::{fs, path::PathBuf, str::FromStr, sync::Arc, time::Duration}; +use std::{path::PathBuf, str::FromStr, sync::Arc, time::Duration}; use anyhow::{anyhow, ensure, Result}; use boltz_client::{swaps::boltz::*, util::secrets::Preimage}; @@ -72,7 +72,7 @@ pub const DEFAULT_EXTERNAL_INPUT_PARSERS: &[(&str, &str, &str)] = &[( pub(crate) const NETWORK_PROPAGATION_GRACE_PERIOD: Duration = Duration::from_secs(30); -pub(crate) struct LiquidSdkBuilder { +pub struct LiquidSdkBuilder { config: Config, signer: Arc>, breez_server: Arc, @@ -162,17 +162,20 @@ impl LiquidSdkBuilder { self } + fn get_working_dir(&self) -> Result { + let fingerprint_hex: String = + Xpub::decode(self.signer.xpub()?.as_slice())?.identifier()[0..4].to_hex(); + self.config + .get_wallet_dir(&self.config.working_dir, &fingerprint_hex) + } + pub fn build(&self) -> Result> { if let Some(breez_api_key) = &self.config.breez_api_key { LiquidSdk::validate_breez_api_key(breez_api_key)? } - fs::create_dir_all(&self.config.working_dir)?; let fingerprint_hex: String = Xpub::decode(self.signer.xpub()?.as_slice())?.identifier()[0..4].to_hex(); - let working_dir = self - .config - .get_wallet_dir(&self.config.working_dir, &fingerprint_hex)?; let cache_dir = self.config.get_wallet_dir( self.config .cache_dir @@ -181,24 +184,20 @@ impl LiquidSdkBuilder { &fingerprint_hex, )?; - let sync_enabled = self - .config - .sync_service_url - .clone() - .map(|_| true) - .unwrap_or(false); - let persister = match self.persister.clone() { Some(persister) => persister, None => { - let persister = Arc::new(Persister::new( - &working_dir, + #[cfg(all(target_family = "wasm", target_os = "unknown"))] + return Err(anyhow!( + "Must provide a WASM-compatible persister on WASM builds" + )); + #[cfg(not(all(target_family = "wasm", target_os = "unknown")))] + Arc::new(Persister::new_using_fs( + &self.get_working_dir()?, self.config.network, - sync_enabled, - )?); - persister.init()?; - persister.replace_asset_metadata(self.config.asset_metadata.clone())?; - persister + self.config.sync_enabled(), + self.config.asset_metadata.clone(), + )?) } }; @@ -226,7 +225,7 @@ impl LiquidSdkBuilder { Some(onchain_wallet) => onchain_wallet, None => Arc::new(LiquidOnchainWallet::new( self.config.clone(), - &cache_dir, + cache_dir, persister.clone(), self.signer.clone(), )?), @@ -383,43 +382,49 @@ impl LiquidSdk { /// * `passphrase` - the optional passphrase for the mnemonic /// * `seed` - the optional Liquid wallet seed pub async fn connect(req: ConnectRequest) -> Result> { - let start_ts = Instant::now(); - let is_mainnet = req.config.network == LiquidNetwork::Mainnet; + let signer = Self::default_signer(&req)?; - let signer = match (req.mnemonic, req.seed) { - (None, Some(seed)) => Box::new(SdkSigner::new_with_seed(seed, is_mainnet)?), - (Some(mnemonic), None) => Box::new(SdkSigner::new( - &mnemonic, - req.passphrase.unwrap_or("".to_string()).as_ref(), + Self::connect_with_signer( + ConnectWithSignerRequest { config: req.config }, + Box::new(signer), + ) + .inspect_err(|e| error!("Failed to connect: {:?}", e)) + .await + } + + pub fn default_signer(req: &ConnectRequest) -> Result { + let is_mainnet = req.config.network == LiquidNetwork::Mainnet; + match (&req.mnemonic, &req.seed) { + (None, Some(seed)) => Ok(SdkSigner::new_with_seed(seed.clone(), is_mainnet)?), + (Some(mnemonic), None) => Ok(SdkSigner::new( + mnemonic, + req.passphrase.as_ref().unwrap_or(&"".to_string()).as_ref(), is_mainnet, )?), - _ => return Err(anyhow!("Either `mnemonic` or `seed` must be set")), - }; - - let sdk = - Self::connect_with_signer(ConnectWithSignerRequest { config: req.config }, signer) - .inspect_err(|e| error!("Failed to connect: {:?}", e)) - .await; - - let init_time = Instant::now().duration_since(start_ts); - utils::log_print_header(init_time); - - sdk + _ => Err(anyhow!("Either `mnemonic` or `seed` must be set")), + } } pub async fn connect_with_signer( req: ConnectWithSignerRequest, signer: Box, ) -> Result> { + let start_ts = Instant::now(); + + #[cfg(not(all(target_family = "wasm", target_os = "unknown")))] + std::fs::create_dir_all(&req.config.working_dir)?; + let sdk = LiquidSdkBuilder::new( req.config, PRODUCTION_BREEZSERVER_URL.into(), Arc::new(signer), )? .build()?; - sdk.start() - .inspect_err(|e| error!("Failed to start an SDK instance: {:?}", e)) - .await?; + sdk.start().await?; + + let init_time = Instant::now().duration_since(start_ts); + utils::log_print_header(init_time); + Ok(sdk) } @@ -450,9 +455,8 @@ impl LiquidSdk { /// Starts an SDK instance. /// - /// Internal method. Should only be called once per instance. - /// Should only be called as part of [LiquidSdk::connect]. - async fn start(self: &Arc) -> SdkResult<()> { + /// Should only be called once per instance. + pub async fn start(self: &Arc) -> SdkResult<()> { let mut is_started = self.is_started.write().await; self.persister .update_send_swaps_by_state(Created, TimedOut, Some(true)) @@ -3305,13 +3309,14 @@ impl LiquidSdk { } /// Empties the Liquid Wallet cache for the [Config::network]. + #[cfg(not(all(target_family = "wasm", target_os = "unknown")))] pub fn empty_wallet_cache(&self) -> Result<()> { let mut path = PathBuf::from(self.config.working_dir.clone()); path.push(Into::::into(self.config.network).as_str()); path.push("enc_cache"); - fs::remove_dir_all(&path)?; - fs::create_dir_all(path)?; + std::fs::remove_dir_all(&path)?; + std::fs::create_dir_all(path)?; Ok(()) } @@ -3827,6 +3832,7 @@ impl LiquidSdk { /// An error is thrown if the log file cannot be created in the working directory. /// /// An error is thrown if a global logger is already configured. + #[cfg(not(all(target_family = "wasm", target_os = "unknown")))] pub fn init_logging(log_dir: &str, app_logger: Option>) -> Result<()> { crate::logger::init_logging(log_dir, app_logger) } diff --git a/lib/core/src/sync/mod.rs b/lib/core/src/sync/mod.rs index a5a400c..0de0ba5 100644 --- a/lib/core/src/sync/mod.rs +++ b/lib/core/src/sync/mod.rs @@ -39,7 +39,7 @@ pub(crate) struct SyncCompletedData { pub(crate) pushed_records_count: u32, } -pub(crate) struct SyncService { +pub struct SyncService { remote_url: String, client_id: String, persister: Arc, diff --git a/lib/core/src/test_utils/persist.rs b/lib/core/src/test_utils/persist.rs index 112162b..46ac019 100644 --- a/lib/core/src/test_utils/persist.rs +++ b/lib/core/src/test_utils/persist.rs @@ -123,16 +123,38 @@ pub(crate) fn new_receive_swap( macro_rules! create_persister { ($name:ident) => { - let temp_dir = tempdir::TempDir::new("liquid-sdk")?; - let $name = std::sync::Arc::new(crate::persist::Persister::new( - temp_dir + #[cfg(all(target_family = "wasm", target_os = "unknown"))] + let $name = { + let db_id = { + use rand::Rng; + let res: String = rand::thread_rng() + .sample_iter(&rand::distributions::Alphanumeric) + .take(16) + .map(char::from) + .collect(); + res + }; + std::sync::Arc::new(crate::persist::Persister::new_in_memory( + &db_id, + crate::model::LiquidNetwork::Testnet, + true, + None, + )?) + }; + #[cfg(not(all(target_family = "wasm", target_os = "unknown")))] + let $name = { + let temp_dir_path = tempdir::TempDir::new("liquid-sdk")? .path() .to_str() - .ok_or(anyhow::anyhow!("Could not create temporary directory"))?, - crate::model::LiquidNetwork::Testnet, - true, - )?); - $name.init()?; + .ok_or(anyhow::anyhow!("Could not create temporary directory"))? + .to_string(); + std::sync::Arc::new(crate::persist::Persister::new_using_fs( + &temp_dir_path, + crate::model::LiquidNetwork::Testnet, + true, + None, + )?) + }; }; } pub(crate) use create_persister; diff --git a/lib/core/src/wallet.rs b/lib/core/src/wallet.rs index 5747205..233b1b7 100644 --- a/lib/core/src/wallet.rs +++ b/lib/core/src/wallet.rs @@ -1,8 +1,6 @@ use std::collections::HashMap; -use std::fs::{self, create_dir_all}; use std::io::Write; -use std::path::PathBuf; -use std::{path::Path, str::FromStr, sync::Arc}; +use std::{str::FromStr, sync::Arc}; use anyhow::{anyhow, Result}; use boltz_client::ElementsAddress; @@ -10,10 +8,10 @@ use log::{debug, info, warn}; use lwk_common::Signer as LwkSigner; use lwk_common::{singlesig_desc, Singlesig}; use lwk_wollet::elements::{AssetId, Txid}; -use lwk_wollet::ElectrumOptions; use lwk_wollet::{ elements::{hex::ToHex, Address, Transaction}, - ElectrumClient, ElectrumUrl, ElementsNetwork, FsPersister, WalletTx, Wollet, WolletDescriptor, + ElectrumClient, ElectrumOptions, ElectrumUrl, ElementsNetwork, FsPersister, NoPersist, + WalletTx, Wollet, WolletDescriptor, }; use sdk_common::bitcoin::hashes::{sha256, Hash}; use sdk_common::bitcoin::secp256k1::PublicKey; @@ -99,28 +97,29 @@ pub trait OnchainWallet: Send + Sync { async fn full_scan(&self) -> Result<(), PaymentError>; } -pub(crate) struct LiquidOnchainWallet { +pub struct LiquidOnchainWallet { config: Config, persister: Arc, wallet: Arc>, electrum_client: Mutex>, - working_dir: String, + working_dir: Option, pub(crate) signer: SdkLwkSigner, } impl LiquidOnchainWallet { + /// Creates a new LiquidOnchainWallet that caches data on the provided `working_dir`. pub(crate) fn new( config: Config, - working_dir: &String, + working_dir: String, persister: Arc, user_signer: Arc>, ) -> Result { - let signer = crate::signer::SdkLwkSigner::new(user_signer.clone())?; - let wollet = Self::create_wallet(&config, working_dir, &signer)?; + let signer = SdkLwkSigner::new(user_signer.clone())?; + let wollet = Self::create_wallet(&config, Some(&working_dir), &signer)?; - let working_dir_buf = PathBuf::from_str(working_dir)?; + let working_dir_buf = std::path::PathBuf::from_str(&working_dir)?; if !working_dir_buf.exists() { - create_dir_all(&working_dir_buf)?; + std::fs::create_dir_all(&working_dir_buf)?; } Ok(Self { @@ -128,40 +127,70 @@ impl LiquidOnchainWallet { persister, wallet: Arc::new(Mutex::new(wollet)), electrum_client: Mutex::new(None), - working_dir: working_dir.clone(), + working_dir: Some(working_dir), signer, }) } - fn create_wallet>( + /// Creates a new LiquidOnchainWallet that caches data in memory + pub fn new_in_memory( + config: Config, + persister: Arc, + user_signer: Arc>, + ) -> Result { + let signer = SdkLwkSigner::new(user_signer.clone())?; + let wollet = Self::create_wallet(&config, None, &signer)?; + + Ok(Self { + config, + persister, + wallet: Arc::new(Mutex::new(wollet)), + electrum_client: Mutex::new(None), + working_dir: None, + signer, + }) + } + + fn create_wallet( config: &Config, - working_dir: P, + working_dir: Option<&str>, signer: &SdkLwkSigner, ) -> Result { let elements_network: ElementsNetwork = config.network.into(); let descriptor = LiquidOnchainWallet::get_descriptor(signer, config.network)?; - let mut lwk_persister = - FsPersister::new(working_dir.as_ref(), elements_network, &descriptor)?; - let wollet_res = Wollet::new(elements_network, lwk_persister, descriptor.clone()); + let wollet_res = match &working_dir { + Some(working_dir) => Wollet::new( + elements_network, + FsPersister::new(working_dir, elements_network, &descriptor)?, + descriptor.clone(), + ), + None => Wollet::new(elements_network, NoPersist::new(), descriptor.clone()), + }; match wollet_res { Ok(wollet) => Ok(wollet), - Err( + res @ Err( lwk_wollet::Error::PersistError(_) | lwk_wollet::Error::UpdateHeightTooOld { .. } | lwk_wollet::Error::UpdateOnDifferentStatus { .. }, - ) => { - warn!("Update error initialising wollet, wipping storage and retrying: {wollet_res:?}"); - let mut path = working_dir.as_ref().to_path_buf(); - path.push(elements_network.as_str()); - fs::remove_dir_all(&path)?; - warn!("Wiping wallet in path: {:?}", path); - lwk_persister = FsPersister::new(working_dir, elements_network, &descriptor)?; - Ok(Wollet::new( - elements_network, - lwk_persister, - descriptor.clone(), - )?) - } + ) => match working_dir { + Some(working_dir) => { + warn!( + "Update error initialising wollet, wipping storage and retrying: {res:?}" + ); + let mut path = std::path::PathBuf::from(working_dir); + path.push(elements_network.as_str()); + std::fs::remove_dir_all(&path)?; + warn!("Wiping wallet in path: {:?}", path); + let lwk_persister = + FsPersister::new(working_dir, elements_network, &descriptor)?; + Ok(Wollet::new( + elements_network, + lwk_persister, + descriptor.clone(), + )?) + } + None => res.map_err(Into::into), + }, Err(e) => Err(e.into()), } } @@ -397,7 +426,7 @@ impl OnchainWallet for LiquidOnchainWallet { Err(lwk_wollet::Error::UpdateHeightTooOld { .. }) => { warn!("Full scan failed with update height too old, wiping storage and retrying"); let mut new_wallet = - Self::create_wallet(&self.config, &self.working_dir, &self.signer)?; + Self::create_wallet(&self.config, self.working_dir.as_deref(), &self.signer)?; lwk_wollet::full_scan_to_index_with_electrum_client( &mut new_wallet, index_with_buffer, @@ -443,7 +472,6 @@ mod tests { use crate::test_utils::persist::create_persister; use crate::wallet::LiquidOnchainWallet; use anyhow::Result; - use tempdir::TempDir; #[cfg(all(target_family = "wasm", target_os = "unknown"))] wasm_bindgen_test::wasm_bindgen_test_configure!(run_in_browser); @@ -456,15 +484,27 @@ mod tests { let config = Config::testnet(None); - // Create a temporary directory for working_dir - let temp_dir = TempDir::new("").unwrap(); - let working_dir = temp_dir.path().to_str().unwrap().to_string(); - create_persister!(storage); - let wallet: Arc = Arc::new( - LiquidOnchainWallet::new(config, &working_dir, storage, sdk_signer.clone()).unwrap(), - ); + let wallet: Arc = + if cfg!(not(all(target_family = "wasm", target_os = "unknown"))) { + // Create a temporary directory for working_dir + let working_dir = tempdir::TempDir::new("") + .unwrap() + .path() + .to_str() + .unwrap() + .to_string(); + Arc::new( + LiquidOnchainWallet::new(config, working_dir, storage, sdk_signer.clone()) + .unwrap(), + ) + } else { + Arc::new( + LiquidOnchainWallet::new_in_memory(config, storage, sdk_signer.clone()) + .unwrap(), + ) + }; // Test message let message = "Hello, Liquid!"; diff --git a/lib/wasm/src/lib.rs b/lib/wasm/src/lib.rs index 1c1f9e6..37ddde7 100644 --- a/lib/wasm/src/lib.rs +++ b/lib/wasm/src/lib.rs @@ -6,15 +6,17 @@ mod signer; use std::str::FromStr; use std::sync::Arc; +use crate::event::{EventListener, WasmEventListener}; +use crate::model::*; use anyhow::anyhow; -use breez_sdk_liquid::sdk::LiquidSdk; +use breez_sdk_liquid::persist::Persister; +use breez_sdk_liquid::sdk::{LiquidSdk, LiquidSdkBuilder}; +use breez_sdk_liquid::wallet::LiquidOnchainWallet; +use breez_sdk_liquid::PRODUCTION_BREEZSERVER_URL; use log::Level; use signer::{Signer, WasmSigner}; use wasm_bindgen::prelude::*; -use crate::event::{EventListener, WasmEventListener}; -use crate::model::*; - #[wasm_bindgen] pub struct BindingLiquidSdk { sdk: Arc, @@ -22,8 +24,8 @@ pub struct BindingLiquidSdk { #[wasm_bindgen(js_name = "connect")] pub async fn connect(req: ConnectRequest) -> WasmResult { - let sdk = LiquidSdk::connect(req.into()).await?; - Ok(BindingLiquidSdk { sdk }) + let signer = Box::new(LiquidSdk::default_signer(&req.clone().into())?); + connect_inner(req.config, signer).await } #[wasm_bindgen(js_name = "connectWithSigner")] @@ -31,8 +33,42 @@ pub async fn connect_with_signer( req: ConnectWithSignerRequest, signer: Signer, ) -> WasmResult { - let wasm_signer = Box::new(WasmSigner { signer }); - let sdk = LiquidSdk::connect_with_signer(req.into(), wasm_signer).await?; + let signer: Box = Box::new(WasmSigner { signer }); + connect_inner(req.config, signer).await +} + +async fn connect_inner( + config: Config, + signer: Box, +) -> WasmResult { + let config: breez_sdk_liquid::model::Config = config.into(); + let signer = Arc::new(signer); + + let mut sdk_builder = LiquidSdkBuilder::new( + config.clone(), + PRODUCTION_BREEZSERVER_URL.to_string(), + Arc::clone(&signer), + )?; + + let persister = Arc::new(Persister::new_in_memory( + &config.working_dir, + config.network, + config.sync_enabled(), + config.asset_metadata.clone(), + )?); + + let onchain_wallet = Arc::new(LiquidOnchainWallet::new_in_memory( + config, + Arc::clone(&persister), + signer, + )?); + + sdk_builder.persister(persister); + sdk_builder.onchain_wallet(onchain_wallet); + + let sdk = sdk_builder.build()?; + sdk.start().await?; + Ok(BindingLiquidSdk { sdk }) } @@ -294,12 +330,6 @@ impl BindingLiquidSdk { Ok(self.sdk.recommended_fees().await?.into()) } - #[wasm_bindgen(js_name = "emptyWalletCache")] - pub fn empty_wallet_cache(&self) -> WasmResult<()> { - self.sdk.empty_wallet_cache()?; - Ok(()) - } - #[wasm_bindgen(js_name = "backup")] pub fn backup(&self, req: BackupRequest) -> WasmResult<()> { self.sdk.backup(req.into())?; diff --git a/lib/wasm/src/model.rs b/lib/wasm/src/model.rs index db8d8eb..ce9f4eb 100644 --- a/lib/wasm/src/model.rs +++ b/lib/wasm/src/model.rs @@ -10,6 +10,7 @@ pub enum Network { Regtest, } +#[derive(Clone)] #[sdk_macros::extern_wasm_bindgen(breez_sdk_liquid::prelude::ExternalInputParser)] pub struct ExternalInputParser { pub provider_id: String, @@ -292,6 +293,7 @@ pub struct Symbol { pub position: Option, } +#[derive(Clone)] #[sdk_macros::extern_wasm_bindgen(breez_sdk_liquid::prelude::Config)] pub struct Config { pub liquid_electrum_url: String, @@ -310,6 +312,7 @@ pub struct Config { pub asset_metadata: Option>, } +#[derive(Clone)] #[sdk_macros::extern_wasm_bindgen(breez_sdk_liquid::prelude::LiquidNetwork)] pub enum LiquidNetwork { Mainnet, @@ -330,6 +333,7 @@ pub enum SdkEvent { Synced, } +#[derive(Clone)] #[sdk_macros::extern_wasm_bindgen(breez_sdk_liquid::prelude::ConnectRequest)] pub struct ConnectRequest { pub config: Config, @@ -639,6 +643,7 @@ pub struct LnUrlInfo { pub lnurl_withdraw_endpoint: Option, } +#[derive(Clone)] #[sdk_macros::extern_wasm_bindgen(breez_sdk_liquid::prelude::AssetMetadata)] pub struct AssetMetadata { pub asset_id: String,