quantus_cli/cli/
recovery.rs

1use crate::{
2	chain::quantus_subxt, cli::common::resolve_address, log_error, log_print, log_success,
3};
4use clap::Subcommand;
5// no colored output needed here
6use sp_core::crypto::{AccountId32 as SpAccountId32, Ss58Codec};
7
8// Base unit (QUAN) decimals for amount conversions
9const QUAN_DECIMALS: u128 = 1_000_000_000_000; // 10^12
10
11/// Recovery-related commands
12#[derive(Subcommand, Debug)]
13pub enum RecoveryCommands {
14	/// Initiate recovery (rescuer starts)
15	Initiate {
16		/// Rescuer wallet name
17		#[arg(long)]
18		rescuer: String,
19		/// Lost account (SS58 or wallet name)
20		#[arg(long)]
21		lost: String,
22		/// Password for rescuer wallet
23		#[arg(short, long)]
24		password: Option<String>,
25		/// Read password from file (for scripting)
26		#[arg(long)]
27		password_file: Option<String>,
28	},
29
30	/// Vouch for a recovery attempt (friend)
31	Vouch {
32		/// Friend wallet name (who vouches)
33		#[arg(long)]
34		friend: String,
35		/// Lost account (SS58 or wallet name)
36		#[arg(long)]
37		lost: String,
38		/// Rescuer account (SS58 or wallet name)
39		#[arg(long)]
40		rescuer: String,
41		/// Password for friend wallet
42		#[arg(short, long)]
43		password: Option<String>,
44		/// Read password from file
45		#[arg(long)]
46		password_file: Option<String>,
47	},
48
49	/// Claim recovery (rescuer claims after threshold and delay)
50	Claim {
51		/// Rescuer wallet name
52		#[arg(long)]
53		rescuer: String,
54		/// Lost account (SS58 or wallet name)
55		#[arg(long)]
56		lost: String,
57		/// Password for rescuer wallet
58		#[arg(short, long)]
59		password: Option<String>,
60		/// Read password from file
61		#[arg(long)]
62		password_file: Option<String>,
63	},
64
65	/// Close an active recovery (lost account stops a malicious attempt)
66	Close {
67		/// Lost wallet name (the recoverable account)
68		#[arg(long)]
69		lost: String,
70		/// Rescuer account (SS58 or wallet name)
71		#[arg(long)]
72		rescuer: String,
73		/// Password for lost wallet
74		#[arg(short, long)]
75		password: Option<String>,
76		/// Read password from file
77		#[arg(long)]
78		password_file: Option<String>,
79	},
80
81	/// Cancel recovered proxy (rescuer disables their own proxy)
82	CancelProxy {
83		/// Rescuer wallet name
84		#[arg(long)]
85		rescuer: String,
86		/// Lost account (SS58 or wallet name)
87		#[arg(long)]
88		lost: String,
89		/// Password for rescuer wallet
90		#[arg(short, long)]
91		password: Option<String>,
92		/// Read password from file
93		#[arg(long)]
94		password_file: Option<String>,
95	},
96
97	/// Query: active recovery info
98	Active {
99		/// Lost account (SS58 or wallet name)
100		#[arg(long)]
101		lost: String,
102		/// Rescuer account (SS58 or wallet name)
103		#[arg(long)]
104		rescuer: String,
105	},
106
107	/// Query: proxy-of (rescuer -> lost)
108	ProxyOf {
109		/// Rescuer account (SS58 or wallet name)
110		#[arg(long)]
111		rescuer: String,
112	},
113
114	/// Query: recovery config (recoverable)
115	Config {
116		/// Account to query (SS58 or wallet name)
117		#[arg(long)]
118		account: String,
119	},
120
121	/// Recover all funds from the lost account to a destination
122	RecoverAll {
123		/// Rescuer wallet name
124		#[arg(long)]
125		rescuer: String,
126		/// Lost account (SS58 or wallet name)
127		#[arg(long)]
128		lost: String,
129		/// Destination to receive the recovered funds
130		#[arg(long)]
131		dest: String,
132		/// Keep the lost account alive
133		#[arg(long, default_value_t = true)]
134		keep_alive: bool,
135		/// Password for rescuer wallet
136		#[arg(short, long)]
137		password: Option<String>,
138		/// Read password from file
139		#[arg(long)]
140		password_file: Option<String>,
141	},
142
143	/// Recover a specific amount (in QUAN units) from the lost account to destination
144	RecoverAmount {
145		/// Rescuer wallet name
146		#[arg(long)]
147		rescuer: String,
148		/// Lost account (SS58 or wallet name)
149		#[arg(long)]
150		lost: String,
151		/// Destination to receive the recovered funds
152		#[arg(long)]
153		dest: String,
154		/// Amount in QUAN (human units) - multiplied by chain decimals
155		#[arg(long, value_name = "AMOUNT_QUAN")]
156		amount_quan: u128,
157		/// Keep the lost account alive
158		#[arg(long, default_value_t = true)]
159		keep_alive: bool,
160		/// Password for rescuer wallet
161		#[arg(short, long)]
162		password: Option<String>,
163		/// Read password from file
164		#[arg(long)]
165		password_file: Option<String>,
166	},
167}
168
169pub async fn handle_recovery_command(
170	command: RecoveryCommands,
171	node_url: &str,
172	finalized: bool,
173) -> crate::error::Result<()> {
174	let quantus_client = crate::chain::client::QuantusClient::new(node_url).await?;
175
176	match command {
177		RecoveryCommands::Initiate { rescuer, lost, password, password_file } => {
178			let rescuer_key =
179				crate::wallet::load_keypair_from_wallet(&rescuer, password, password_file)?;
180			let rescuer_addr = rescuer_key.to_account_id_ss58check();
181			log_print!("🔑 Rescuer: {}", rescuer);
182			log_print!("🔑 Rescuer address: {}", rescuer_addr);
183			let lost_resolved = resolve_address(&lost)?;
184			let lost_id_sp = SpAccountId32::from_ss58check(&lost_resolved).map_err(|e| {
185				crate::error::QuantusError::Generic(format!("Invalid lost address: {e:?}"))
186			})?;
187			let lost_id_bytes: [u8; 32] = *lost_id_sp.as_ref();
188			let lost_id = subxt::ext::subxt_core::utils::AccountId32::from(lost_id_bytes);
189			let call = quantus_subxt::api::tx()
190				.recovery()
191				.initiate_recovery(subxt::ext::subxt_core::utils::MultiAddress::Id(lost_id));
192
193			let tx_hash = crate::cli::common::submit_transaction(
194				&quantus_client,
195				&rescuer_key,
196				call,
197				None,
198				finalized,
199			)
200			.await
201			.map_err(|e| {
202				crate::error::QuantusError::NetworkError(format!(
203					"Failed to submit initiate_recovery transaction: {e}"
204				))
205			})?;
206			log_success!("✅ Initiate recovery submitted successfully {:?}", tx_hash);
207		},
208
209		RecoveryCommands::Vouch { friend, lost, rescuer, password, password_file } => {
210			let friend_key =
211				crate::wallet::load_keypair_from_wallet(&friend, password, password_file)?;
212			let lost_resolved = resolve_address(&lost)?;
213			let rescuer_resolved = resolve_address(&rescuer)?;
214			let lost_sp = SpAccountId32::from_ss58check(&lost_resolved).map_err(|e| {
215				crate::error::QuantusError::Generic(format!("Invalid lost address: {e:?}"))
216			})?;
217			let lost_bytes: [u8; 32] = *lost_sp.as_ref();
218			let lost_id = subxt::ext::subxt_core::utils::AccountId32::from(lost_bytes);
219			let rescuer_sp = SpAccountId32::from_ss58check(&rescuer_resolved).map_err(|e| {
220				crate::error::QuantusError::Generic(format!("Invalid rescuer address: {e:?}"))
221			})?;
222			let rescuer_bytes: [u8; 32] = *rescuer_sp.as_ref();
223			let rescuer_id = subxt::ext::subxt_core::utils::AccountId32::from(rescuer_bytes);
224			let call = quantus_subxt::api::tx().recovery().vouch_recovery(
225				subxt::ext::subxt_core::utils::MultiAddress::Id(lost_id),
226				subxt::ext::subxt_core::utils::MultiAddress::Id(rescuer_id),
227			);
228			let tx_hash = crate::cli::common::submit_transaction(
229				&quantus_client,
230				&friend_key,
231				call,
232				None,
233				finalized,
234			)
235			.await
236			.map_err(|e| {
237				crate::error::QuantusError::NetworkError(format!(
238					"Failed to submit vouch_recovery transaction: {e}"
239				))
240			})?;
241			log_success!("✅ Vouch submitted successfully {:?}", tx_hash);
242		},
243
244		RecoveryCommands::Claim { rescuer, lost, password, password_file } => {
245			let rescuer_key =
246				crate::wallet::load_keypair_from_wallet(&rescuer, password, password_file)?;
247			let lost_resolved = resolve_address(&lost)?;
248			let lost_sp = SpAccountId32::from_ss58check(&lost_resolved).map_err(|e| {
249				crate::error::QuantusError::Generic(format!("Invalid lost address: {e:?}"))
250			})?;
251			let lost_bytes: [u8; 32] = *lost_sp.as_ref();
252			let lost_id = subxt::ext::subxt_core::utils::AccountId32::from(lost_bytes);
253			let call = quantus_subxt::api::tx()
254				.recovery()
255				.claim_recovery(subxt::ext::subxt_core::utils::MultiAddress::Id(lost_id));
256			let tx_hash = crate::cli::common::submit_transaction(
257				&quantus_client,
258				&rescuer_key,
259				call,
260				None,
261				finalized,
262			)
263			.await
264			.map_err(|e| {
265				crate::error::QuantusError::NetworkError(format!(
266					"Failed to submit claim_recovery transaction: {e}"
267				))
268			})?;
269
270			log_success!("✅ Claim submitted successfully {:?}", tx_hash);
271		},
272
273		RecoveryCommands::RecoverAll {
274			rescuer,
275			lost,
276			dest,
277			keep_alive,
278			password,
279			password_file,
280		} => {
281			use quantus_subxt::api::runtime_types::pallet_balances::pallet::Call as BalancesCall;
282
283			let rescuer_key =
284				crate::wallet::load_keypair_from_wallet(&rescuer, password, password_file)?;
285			let rescuer_addr = rescuer_key.to_account_id_ss58check();
286			log_print!("🔑 Rescuer: {}", rescuer);
287			log_print!("🔑 Rescuer address: {}", rescuer_addr);
288
289			let lost_resolved = resolve_address(&lost)?;
290			let dest_resolved = resolve_address(&dest)?;
291			log_print!("🆘 Lost input: {} -> {}", lost, lost_resolved);
292			log_print!("🎯 Dest input: {} -> {}", dest, dest_resolved);
293			log_print!("🛟 keep_alive: {}", keep_alive);
294
295			let lost_sp = SpAccountId32::from_ss58check(&lost_resolved).map_err(|e| {
296				crate::error::QuantusError::Generic(format!("Invalid lost address: {e:?}"))
297			})?;
298			let dest_sp = SpAccountId32::from_ss58check(&dest_resolved).map_err(|e| {
299				crate::error::QuantusError::Generic(format!("Invalid dest address: {e:?}"))
300			})?;
301
302			let lost_id_bytes: [u8; 32] = *lost_sp.as_ref();
303			let dest_id_bytes: [u8; 32] = *dest_sp.as_ref();
304			let lost_id = subxt::ext::subxt_core::utils::AccountId32::from(lost_id_bytes);
305			let dest_id = subxt::ext::subxt_core::utils::AccountId32::from(dest_id_bytes);
306
307			// Check proxy mapping for rescuer
308			let rescuer_sp = SpAccountId32::from_ss58check(&rescuer_addr).map_err(|e| {
309				crate::error::QuantusError::Generic(format!(
310					"Invalid rescuer address from wallet: {e:?}"
311				))
312			})?;
313			let rescuer_id_bytes: [u8; 32] = *rescuer_sp.as_ref();
314			let rescuer_id = subxt::ext::subxt_core::utils::AccountId32::from(rescuer_id_bytes);
315			let proxy_storage = quantus_subxt::api::storage().recovery().proxy(rescuer_id);
316			let latest = quantus_client.get_latest_block().await?;
317			let proxy_result =
318				quantus_client.client().storage().at(latest).fetch(&proxy_storage).await;
319			let proxy_of = match proxy_result {
320				Ok(Some(proxy)) => {
321					log_print!("🧩 Proxy mapping: rescuer proxies -> {}", format!("{}", proxy));
322					Some(proxy)
323				},
324				Ok(None) => {
325					log_error!(
326						"❌ No proxy mapping found for rescuer - recovery not set up properly"
327					);
328					return Err(crate::error::QuantusError::Generic(
329						"Rescuer has no proxy mapping. Recovery process may not be properly set up.".to_string()
330					));
331				},
332				Err(e) => {
333					log_error!("❌ Proxy mapping fetch error: {:?}", e);
334					return Err(crate::error::QuantusError::NetworkError(format!(
335						"Failed to check proxy mapping: {e:?}"
336					)));
337				},
338			};
339
340			// Validate that the proxy points to the correct lost account
341			if let Some(proxy) = proxy_of {
342				let proxy_addr = format!("{proxy}");
343				if proxy_addr != lost_resolved {
344					log_error!(
345						"❌ Proxy mismatch! Rescuer proxies {} but we're trying to recover {}",
346						proxy_addr,
347						lost_resolved
348					);
349					return Err(crate::error::QuantusError::Generic(format!(
350						"Proxy mismatch: rescuer proxies {proxy_addr} but target is {lost_resolved}"
351					)));
352				}
353				log_print!("✅ Proxy validation successful");
354			}
355
356			let inner_call = quantus_subxt::api::Call::Balances(BalancesCall::transfer_all {
357				dest: subxt::ext::subxt_core::utils::MultiAddress::Id(dest_id),
358				keep_alive,
359			});
360			log_print!("🧱 Inner call: Balances.transfer_all(keep_alive={})", keep_alive);
361
362			let call = quantus_subxt::api::tx()
363				.recovery()
364				.as_recovered(subxt::ext::subxt_core::utils::MultiAddress::Id(lost_id), inner_call);
365
366			let tx_hash = match crate::cli::common::submit_transaction(
367				&quantus_client,
368				&rescuer_key,
369				call,
370				None,
371				finalized,
372			)
373			.await
374			{
375				Ok(h) => h,
376				Err(e) => {
377					log_error!("❌ Submit error (recover_all): {:?}", e);
378					return Err(e);
379				},
380			};
381			log_success!("✅ recover_all submitted successfully {:?}", tx_hash);
382		},
383
384		RecoveryCommands::RecoverAmount {
385			rescuer,
386			lost,
387			dest,
388			amount_quan,
389			keep_alive,
390			password,
391			password_file,
392		} => {
393			use quantus_subxt::api::runtime_types::pallet_balances::pallet::Call as BalancesCall;
394
395			let rescuer_key =
396				crate::wallet::load_keypair_from_wallet(&rescuer, password, password_file)?;
397
398			let rescuer_addr = rescuer_key.to_account_id_ss58check();
399			log_print!("🔑 Rescuer: {}", rescuer);
400			log_print!("🔑 Rescuer address: {}", rescuer_addr);
401
402			let lost_resolved = resolve_address(&lost)?;
403			let dest_resolved = resolve_address(&dest)?;
404			log_print!("🆘 Lost input: {} -> {}", lost, lost_resolved);
405			log_print!("🎯 Dest input: {} -> {}", dest, dest_resolved);
406			log_print!("💵 amount_quan: {} (QUAN_DECIMALS={})", amount_quan, QUAN_DECIMALS);
407			log_print!("🛟 keep_alive: {}", keep_alive);
408
409			let lost_sp = SpAccountId32::from_ss58check(&lost_resolved).map_err(|e| {
410				crate::error::QuantusError::Generic(format!("Invalid lost address: {e:?}"))
411			})?;
412			let dest_sp = SpAccountId32::from_ss58check(&dest_resolved).map_err(|e| {
413				crate::error::QuantusError::Generic(format!("Invalid dest address: {e:?}"))
414			})?;
415
416			let lost_id_bytes: [u8; 32] = *lost_sp.as_ref();
417			let dest_id_bytes: [u8; 32] = *dest_sp.as_ref();
418			let lost_id = subxt::ext::subxt_core::utils::AccountId32::from(lost_id_bytes);
419			let dest_id = subxt::ext::subxt_core::utils::AccountId32::from(dest_id_bytes);
420
421			let amount_plancks = amount_quan.saturating_mul(QUAN_DECIMALS);
422			log_print!("💵 amount_plancks: {}", amount_plancks);
423
424			let latest = quantus_client.get_latest_block().await?;
425
426			// Check account balance before attempting transfer
427			log_print!("💰 Checking lost account balance...");
428			let balance_result = quantus_client
429				.client()
430				.storage()
431				.at(latest)
432				.fetch(&quantus_subxt::api::storage().system().account(lost_id.clone()))
433				.await;
434
435			let account_info = match balance_result {
436				Ok(Some(info)) => info,
437				Ok(None) => {
438					log_error!("❌ Lost account not found in storage");
439					return Err(crate::error::QuantusError::Generic(
440						"Lost account not found in storage".to_string(),
441					));
442				},
443				Err(e) => {
444					log_error!("❌ Failed to fetch account balance: {:?}", e);
445					return Err(crate::error::QuantusError::NetworkError(format!(
446						"Failed to fetch account balance: {e:?}"
447					)));
448				},
449			};
450
451			let available_balance = account_info.data.free;
452			log_print!("💰 Available balance: {} plancks", available_balance);
453
454			if available_balance < amount_plancks {
455				log_error!(
456					"❌ Insufficient funds! Account has {} plancks but needs {} plancks",
457					available_balance,
458					amount_plancks
459				);
460				return Err(crate::error::QuantusError::Generic(format!(
461					"Insufficient funds: account has {available_balance} plancks but transfer requires {amount_plancks} plancks"
462				)));
463			}
464
465			log_print!("✅ Balance validation successful - sufficient funds available");
466
467			let inner_call =
468				quantus_subxt::api::Call::Balances(BalancesCall::transfer_keep_alive {
469					dest: subxt::ext::subxt_core::utils::MultiAddress::Id(dest_id),
470					value: amount_plancks,
471				});
472
473			let call = quantus_subxt::api::tx()
474				.recovery()
475				.as_recovered(subxt::ext::subxt_core::utils::MultiAddress::Id(lost_id), inner_call);
476
477			let tx_hash = match crate::cli::common::submit_transaction(
478				&quantus_client,
479				&rescuer_key,
480				call,
481				None,
482				finalized,
483			)
484			.await
485			{
486				Ok(h) => h,
487				Err(e) => {
488					log_error!("❌ Submit error (recover_amount): {:?}", e);
489					return Err(e);
490				},
491			};
492			log_success!("✅ recover_amount submitted successfully {:?}", tx_hash);
493		},
494
495		RecoveryCommands::Close { lost, rescuer, password, password_file } => {
496			let lost_key = crate::wallet::load_keypair_from_wallet(&lost, password, password_file)?;
497			let rescuer_resolved = resolve_address(&rescuer)?;
498			let rescuer_sp = SpAccountId32::from_ss58check(&rescuer_resolved).map_err(|e| {
499				crate::error::QuantusError::Generic(format!("Invalid rescuer address: {e:?}"))
500			})?;
501			let rescuer_bytes: [u8; 32] = *rescuer_sp.as_ref();
502			let rescuer_id = subxt::ext::subxt_core::utils::AccountId32::from(rescuer_bytes);
503			let call = quantus_subxt::api::tx()
504				.recovery()
505				.close_recovery(subxt::ext::subxt_core::utils::MultiAddress::Id(rescuer_id));
506			let tx_hash = crate::cli::common::submit_transaction(
507				&quantus_client,
508				&lost_key,
509				call,
510				None,
511				finalized,
512			)
513			.await
514			.map_err(|e| {
515				crate::error::QuantusError::NetworkError(format!(
516					"Failed to submit close_recovery transaction: {e}"
517				))
518			})?;
519
520			log_print!("📋 Transaction submitted: 0x{}", hex::encode(tx_hash.as_ref()));
521			log_success!("✅ close_recovery submitted successfully");
522		},
523
524		RecoveryCommands::CancelProxy { rescuer, lost, password, password_file } => {
525			let rescuer_key =
526				crate::wallet::load_keypair_from_wallet(&rescuer, password, password_file)?;
527			let lost_resolved = resolve_address(&lost)?;
528			let lost_sp = SpAccountId32::from_ss58check(&lost_resolved).map_err(|e| {
529				crate::error::QuantusError::Generic(format!("Invalid lost address: {e:?}"))
530			})?;
531			let lost_bytes: [u8; 32] = *lost_sp.as_ref();
532			let lost_id = subxt::ext::subxt_core::utils::AccountId32::from(lost_bytes);
533			let call = quantus_subxt::api::tx()
534				.recovery()
535				.cancel_recovered(subxt::ext::subxt_core::utils::MultiAddress::Id(lost_id));
536			let tx_hash = crate::cli::common::submit_transaction(
537				&quantus_client,
538				&rescuer_key,
539				call,
540				None,
541				finalized,
542			)
543			.await
544			.map_err(|e| {
545				crate::error::QuantusError::NetworkError(format!(
546					"Failed to submit cancel_recovered transaction: {e}"
547				))
548			})?;
549
550			log_success!("✅ cancel_recovered submitted successfully {:?}", tx_hash);
551		},
552
553		RecoveryCommands::Active { lost, rescuer } => {
554			let lost_resolved = resolve_address(&lost)?;
555			let rescuer_resolved = resolve_address(&rescuer)?;
556			let lost_sp = SpAccountId32::from_ss58check(&lost_resolved).map_err(|e| {
557				crate::error::QuantusError::Generic(format!("Invalid lost address: {e:?}"))
558			})?;
559			let lost_bytes: [u8; 32] = *lost_sp.as_ref();
560			let lost_id = subxt::ext::subxt_core::utils::AccountId32::from(lost_bytes);
561			let rescuer_sp = SpAccountId32::from_ss58check(&rescuer_resolved).map_err(|e| {
562				crate::error::QuantusError::Generic(format!("Invalid rescuer address: {e:?}"))
563			})?;
564			let rescuer_bytes: [u8; 32] = *rescuer_sp.as_ref();
565			let rescuer_id = subxt::ext::subxt_core::utils::AccountId32::from(rescuer_bytes);
566			let storage_addr =
567				quantus_subxt::api::storage().recovery().active_recoveries(lost_id, rescuer_id);
568			let latest = quantus_client.get_latest_block().await?;
569			let value = quantus_client
570				.client()
571				.storage()
572				.at(latest)
573				.fetch(&storage_addr)
574				.await
575				.map_err(|e| {
576					crate::error::QuantusError::NetworkError(format!("Fetch error: {e:?}"))
577				})?;
578			if let Some(active) = value {
579				log_print!(
580					"{}",
581					serde_json::json!({
582						"created": active.created,
583						"deposit": active.deposit,
584						"friends_vouched": active.friends.0.len(),
585					})
586				);
587			} else {
588				log_print!("{}", serde_json::json!({"active": false}));
589			}
590		},
591
592		RecoveryCommands::ProxyOf { rescuer } => {
593			let rescuer_resolved = resolve_address(&rescuer)?;
594			let rescuer_sp = SpAccountId32::from_ss58check(&rescuer_resolved).map_err(|e| {
595				crate::error::QuantusError::Generic(format!("Invalid rescuer address: {e:?}"))
596			})?;
597			let rescuer_bytes: [u8; 32] = *rescuer_sp.as_ref();
598			let rescuer_id = subxt::ext::subxt_core::utils::AccountId32::from(rescuer_bytes);
599			let storage_addr = quantus_subxt::api::storage().recovery().proxy(rescuer_id);
600			let latest = quantus_client.get_latest_block().await?;
601			let value = quantus_client
602				.client()
603				.storage()
604				.at(latest)
605				.fetch(&storage_addr)
606				.await
607				.map_err(|e| {
608					crate::error::QuantusError::NetworkError(format!("Fetch error: {e:?}"))
609				})?;
610			if let Some(lost_id) = value {
611				log_print!("{}", serde_json::json!({"lost": format!("{}", lost_id)}));
612			} else {
613				log_print!("{}", serde_json::json!({"lost": null}));
614			}
615		},
616
617		RecoveryCommands::Config { account } => {
618			let account_resolved = resolve_address(&account)?;
619			let account_sp = SpAccountId32::from_ss58check(&account_resolved).map_err(|e| {
620				crate::error::QuantusError::Generic(format!("Invalid account address: {e:?}"))
621			})?;
622			let account_bytes: [u8; 32] = *account_sp.as_ref();
623			let account_id = subxt::ext::subxt_core::utils::AccountId32::from(account_bytes);
624			let storage_addr = quantus_subxt::api::storage().recovery().recoverable(account_id);
625			let latest = quantus_client.get_latest_block().await?;
626			let value = quantus_client
627				.client()
628				.storage()
629				.at(latest)
630				.fetch(&storage_addr)
631				.await
632				.map_err(|e| {
633					crate::error::QuantusError::NetworkError(format!("Fetch error: {e:?}"))
634				})?;
635			if let Some(cfg) = value {
636				log_print!(
637					"{}",
638					serde_json::json!({
639						"delay_period": cfg.delay_period,
640						"deposit": cfg.deposit,
641						"friends": cfg.friends.0.iter().map(|f| format!("{f}")).collect::<Vec<_>>(),
642						"threshold": cfg.threshold,
643					})
644				);
645			} else {
646				log_print!("{}", serde_json::json!({"recoverable": false}));
647			}
648		},
649	};
650
651	Ok(())
652}