1use crate::accs::hash_access_control_conditions;
2use crate::auth::{AuthContext, AuthSig};
3use crate::client::{ExecuteJsOptions, LitClient};
4use crate::error::LitSdkError;
5use crate::session::issue_session_sigs;
6use crate::types::EncryptParams;
7use base64ct::{Base64, Encoding};
8use ethers::types::U256;
9use rand::RngCore;
10use reqwest::Method;
11use serde::{Deserialize, Serialize};
12use serde_json::Value;
13
14const LIT_PREFIX: &str = "lit_";
15const AUTH_HEADER_PREFIX: &str = "LitSessionSig:";
16
17const SERVICE_URL_TEST: &str = "https://test.wrapped.litprotocol.com/encrypted";
18const SERVICE_URL_PROD: &str = "https://wrapped.litprotocol.com/encrypted";
19
20const CID_SIGN_TRANSACTION_EVM: &str = "QmdSQqkdGF5EqPCBi4pidkjGQXLNoP5kp8gxGLtgzyiw7L";
21const CID_SIGN_TRANSACTION_SOLANA: &str = "QmVeR4rKuyN27gq1JrsoJqjN2GrwZD87Sm2vHzV5MmBxwb";
22const CID_SIGN_MESSAGE_EVM: &str = "QmQWwWjJXLiCKi7ZpfwXnGPeXAcjfG1VXjB6CLTb3Xh31X";
23const CID_SIGN_MESSAGE_SOLANA: &str = "QmawpLLPxL6GVKj7QW3PR8us4gNyVEijPpcDzD8Uo95jXR";
24const CID_GENERATE_KEY_EVM: &str = "QmSKi3kMRP7biW6HhNf79vcffMSXDSu7Yr1K653ZoGXsxw";
25const CID_GENERATE_KEY_SOLANA: &str = "QmZGd5gPcqTJmeM9Thdguz6DufFkQCa9Qq2oS6L3D6HD8o";
26const CID_EXPORT_PRIVATE_KEY: &str = "QmaCGGq6EqXezgBiwAAbwh2UTeeTZnLaHQxxDcXwRboFXM";
27const CID_BATCH_GENERATE_KEYS: &str = "QmUDB7jZfCMwh9CuQZZ4YDmrJnNdPq9NGdqHzmQE3RggSr";
28
29#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
30#[serde(rename_all = "lowercase")]
31pub enum WrappedKeysNetwork {
32 Evm,
33 Solana,
34}
35
36#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
37pub enum WrappedKeysKeyType {
38 #[serde(rename = "K256")]
39 K256,
40 #[serde(rename = "ed25519")]
41 Ed25519,
42}
43
44impl WrappedKeysNetwork {
45 pub fn key_type(self) -> WrappedKeysKeyType {
46 match self {
47 WrappedKeysNetwork::Evm => WrappedKeysKeyType::K256,
48 WrappedKeysNetwork::Solana => WrappedKeysKeyType::Ed25519,
49 }
50 }
51}
52
53#[derive(Debug, Clone, Serialize, Deserialize)]
54#[serde(rename_all = "camelCase")]
55pub struct StoredKeyMetadata {
56 pub public_key: String,
57 pub pkp_address: String,
58 pub key_type: WrappedKeysKeyType,
59 pub lit_network: String,
60 pub memo: String,
61 pub id: String,
62}
63
64#[derive(Debug, Clone, Serialize, Deserialize)]
65#[serde(rename_all = "camelCase")]
66pub struct StoredKeyData {
67 #[serde(flatten)]
68 pub metadata: StoredKeyMetadata,
69 pub ciphertext: String,
70 pub data_to_encrypt_hash: String,
71}
72
73#[derive(Debug, Clone, Serialize, Deserialize)]
74#[serde(rename_all = "camelCase")]
75pub struct StoreEncryptedKeyResult {
76 pub id: String,
77 pub pkp_address: String,
78}
79
80#[derive(Debug, Clone, Serialize, Deserialize)]
81#[serde(rename_all = "camelCase")]
82pub struct StoreEncryptedKeyBatchResult {
83 pub pkp_address: String,
84 pub ids: Vec<String>,
85}
86
87#[derive(Debug, Clone, Serialize, Deserialize)]
88#[serde(rename_all = "camelCase")]
89pub struct GeneratePrivateKeyResult {
90 pub pkp_address: String,
91 pub generated_public_key: String,
92 pub id: String,
93}
94
95#[derive(Debug, Clone, Serialize, Deserialize)]
96#[serde(rename_all = "camelCase")]
97pub struct ExportPrivateKeyResult {
98 pub pkp_address: String,
99 pub decrypted_private_key: String,
100 pub public_key: String,
101 pub lit_network: String,
102 pub key_type: WrappedKeysKeyType,
103 pub memo: String,
104 pub id: String,
105}
106
107#[derive(Debug, Clone, Serialize, Deserialize)]
108#[serde(rename_all = "camelCase")]
109pub struct ImportPrivateKeyResult {
110 pub pkp_address: String,
111 pub id: String,
112}
113
114#[derive(Debug, Clone, Serialize, Deserialize)]
115#[serde(rename_all = "camelCase")]
116pub struct StoreEncryptedKeyParams {
117 pub ciphertext: String,
118 pub data_to_encrypt_hash: String,
119 pub public_key: String,
120 pub key_type: WrappedKeysKeyType,
121 pub memo: String,
122}
123
124#[derive(Debug, Clone, Serialize, Deserialize)]
125#[serde(rename_all = "camelCase")]
126pub struct GenerateKeyParams {
127 pub memo: String,
128}
129
130#[derive(Debug, Clone, Serialize, Deserialize)]
131#[serde(rename_all = "camelCase")]
132pub struct SignMessageParams {
133 pub message_to_sign: String,
134}
135
136#[derive(Debug, Clone, Serialize, Deserialize)]
137#[serde(rename_all = "camelCase")]
138pub struct GeneratePrivateKeyAction {
139 pub network: WrappedKeysNetwork,
140 pub generate_key_params: GenerateKeyParams,
141 #[serde(skip_serializing_if = "Option::is_none")]
142 pub sign_message_params: Option<SignMessageParams>,
143}
144
145#[derive(Debug, Clone, Serialize, Deserialize)]
146#[serde(rename_all = "camelCase")]
147pub struct BatchGeneratePrivateKeysParams {
148 pub actions: Vec<GeneratePrivateKeyAction>,
149}
150
151#[derive(Debug, Clone, Serialize, Deserialize)]
152#[serde(rename_all = "camelCase")]
153pub struct BatchGeneratePrivateKeysActionResult {
154 pub generate_encrypted_private_key: GeneratePrivateKeyResultWithMemo,
155 #[serde(skip_serializing_if = "Option::is_none")]
156 pub sign_message: Option<BatchSignedMessageResult>,
157}
158
159#[derive(Debug, Clone, Serialize, Deserialize)]
160#[serde(rename_all = "camelCase")]
161pub struct BatchGeneratePrivateKeysResult {
162 pub pkp_address: String,
163 pub results: Vec<BatchGeneratePrivateKeysActionResult>,
164}
165
166#[derive(Debug, Clone, Serialize, Deserialize)]
167#[serde(rename_all = "camelCase")]
168pub struct GeneratePrivateKeyResultWithMemo {
169 pub pkp_address: String,
170 pub generated_public_key: String,
171 pub id: String,
172 pub memo: String,
173}
174
175#[derive(Debug, Clone, Serialize, Deserialize)]
176#[serde(rename_all = "camelCase")]
177pub struct BatchSignedMessageResult {
178 pub signature: String,
179}
180
181#[derive(Clone)]
182pub struct WrappedKeysClient {
183 lit_client: LitClient,
184 http: reqwest::Client,
185}
186
187impl WrappedKeysClient {
188 pub fn new(lit_client: LitClient) -> Result<Self, LitSdkError> {
189 let http = reqwest::Client::builder()
190 .user_agent("lit-sdk-rust-wrapped-keys/0.1.0")
191 .build()?;
192 Ok(Self { lit_client, http })
193 }
194
195 pub fn lit_client(&self) -> &LitClient {
196 &self.lit_client
197 }
198
199 pub async fn generate_private_key(
200 &self,
201 auth_context: &AuthContext,
202 network: WrappedKeysNetwork,
203 memo: impl Into<String>,
204 user_max_price_wei: Option<U256>,
205 ) -> Result<GeneratePrivateKeyResult, LitSdkError> {
206 let pkp_address = pkp_address_from_auth_context(auth_context)?;
207 let access_control_conditions = pkp_access_control_conditions(&pkp_address);
208
209 let cid = match network {
210 WrappedKeysNetwork::Evm => CID_GENERATE_KEY_EVM,
211 WrappedKeysNetwork::Solana => CID_GENERATE_KEY_SOLANA,
212 };
213
214 let js_params = serde_json::json!({
215 "pkpAddress": pkp_address,
216 "accessControlConditions": access_control_conditions,
217 });
218
219 let lit_action_res = self
220 .lit_client
221 .execute_js_with_options(
222 None,
223 Some(cid.to_string()),
224 Some(js_params),
225 auth_context,
226 ExecuteJsOptions {
227 use_single_node: true,
228 user_max_price_wei,
229 ..Default::default()
230 },
231 )
232 .await?;
233
234 let generated: GenerateEncryptedKeyResponse = decode_lit_action_json(&lit_action_res)?;
235 let store_res = self
236 .store_encrypted_key(
237 auth_context,
238 StoreEncryptedKeyParams {
239 ciphertext: generated.ciphertext,
240 data_to_encrypt_hash: generated.data_to_encrypt_hash,
241 public_key: generated.public_key.clone(),
242 key_type: network.key_type(),
243 memo: memo.into(),
244 },
245 )
246 .await?;
247
248 Ok(GeneratePrivateKeyResult {
249 pkp_address: store_res.pkp_address,
250 id: store_res.id,
251 generated_public_key: generated.public_key,
252 })
253 }
254
255 pub async fn export_private_key(
256 &self,
257 auth_context: &AuthContext,
258 network: WrappedKeysNetwork,
259 id: &str,
260 user_max_price_wei: Option<U256>,
261 ) -> Result<ExportPrivateKeyResult, LitSdkError> {
262 let stored = self.get_encrypted_key(auth_context, id).await?;
263 let access_control_conditions = pkp_access_control_conditions(&stored.metadata.pkp_address);
264
265 let js_params = serde_json::json!({
266 "pkpAddress": stored.metadata.pkp_address,
267 "ciphertext": stored.ciphertext,
268 "dataToEncryptHash": stored.data_to_encrypt_hash,
269 "accessControlConditions": access_control_conditions,
270 });
271
272 let lit_action_res = self
273 .lit_client
274 .execute_js_with_options(
275 None,
276 Some(lit_action_cid_for_export_private_key(network).to_string()),
277 Some(js_params),
278 auth_context,
279 ExecuteJsOptions {
280 use_single_node: false,
281 user_max_price_wei,
282 ..Default::default()
283 },
284 )
285 .await?;
286
287 let decrypted_private_key = decode_lit_action_string(&lit_action_res)?;
288
289 Ok(ExportPrivateKeyResult {
290 pkp_address: stored.metadata.pkp_address,
291 decrypted_private_key,
292 public_key: stored.metadata.public_key,
293 lit_network: stored.metadata.lit_network,
294 key_type: stored.metadata.key_type,
295 memo: stored.metadata.memo,
296 id: stored.metadata.id,
297 })
298 }
299
300 pub async fn list_encrypted_key_metadata(
301 &self,
302 auth_context: &AuthContext,
303 ) -> Result<Vec<StoredKeyMetadata>, LitSdkError> {
304 let base_url = service_url_for_lit_network(self.lit_client.network_name())?;
305 let pkp_address = pkp_address_from_auth_context(auth_context)?;
306 let session_sig = session_sig_for_service(&self.lit_client, auth_context)?;
307 let request_id = random_request_id();
308
309 let url = format!("{base_url}/{pkp_address}");
310 self.service_request(Method::GET, &url, &session_sig, &request_id, None)
311 .await
312 }
313
314 pub async fn get_encrypted_key(
315 &self,
316 auth_context: &AuthContext,
317 id: &str,
318 ) -> Result<StoredKeyData, LitSdkError> {
319 let base_url = service_url_for_lit_network(self.lit_client.network_name())?;
320 let pkp_address = pkp_address_from_auth_context(auth_context)?;
321 let session_sig = session_sig_for_service(&self.lit_client, auth_context)?;
322 let request_id = random_request_id();
323
324 let url = format!("{base_url}/{pkp_address}/{id}");
325 self.service_request(Method::GET, &url, &session_sig, &request_id, None)
326 .await
327 }
328
329 pub async fn store_encrypted_key(
330 &self,
331 auth_context: &AuthContext,
332 params: StoreEncryptedKeyParams,
333 ) -> Result<StoreEncryptedKeyResult, LitSdkError> {
334 let base_url = service_url_for_lit_network(self.lit_client.network_name())?;
335 let session_sig = session_sig_for_service(&self.lit_client, auth_context)?;
336 let request_id = random_request_id();
337
338 let body = serde_json::to_value(params).map_err(|e| LitSdkError::Network(e.to_string()))?;
339 self.service_request(
340 Method::POST,
341 base_url,
342 &session_sig,
343 &request_id,
344 Some(body),
345 )
346 .await
347 }
348
349 pub async fn store_encrypted_key_batch(
350 &self,
351 auth_context: &AuthContext,
352 key_batch: Vec<StoreEncryptedKeyParams>,
353 ) -> Result<StoreEncryptedKeyBatchResult, LitSdkError> {
354 let base_url = service_url_for_lit_network(self.lit_client.network_name())?;
355 let session_sig = session_sig_for_service(&self.lit_client, auth_context)?;
356 let request_id = random_request_id();
357
358 let body = serde_json::json!({ "keyParamsBatch": key_batch });
359 let url = format!("{base_url}_batch");
360 self.service_request(Method::POST, &url, &session_sig, &request_id, Some(body))
361 .await
362 }
363
364 pub async fn import_private_key(
365 &self,
366 auth_context: &AuthContext,
367 private_key: &str,
368 public_key: &str,
369 key_type: WrappedKeysKeyType,
370 memo: impl Into<String>,
371 ) -> Result<ImportPrivateKeyResult, LitSdkError> {
372 let pkp_address = pkp_address_from_auth_context(auth_context)?;
373 let access_control_conditions = pkp_access_control_conditions(&pkp_address);
374
375 let salted_private_key = format!("{LIT_PREFIX}{private_key}");
376 let hashed_accs = hex::encode(hash_access_control_conditions(&access_control_conditions)?);
377 let encrypted = self
378 .lit_client
379 .encrypt(EncryptParams {
380 data_to_encrypt: salted_private_key.as_bytes().to_vec(),
381 unified_access_control_conditions: None,
382 hashed_access_control_conditions_hex: Some(hashed_accs),
383 metadata: None,
384 })
385 .await?;
386
387 let store_res = self
388 .store_encrypted_key(
389 auth_context,
390 StoreEncryptedKeyParams {
391 ciphertext: encrypted.ciphertext_base64,
392 data_to_encrypt_hash: encrypted.data_to_encrypt_hash_hex,
393 public_key: public_key.to_string(),
394 key_type,
395 memo: memo.into(),
396 },
397 )
398 .await?;
399
400 Ok(ImportPrivateKeyResult {
401 pkp_address: store_res.pkp_address,
402 id: store_res.id,
403 })
404 }
405
406 pub async fn sign_message_with_encrypted_key(
407 &self,
408 auth_context: &AuthContext,
409 network: WrappedKeysNetwork,
410 id: &str,
411 message_to_sign: &str,
412 user_max_price_wei: Option<U256>,
413 ) -> Result<String, LitSdkError> {
414 let stored = self.get_encrypted_key(auth_context, id).await?;
415 let access_control_conditions = pkp_access_control_conditions(&stored.metadata.pkp_address);
416
417 let cid = match network {
418 WrappedKeysNetwork::Evm => CID_SIGN_MESSAGE_EVM,
419 WrappedKeysNetwork::Solana => CID_SIGN_MESSAGE_SOLANA,
420 };
421
422 let js_params = serde_json::json!({
423 "pkpAddress": stored.metadata.pkp_address,
424 "ciphertext": stored.ciphertext,
425 "dataToEncryptHash": stored.data_to_encrypt_hash,
426 "messageToSign": message_to_sign,
427 "accessControlConditions": access_control_conditions,
428 });
429
430 let lit_action_res = self
431 .lit_client
432 .execute_js_with_options(
433 None,
434 Some(cid.to_string()),
435 Some(js_params),
436 auth_context,
437 ExecuteJsOptions {
438 use_single_node: false,
439 user_max_price_wei,
440 ..Default::default()
441 },
442 )
443 .await?;
444
445 decode_lit_action_string(&lit_action_res)
446 }
447
448 #[allow(clippy::too_many_arguments)]
449 pub async fn sign_transaction_with_encrypted_key<T: Serialize>(
450 &self,
451 auth_context: &AuthContext,
452 network: WrappedKeysNetwork,
453 id: &str,
454 unsigned_transaction: T,
455 broadcast: bool,
456 versioned_transaction: Option<bool>,
457 user_max_price_wei: Option<U256>,
458 ) -> Result<String, LitSdkError> {
459 let stored = self.get_encrypted_key(auth_context, id).await?;
460 let access_control_conditions = pkp_access_control_conditions(&stored.metadata.pkp_address);
461
462 let cid = match network {
463 WrappedKeysNetwork::Evm => CID_SIGN_TRANSACTION_EVM,
464 WrappedKeysNetwork::Solana => CID_SIGN_TRANSACTION_SOLANA,
465 };
466
467 let unsigned_tx_val = serde_json::to_value(unsigned_transaction)
468 .map_err(|e| LitSdkError::Network(e.to_string()))?;
469
470 let inner = if let Some(v) = versioned_transaction {
471 serde_json::json!({
472 "pkpAddress": stored.metadata.pkp_address,
473 "ciphertext": stored.ciphertext,
474 "dataToEncryptHash": stored.data_to_encrypt_hash,
475 "unsignedTransaction": unsigned_tx_val,
476 "broadcast": broadcast,
477 "accessControlConditions": access_control_conditions,
478 "versionedTransaction": v,
479 })
480 } else {
481 serde_json::json!({
482 "pkpAddress": stored.metadata.pkp_address,
483 "ciphertext": stored.ciphertext,
484 "dataToEncryptHash": stored.data_to_encrypt_hash,
485 "unsignedTransaction": unsigned_tx_val,
486 "broadcast": broadcast,
487 "accessControlConditions": access_control_conditions,
488 })
489 };
490 let mut js_params = inner.clone();
491 if let Some(obj) = js_params.as_object_mut() {
492 obj.insert("jsParams".into(), inner);
493 }
494
495 let lit_action_res = self
496 .lit_client
497 .execute_js_with_options(
498 None,
499 Some(cid.to_string()),
500 Some(js_params),
501 auth_context,
502 ExecuteJsOptions {
503 use_single_node: false,
504 user_max_price_wei,
505 ..Default::default()
506 },
507 )
508 .await?;
509
510 decode_lit_action_string(&lit_action_res)
511 }
512
513 pub async fn batch_generate_private_keys(
514 &self,
515 auth_context: &AuthContext,
516 params: BatchGeneratePrivateKeysParams,
517 user_max_price_wei: Option<U256>,
518 ) -> Result<BatchGeneratePrivateKeysResult, LitSdkError> {
519 let pkp_address = pkp_address_from_auth_context(auth_context)?;
520 let access_control_conditions = pkp_access_control_conditions(&pkp_address);
521
522 let lit_action_res = self
523 .lit_client
524 .execute_js_with_options(
525 None,
526 Some(CID_BATCH_GENERATE_KEYS.to_string()),
527 Some(serde_json::json!({
528 "actions": params.actions,
529 "accessControlConditions": access_control_conditions,
530 })),
531 auth_context,
532 ExecuteJsOptions {
533 use_single_node: true,
534 user_max_price_wei,
535 ..Default::default()
536 },
537 )
538 .await?;
539
540 let action_results: Vec<BatchGenerateLitActionResult> =
541 decode_lit_action_json(&lit_action_res)?;
542 let store_batch: Vec<StoreEncryptedKeyParams> = action_results
543 .iter()
544 .map(|r| StoreEncryptedKeyParams {
545 ciphertext: r.generate_encrypted_private_key.ciphertext.clone(),
546 data_to_encrypt_hash: r
547 .generate_encrypted_private_key
548 .data_to_encrypt_hash
549 .clone(),
550 public_key: r.generate_encrypted_private_key.public_key.clone(),
551 key_type: r.network.key_type(),
552 memo: r.generate_encrypted_private_key.memo.clone(),
553 })
554 .collect();
555
556 let stored = self
557 .store_encrypted_key_batch(auth_context, store_batch)
558 .await?;
559
560 let mut results = vec![];
561 for (idx, r) in action_results.into_iter().enumerate() {
562 let id = stored.ids.get(idx).cloned().ok_or_else(|| {
563 LitSdkError::Network(
564 "wrapped keys service returned fewer ids than requested".into(),
565 )
566 })?;
567
568 results.push(BatchGeneratePrivateKeysActionResult {
569 generate_encrypted_private_key: GeneratePrivateKeyResultWithMemo {
570 pkp_address: pkp_address.clone(),
571 generated_public_key: r.generate_encrypted_private_key.public_key,
572 id,
573 memo: r.generate_encrypted_private_key.memo,
574 },
575 sign_message: r.sign_message,
576 });
577 }
578
579 Ok(BatchGeneratePrivateKeysResult {
580 pkp_address,
581 results,
582 })
583 }
584
585 async fn service_request<T: serde::de::DeserializeOwned>(
586 &self,
587 method: Method,
588 url: &str,
589 session_sig: &AuthSig,
590 request_id: &str,
591 body: Option<Value>,
592 ) -> Result<T, LitSdkError> {
593 let lit_network = self.lit_client.network_name();
594 let auth_header = compose_service_auth_header(session_sig)?;
595
596 let mut req = self
597 .http
598 .request(method, url)
599 .header("x-correlation-id", request_id)
600 .header("Content-Type", "application/json")
601 .header("Lit-Network", lit_network)
602 .header("Authorization", auth_header);
603 if let Some(body_val) = body {
604 req = req.json(&body_val);
605 }
606
607 let res = req.send().await?;
608 let status = res.status();
609 if !status.is_success() {
610 let txt = res.text().await.unwrap_or_default();
611 return Err(LitSdkError::Network(format!(
612 "Request({request_id}) for wrapped key failed. HTTP({}): {txt}",
613 status.as_u16()
614 )));
615 }
616
617 res.json::<T>().await.map_err(|e| {
618 LitSdkError::Network(format!(
619 "Request({request_id}) for wrapped key failed to parse JSON: {e}"
620 ))
621 })
622 }
623}
624
625fn service_url_for_lit_network(lit_network: &str) -> Result<&'static str, LitSdkError> {
626 match lit_network {
627 "naga-dev" | "naga-test" => Ok(SERVICE_URL_TEST),
628 "naga" => Ok(SERVICE_URL_PROD),
629 other => Err(LitSdkError::Config(format!(
630 "wrapped keys service unsupported for network {other}"
631 ))),
632 }
633}
634
635fn pkp_address_from_auth_context(auth_context: &AuthContext) -> Result<String, LitSdkError> {
636 if auth_context.delegation_auth_sig.algo.as_deref() != Some("LIT_BLS") {
637 return Err(LitSdkError::Config(
638 "wrapped keys requires a PKP auth context (delegationAuthSig algo LIT_BLS)".into(),
639 ));
640 }
641 Ok(auth_context.delegation_auth_sig.address.clone())
642}
643
644fn pkp_access_control_conditions(pkp_address: &str) -> Value {
645 serde_json::json!([
646 {
647 "contractAddress": "",
648 "standardContractType": "",
649 "chain": "ethereum",
650 "method": "",
651 "parameters": [":userAddress"],
652 "returnValueTest": {
653 "comparator": "=",
654 "value": pkp_address,
655 }
656 }
657 ])
658}
659
660fn session_sig_for_service(
661 lit_client: &LitClient,
662 auth_context: &AuthContext,
663) -> Result<AuthSig, LitSdkError> {
664 let url = lit_client
665 .handshake_result()
666 .connected_nodes
667 .first()
668 .ok_or_else(|| LitSdkError::Network("no connected nodes available".into()))?
669 .clone();
670 let map = issue_session_sigs(
671 &auth_context.session_key_pair,
672 &auth_context.auth_config,
673 &auth_context.delegation_auth_sig,
674 &[url],
675 )?;
676 map.into_iter()
677 .next()
678 .map(|(_, sig)| sig)
679 .ok_or_else(|| LitSdkError::Network("failed to generate sessionSig".into()))
680}
681
682fn compose_service_auth_header(session_sig: &AuthSig) -> Result<String, LitSdkError> {
683 let session_sig_json =
684 serde_json::to_string(session_sig).map_err(|e| LitSdkError::Network(e.to_string()))?;
685 let encoded = Base64::encode_string(session_sig_json.as_bytes());
686 Ok(format!("{AUTH_HEADER_PREFIX}{encoded}"))
687}
688
689fn random_request_id() -> String {
690 let mut bytes = [0u8; 8];
691 rand::thread_rng().fill_bytes(&mut bytes);
692 hex::encode(bytes)
693}
694
695fn lit_action_cid_for_export_private_key(_network: WrappedKeysNetwork) -> &'static str {
696 CID_EXPORT_PRIVATE_KEY
697}
698
699fn decode_lit_action_string(res: &crate::types::ExecuteJsResponse) -> Result<String, LitSdkError> {
700 match &res.response {
701 Value::String(s) => {
702 if s.is_empty() {
703 return Err(LitSdkError::Network(
704 "Lit Action returned an empty response".into(),
705 ));
706 }
707 if s.starts_with("Error:") {
708 return Err(LitSdkError::Network(format!(
709 "error executing Lit Action: {s}"
710 )));
711 }
712 Ok(s.clone())
713 }
714 other => Err(LitSdkError::Network(format!(
715 "expected Lit Action response string, got: {other}"
716 ))),
717 }
718}
719
720fn decode_lit_action_json<T: serde::de::DeserializeOwned>(
721 res: &crate::types::ExecuteJsResponse,
722) -> Result<T, LitSdkError> {
723 let s = match &res.response {
724 Value::String(s) => s.clone(),
725 other => serde_json::to_string(other).map_err(|e| LitSdkError::Network(e.to_string()))?,
726 };
727 if s.is_empty() {
728 return Err(LitSdkError::Network(
729 "Lit Action returned an empty response".into(),
730 ));
731 }
732 if s.starts_with("Error:") {
733 return Err(LitSdkError::Network(format!(
734 "error executing Lit Action: {s}"
735 )));
736 }
737 serde_json::from_str(&s).map_err(|e| LitSdkError::Network(e.to_string()))
738}
739
740#[derive(Debug, Deserialize)]
741#[serde(rename_all = "camelCase")]
742struct GenerateEncryptedKeyResponse {
743 ciphertext: String,
744 data_to_encrypt_hash: String,
745 public_key: String,
746}
747
748#[derive(Debug, Clone, Deserialize)]
749#[serde(rename_all = "camelCase")]
750struct BatchGenerateEncryptedPrivateKeyResult {
751 ciphertext: String,
752 data_to_encrypt_hash: String,
753 public_key: String,
754 memo: String,
755}
756
757#[derive(Debug, Clone, Deserialize)]
758#[serde(rename_all = "camelCase")]
759struct BatchGenerateLitActionResult {
760 network: WrappedKeysNetwork,
761 #[serde(default)]
762 sign_message: Option<BatchSignedMessageResult>,
763 generate_encrypted_private_key: BatchGenerateEncryptedPrivateKeyResult,
764}