spark_sdk/wallet/handlers/
init.rs

1use std::sync::Arc;
2
3use crate::{
4    error::SparkSdkError,
5    signer::traits::SparkSigner,
6    wallet::{
7        config::WalletConfig,
8        internal_handlers::traits::{
9            authenticate::AuthenticateInternalHandlers, leaves::LeavesInternalHandlers,
10        },
11        leaf_manager::LeafManager,
12    },
13    SparkNetwork, SparkSdk,
14};
15
16impl<S: SparkSigner + Send + Sync + Clone + 'static> SparkSdk<S> {
17    /// Creates a new instance of the Spark SDK.
18    ///
19    /// This is the main entry point for interacting with the Spark protocol. It initializes the SDK with
20    /// the provided network configuration, signer implementation, and optional data storage path.
21    ///
22    /// # Arguments
23    ///
24    /// * `network` - The Spark network to connect to (e.g. Regtest or Mainnet)
25    /// * `signer` - Implementation of the SparkSigner trait wrapped in Arc<RwLock> for thread-safe access
26    /// * `data_path` - Optional path to store wallet data. If None, defaults to WALLET_DB_PATH
27    ///
28    /// # Returns
29    ///
30    /// Returns a Result containing either:
31    /// * The initialized SparkSdk instance
32    /// * A SparkSdkError if initialization fails
33    ///
34    /// # Examples
35    ///
36    /// ```no_run
37    /// use spark_wallet_sdk::{SparkSdk, SparkNetwork, DefaultSigner};
38    /// use std::sync::Arc;
39    /// use parking_lot::RwLock;
40    ///
41    /// async fn init_sdk() {
42    ///     let signer = Arc::new(RwLock::new(DefaultSigner::new().unwrap()));
43    ///     let sdk = SparkSdk::new(
44    ///         SparkNetwork::Regtest,
45    ///         signer,
46    ///         None
47    ///     ).await.unwrap();
48    /// }
49    /// ```
50    pub async fn new(network: SparkNetwork, signer: Arc<S>) -> Result<Self, SparkSdkError> {
51        let config = WalletConfig::new(network).await?;
52        let leaf_manager = Arc::new(LeafManager::new());
53        let session = Arc::new(parking_lot::RwLock::new(Vec::new()));
54
55        let sdk = Self {
56            config,
57            identity_public_key: Arc::new(signer.get_identity_public_key()?),
58            signer: signer.clone(),
59            leaf_manager,
60            session,
61        };
62
63        let sessions = sdk.authenticate().await?;
64        let mut write = sdk.session.write();
65        *write = sessions;
66        drop(write);
67
68        let sdk_clone = sdk.clone();
69
70        // refresh BTC leaves
71        sdk_clone.fetch_owned_leaves_from_spark().await?;
72
73        // refresh tokens
74
75        // make a background job to claim deposits
76        tokio::spawn(async move {
77            let mut interval = tokio::time::interval(std::time::Duration::from_secs(10));
78            loop {
79                interval.tick().await;
80                let claimed_deposits = sdk_clone.claim_deposits().await;
81                // // TODO: add a log
82                // if !claimed_deposits.is_ok() {
83                //     let error = claimed_deposits.err().unwrap();
84                //     println!(
85                //         "[Claim Deposit Background Job]: Failed to claim deposits: {}",
86                //         error
87                //     );
88                // } else {
89                //     println!(
90                //         "[Claim Deposit Background Job]: Claimed pending deposits successfully. If there was no pending deposits, did not claim anything."
91                //     );
92                // }
93            }
94        });
95
96        // make a background job to claim transfers
97        let sdk_clone = sdk.clone();
98        tokio::spawn(async move {
99            let mut interval = tokio::time::interval(std::time::Duration::from_secs(10));
100            loop {
101                interval.tick().await;
102                let claimed_transfers = sdk_clone.claim_transfers().await;
103                if !claimed_transfers.is_ok() {
104                    println!(
105                        "[Claim Transfer Background Job] Failed to claim transfers: {}",
106                        claimed_transfers.err().unwrap()
107                    );
108                } else {
109                    println!(
110                        "[Claim Transfer Background Job] Claimed pending transfers successfully. If there was no pending transfers, did not claim anything."
111                    );
112                }
113            }
114        });
115
116        Ok(sdk)
117    }
118
119    /// Returns the identity public key of the wallet.
120    ///
121    /// The identity public key is a 33-byte compressed secp256k1 public key that uniquely identifies
122    /// this wallet instance. It is used for authentication and authorization with Spark operators.
123    /// This key is generated when the wallet is first created and remains constant throughout the
124    /// wallet's lifetime.
125    ///
126    /// The identity public key serves several purposes:
127    /// - Authenticates the wallet with Spark operators during API calls
128    /// - Used in deposit address generation to prove ownership
129    /// - Required for validating operator signatures
130    /// - Helps prevent unauthorized access to wallet funds
131    ///
132    /// # Returns
133    /// A byte slice containing the 33-byte compressed secp256k1 public key in SEC format.
134    /// The first byte is either 0x02 or 0x03 (the parity), followed by the 32-byte X coordinate.
135    ///
136    /// # Examples
137    /// ```no_run
138    /// # use spark_wallet_sdk::{SparkSdk, SparkNetwork, DefaultSigner};
139    /// # use std::sync::Arc;
140    /// # use parking_lot::RwLock;
141    /// # async fn example() {
142    /// let signer = Arc::new(RwLock::new(DefaultSigner::new().unwrap()));
143    /// let sdk = SparkSdk::new(SparkNetwork::Regtest, signer, None).await.unwrap();
144    ///
145    /// let pubkey = sdk.get_identity_public_key();
146    /// assert_eq!(pubkey.len(), 33);
147    /// # }
148    /// ```
149    pub fn get_identity_public_key(&self) -> &[u8] {
150        &self.identity_public_key
151    }
152
153    /// Returns the Bitcoin network that this wallet is connected to.
154    ///
155    /// The network determines which Spark operators the wallet communicates with and which Bitcoin network
156    /// (mainnet or regtest) is used for transactions.
157    ///
158    /// # Network Types
159    /// - [`SparkNetwork::Mainnet`] - Production Bitcoin mainnet environment
160    /// - [`SparkNetwork::Regtest`] - Testing environment using Lightspark's regtest network
161    ///
162    /// The network is set when creating the wallet and cannot be changed after initialization.
163    /// All transactions and addresses will be created for the configured network.
164    ///
165    /// # Returns
166    /// Returns a [`SparkNetwork`] enum indicating whether this is a mainnet or regtest wallet.
167    ///
168    /// # Examples
169    /// ```no_run
170    /// # use spark_wallet_sdk::{SparkSdk, SparkNetwork, DefaultSigner};
171    /// # use std::sync::Arc;
172    /// # use parking_lot::RwLock;
173    /// # async fn example() {
174    /// let signer = Arc::new(RwLock::new(DefaultSigner::new().unwrap()));
175    /// let sdk = SparkSdk::new(SparkNetwork::Regtest, signer, None).await.unwrap();
176    ///
177    /// assert_eq!(sdk.get_network(), SparkNetwork::Regtest);
178    /// # }
179    /// ```
180    pub fn get_network(&self) -> SparkNetwork {
181        self.config.spark_config.network
182    }
183}