spark_sdk/wallet/handlers/deposit.rs
1use spark_protos::spark::{
2 GenerateDepositAddressRequest, QueryUnusedDepositAddressesRequest, TreeNode,
3};
4use tonic::Request;
5
6use crate::{
7 error::SparkSdkError,
8 signer::traits::{secp256k1::KeygenMethod, SparkSigner},
9 wallet::internal_handlers::traits::{
10 deposit::{DepositInternalHandlers, GenerateDepositAddressSdkResponse},
11 mempool::MempoolInternalHandlers,
12 },
13 SparkSdk,
14};
15
16impl<S: SparkSigner + Send + Sync + Clone + 'static> SparkSdk<S> {
17 /// Generates a new deposit address for receiving funds into the Spark wallet.
18 ///
19 /// This function handles the generation of a new deposit address by:
20 /// 1. Creating a new signing keypair for the deposit address
21 /// 2. Requesting a deposit address from the Spark network
22 /// 3. Validating the returned address and proof of possession
23 ///
24 /// # Returns
25 ///
26 /// * `Ok(GenerateDepositAddressSdkResponse)` - Contains the validated deposit address and signing public key
27 /// * `Err(SparkSdkError)` - If there was an error during address generation
28 ///
29 /// # Errors
30 ///
31 /// Returns [`SparkSdkError`] if:
32 /// - Failed to generate new signing keypair
33 /// - Network errors when communicating with Spark operators
34 /// - Address validation fails (e.g. invalid proof of possession)
35 ///
36 /// # Example
37 ///
38 /// ```no_run
39 /// # use spark_wallet_sdk::SparkSdk;
40 /// # async fn example(mut sdk: SparkSdk) -> Result<(), Box<dyn std::error::Error>> {
41 /// let deposit_address = sdk.generate_deposit_address().await?;
42 /// println!("New deposit address: {}", deposit_address.deposit_address.address);
43 /// # Ok(())
44 /// # }
45 /// ```
46 pub async fn generate_deposit_address(
47 &self,
48 ) -> Result<GenerateDepositAddressSdkResponse, SparkSdkError> {
49 let identity_public_key = self.get_identity_public_key().to_vec();
50 let signing_public_key = self
51 .signer
52 .new_secp256k1_keypair(KeygenMethod::Random)?
53 .to_vec();
54
55 let mut spark_client = self.config.spark_config.get_spark_connection(None).await?;
56 let mut request = tonic::Request::new(GenerateDepositAddressRequest {
57 signing_public_key: signing_public_key.clone(),
58 identity_public_key,
59 network: self.config.spark_config.network.marshal_proto(),
60 });
61 self.add_authorization_header_to_request(&mut request, None);
62
63 let response = spark_client.generate_deposit_address(request).await?;
64
65 let response = response.into_inner();
66 let deposit_address = response.deposit_address.clone().unwrap();
67
68 self.validate_deposit_address(deposit_address, signing_public_key.clone())
69 .await?;
70
71 Ok(GenerateDepositAddressSdkResponse {
72 deposit_address: response.deposit_address.unwrap(),
73 signing_public_key,
74 })
75 }
76
77 /// Claims any pending deposits for this wallet by:
78 /// 1. Querying unused deposit addresses from Spark
79 /// 2. Checking the mempool for transactions to those addresses
80 /// 3. Finalizing any found deposits by creating tree nodes
81 ///
82 /// # Errors
83 ///
84 /// Returns [`SparkSdkError`] if:
85 /// - Failed to connect to Spark service
86 /// - Failed to query mempool
87 /// - Failed to finalize deposits
88 ///
89 /// # Example
90 ///
91 /// ```no_run
92 /// # use spark_wallet_sdk::SparkSdk;
93 /// # async fn example(sdk: SparkSdk) -> Result<(), Box<dyn std::error::Error>> {
94 /// sdk.claim_deposits().await?;
95 /// # Ok(())
96 /// # }
97 /// ```
98 pub async fn claim_deposits(&self) -> Result<(), SparkSdkError> {
99 let mut spark_client = self.config.spark_config.get_spark_connection(None).await?;
100 let identity_public_key = self.get_identity_public_key();
101
102 let mut request = Request::new(QueryUnusedDepositAddressesRequest {
103 identity_public_key: identity_public_key.to_vec(),
104 });
105 self.add_authorization_header_to_request(&mut request, None);
106
107 let response = spark_client.query_unused_deposit_addresses(request).await?;
108 let deposits = response.into_inner();
109
110 // println!("deposits are: {:?}", deposits);
111
112 for deposit in deposits.deposit_addresses {
113 // log duration
114 let time_start = std::time::Instant::now();
115 let queried_tx = self
116 .query_mempool_transaction(deposit.deposit_address.clone())
117 .await?;
118 let duration = time_start.elapsed();
119 println!("[query_mempool_transaction] duration: {:?}", duration);
120
121 let mempool_query_result = queried_tx;
122 let deposit_tx = mempool_query_result.transaction;
123 let vout = mempool_query_result.vout;
124
125 println!(
126 "Finalizing deposit for address: {}",
127 deposit.deposit_address
128 );
129 let time_start = std::time::Instant::now();
130 self.finalize_deposit(deposit.user_signing_public_key, deposit_tx, vout)
131 .await?;
132 let duration = time_start.elapsed();
133 println!("[finalize_deposit duration]: {:?}", duration);
134
135 // TODO: add a log
136 println!(
137 "[Claim Deposit Background Job]: Claimed deposit for address: {}",
138 deposit.deposit_address
139 );
140 }
141
142 Ok(())
143 }
144
145 /// Finalizes a deposit by creating a tree node and transferring it to self
146 ///
147 /// # Arguments
148 ///
149 /// * `signing_pubkey` - The public key used for signing
150 /// * `deposit_tx` - The Bitcoin transaction containing the deposit
151 /// * `vout` - The output index in the transaction
152 ///
153 /// # Errors
154 ///
155 /// Returns [`SparkSdkError`] if:
156 /// - Failed to create tree node
157 /// - Failed to transfer deposits
158 ///
159 /// # Returns
160 ///
161 /// Returns an empty vector of `TreeNode`s on success
162 pub async fn finalize_deposit(
163 &self,
164 signing_pubkey: Vec<u8>,
165 deposit_tx: bitcoin::Transaction,
166 vout: u32,
167 ) -> Result<Vec<TreeNode>, SparkSdkError> {
168 self.create_tree(Some(deposit_tx), None, vout, 0, signing_pubkey)
169 .await?;
170
171 // transfer deposits to self
172 // let leaf_ids = create_tree_response
173 // .nodes
174 // .iter()
175 // .map(|node| node.id.clone())
176 // .collect();
177 // self.transfer_deposits_to_self(leaf_ids).await?;
178
179 Ok(vec![])
180 }
181
182 async fn transfer_deposits_to_self(&self, leaf_ids: Vec<String>) -> Result<(), SparkSdkError> {
183 self.transfer_leaf_ids(leaf_ids, self.get_identity_public_key().to_vec())
184 .await?;
185
186 Ok(())
187 }
188}