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}