breez_sdk_spark/sdk/
api.rs1use bitcoin::secp256k1::{PublicKey, ecdsa::Signature};
2use std::str::FromStr;
3use tracing::{error, info};
4
5use crate::{
6 BuyBitcoinRequest, BuyBitcoinResponse, CheckMessageRequest, CheckMessageResponse,
7 GetTokensMetadataRequest, GetTokensMetadataResponse, InputType, ListFiatCurrenciesResponse,
8 ListFiatRatesResponse, OptimizationProgress, SignMessageRequest, SignMessageResponse,
9 UpdateUserSettingsRequest, UserSettings,
10 chain::RecommendedFees,
11 error::SdkError,
12 events::EventListener,
13 issuer::TokenIssuer,
14 models::{GetInfoRequest, GetInfoResponse},
15 persist::ObjectCacheRepository,
16 utils::token::get_tokens_metadata_cached_or_query,
17};
18
19use super::{BreezSdk, helpers::get_or_create_deposit_address, parse_input};
20
21#[cfg_attr(feature = "uniffi", uniffi::export(async_runtime = "tokio"))]
22#[allow(clippy::needless_pass_by_value)]
23impl BreezSdk {
24 pub async fn add_event_listener(&self, listener: Box<dyn EventListener>) -> String {
34 self.event_emitter.add_listener(listener).await
35 }
36
37 pub async fn remove_event_listener(&self, id: &str) -> bool {
47 self.event_emitter.remove_listener(id).await
48 }
49
50 pub async fn disconnect(&self) -> Result<(), SdkError> {
59 info!("Disconnecting Breez SDK");
60 self.shutdown_sender
61 .send(())
62 .map_err(|_| SdkError::Generic("Failed to send shutdown signal".to_string()))?;
63
64 self.shutdown_sender.closed().await;
65 info!("Breez SDK disconnected");
66 Ok(())
67 }
68
69 pub async fn parse(&self, input: &str) -> Result<InputType, SdkError> {
70 parse_input(input, Some(self.external_input_parsers.clone())).await
71 }
72
73 #[allow(unused_variables)]
75 pub async fn get_info(&self, request: GetInfoRequest) -> Result<GetInfoResponse, SdkError> {
76 if request.ensure_synced.unwrap_or_default() {
77 self.initial_synced_watcher
78 .clone()
79 .changed()
80 .await
81 .map_err(|_| {
82 SdkError::Generic("Failed to receive initial synced signal".to_string())
83 })?;
84 }
85 let object_repository = ObjectCacheRepository::new(self.storage.clone());
86 let account_info = object_repository
87 .fetch_account_info()
88 .await?
89 .unwrap_or_default();
90 Ok(GetInfoResponse {
91 identity_pubkey: self.spark_wallet.get_identity_public_key().to_string(),
92 balance_sats: account_info.balance_sats,
93 token_balances: account_info.token_balances,
94 })
95 }
96
97 pub async fn list_fiat_currencies(&self) -> Result<ListFiatCurrenciesResponse, SdkError> {
100 let currencies = self
101 .fiat_service
102 .fetch_fiat_currencies()
103 .await?
104 .into_iter()
105 .map(From::from)
106 .collect();
107 Ok(ListFiatCurrenciesResponse { currencies })
108 }
109
110 pub async fn list_fiat_rates(&self) -> Result<ListFiatRatesResponse, SdkError> {
112 let rates = self
113 .fiat_service
114 .fetch_fiat_rates()
115 .await?
116 .into_iter()
117 .map(From::from)
118 .collect();
119 Ok(ListFiatRatesResponse { rates })
120 }
121
122 pub async fn recommended_fees(&self) -> Result<RecommendedFees, SdkError> {
124 Ok(self.chain_service.recommended_fees().await?)
125 }
126
127 pub async fn get_tokens_metadata(
134 &self,
135 request: GetTokensMetadataRequest,
136 ) -> Result<GetTokensMetadataResponse, SdkError> {
137 let metadata = get_tokens_metadata_cached_or_query(
138 &self.spark_wallet,
139 &ObjectCacheRepository::new(self.storage.clone()),
140 &request
141 .token_identifiers
142 .iter()
143 .map(String::as_str)
144 .collect::<Vec<_>>(),
145 )
146 .await?;
147 Ok(GetTokensMetadataResponse {
148 tokens_metadata: metadata,
149 })
150 }
151
152 pub async fn sign_message(
156 &self,
157 request: SignMessageRequest,
158 ) -> Result<SignMessageResponse, SdkError> {
159 use bitcoin::hex::DisplayHex;
160
161 let pubkey = self.spark_wallet.get_identity_public_key().to_string();
162 let signature = self.spark_wallet.sign_message(&request.message).await?;
163 let signature_hex = if request.compact {
164 signature.serialize_compact().to_lower_hex_string()
165 } else {
166 signature.serialize_der().to_lower_hex_string()
167 };
168
169 Ok(SignMessageResponse {
170 pubkey,
171 signature: signature_hex,
172 })
173 }
174
175 pub async fn check_message(
179 &self,
180 request: CheckMessageRequest,
181 ) -> Result<CheckMessageResponse, SdkError> {
182 let pubkey = PublicKey::from_str(&request.pubkey)
183 .map_err(|_| SdkError::InvalidInput("Invalid public key".to_string()))?;
184 let signature_bytes = hex::decode(&request.signature)
185 .map_err(|_| SdkError::InvalidInput("Not a valid hex encoded signature".to_string()))?;
186 let signature = Signature::from_der(&signature_bytes)
187 .or_else(|_| Signature::from_compact(&signature_bytes))
188 .map_err(|_| {
189 SdkError::InvalidInput("Not a valid DER or compact encoded signature".to_string())
190 })?;
191
192 let is_valid = self
193 .spark_wallet
194 .verify_message(&request.message, &signature, &pubkey)
195 .await
196 .is_ok();
197 Ok(CheckMessageResponse { is_valid })
198 }
199
200 pub async fn get_user_settings(&self) -> Result<UserSettings, SdkError> {
204 self.ensure_spark_private_mode_initialized().await?;
206
207 let spark_user_settings = self.spark_wallet.query_wallet_settings().await?;
208
209 Ok(UserSettings {
212 spark_private_mode_enabled: spark_user_settings.private_enabled,
213 })
214 }
215
216 pub async fn update_user_settings(
220 &self,
221 request: UpdateUserSettingsRequest,
222 ) -> Result<(), SdkError> {
223 if let Some(spark_private_mode_enabled) = request.spark_private_mode_enabled {
224 self.spark_wallet
225 .update_wallet_settings(spark_private_mode_enabled)
226 .await?;
227
228 let lightning_address = match self.get_lightning_address().await {
230 Ok(lightning_address) => lightning_address,
231 Err(e) => {
232 error!("Failed to get lightning address during user settings update: {e:?}");
233 return Ok(());
234 }
235 };
236 let Some(lightning_address) = lightning_address else {
237 return Ok(());
238 };
239 if let Err(e) = self
240 .register_lightning_address_internal(crate::RegisterLightningAddressRequest {
241 username: lightning_address.username,
242 description: Some(lightning_address.description),
243 })
244 .await
245 {
246 error!("Failed to reregister lightning address during user settings update: {e:?}");
247 }
248 }
249 Ok(())
250 }
251
252 pub fn get_token_issuer(&self) -> TokenIssuer {
254 TokenIssuer::new(self.spark_wallet.clone(), self.storage.clone())
255 }
256
257 pub fn start_leaf_optimization(&self) {
263 self.spark_wallet.start_leaf_optimization();
264 }
265
266 pub async fn cancel_leaf_optimization(&self) -> Result<(), SdkError> {
275 self.spark_wallet.cancel_leaf_optimization().await?;
276 Ok(())
277 }
278
279 pub fn get_leaf_optimization_progress(&self) -> OptimizationProgress {
281 self.spark_wallet.get_leaf_optimization_progress().into()
282 }
283
284 pub async fn buy_bitcoin(
298 &self,
299 request: BuyBitcoinRequest,
300 ) -> Result<BuyBitcoinResponse, SdkError> {
301 let address =
302 get_or_create_deposit_address(&self.spark_wallet, self.storage.clone(), true).await?;
303
304 let url = self
305 .buy_bitcoin_provider
306 .buy_bitcoin(address, request.locked_amount_sat, request.redirect_url)
307 .await
308 .map_err(|e| SdkError::Generic(format!("Failed to create buy bitcoin URL: {e}")))?;
309
310 Ok(BuyBitcoinResponse { url })
311 }
312}