quantus_cli/cli/
tech_referenda.rs

1//! `quantus tech-referenda` subcommand - manage Tech Referenda proposals
2use crate::{
3	chain::quantus_subxt,
4	cli::{common::submit_transaction, progress_spinner::wait_for_tx_confirmation},
5	error::QuantusError,
6	log_error, log_print, log_success, log_verbose,
7};
8use clap::Subcommand;
9use colored::Colorize;
10use std::{path::PathBuf, str::FromStr};
11
12/// Tech Referenda management commands
13#[derive(Subcommand, Debug)]
14pub enum TechReferendaCommands {
15	/// Submit a runtime upgrade proposal to Tech Referenda (requires existing preimage)
16	Submit {
17		/// Preimage hash (must already exist on chain)
18		#[arg(long)]
19		preimage_hash: String,
20
21		/// Wallet name to sign with (must be a Tech Collective member or root)
22		#[arg(short, long)]
23		from: String,
24
25		/// Password for the wallet
26		#[arg(short, long)]
27		password: Option<String>,
28
29		/// Read password from file
30		#[arg(long)]
31		password_file: Option<String>,
32	},
33
34	/// Submit a runtime upgrade proposal to Tech Referenda (creates preimage first)
35	SubmitWithPreimage {
36		/// Path to the runtime WASM file
37		#[arg(short, long)]
38		wasm_file: PathBuf,
39
40		/// Wallet name to sign with (must be a Tech Collective member or root)
41		#[arg(short, long)]
42		from: String,
43
44		/// Password for the wallet
45		#[arg(short, long)]
46		password: Option<String>,
47
48		/// Read password from file
49		#[arg(long)]
50		password_file: Option<String>,
51	},
52
53	/// List all active Tech Referenda proposals
54	List,
55
56	/// Get details of a specific Tech Referendum
57	Get {
58		/// Referendum index
59		#[arg(short, long)]
60		index: u32,
61	},
62
63	/// Check the status of a Tech Referendum
64	Status {
65		/// Referendum index
66		#[arg(short, long)]
67		index: u32,
68	},
69
70	/// Place a decision deposit for a Tech Referendum
71	PlaceDecisionDeposit {
72		/// Referendum index
73		#[arg(short, long)]
74		index: u32,
75
76		/// Wallet name to sign with
77		#[arg(short, long)]
78		from: String,
79
80		/// Password for the wallet
81		#[arg(short, long)]
82		password: Option<String>,
83
84		/// Read password from file
85		#[arg(long)]
86		password_file: Option<String>,
87	},
88
89	/// Cancel a Tech Referendum (requires root permissions)
90	Cancel {
91		/// Referendum index to cancel
92		#[arg(short, long)]
93		index: u32,
94
95		/// Wallet name to sign with (must have root permissions)
96		#[arg(short, long)]
97		from: String,
98
99		/// Password for the wallet
100		#[arg(short, long)]
101		password: Option<String>,
102
103		/// Read password from file
104		#[arg(long)]
105		password_file: Option<String>,
106	},
107
108	/// Kill a Tech Referendum (requires root permissions)
109	Kill {
110		/// Referendum index to kill
111		#[arg(short, long)]
112		index: u32,
113
114		/// Wallet name to sign with (must have root permissions)
115		#[arg(short, long)]
116		from: String,
117
118		/// Password for the wallet
119		#[arg(short, long)]
120		password: Option<String>,
121
122		/// Read password from file
123		#[arg(long)]
124		password_file: Option<String>,
125	},
126
127	/// Nudge a Tech Referendum to next phase (sudo origin)
128	Nudge {
129		/// Referendum index to nudge
130		#[arg(short, long)]
131		index: u32,
132
133		/// Wallet name to sign with
134		#[arg(short, long)]
135		from: String,
136
137		/// Password for the wallet
138		#[arg(short, long)]
139		password: Option<String>,
140
141		/// Read password from file
142		#[arg(long)]
143		password_file: Option<String>,
144	},
145
146	/// Refund submission deposit for a completed Tech Referendum
147	RefundSubmissionDeposit {
148		/// Referendum index
149		#[arg(short, long)]
150		index: u32,
151
152		/// Wallet name that submitted the referendum
153		#[arg(short, long)]
154		from: String,
155
156		/// Password for the wallet
157		#[arg(short, long)]
158		password: Option<String>,
159
160		/// Read password from file
161		#[arg(long)]
162		password_file: Option<String>,
163	},
164
165	/// Refund decision deposit for a completed Tech Referendum
166	RefundDecisionDeposit {
167		/// Referendum index
168		#[arg(short, long)]
169		index: u32,
170
171		/// Wallet name that placed the decision deposit
172		#[arg(short, long)]
173		from: String,
174
175		/// Password for the wallet
176		#[arg(short, long)]
177		password: Option<String>,
178
179		/// Read password from file
180		#[arg(long)]
181		password_file: Option<String>,
182	},
183
184	/// Get Tech Referenda configuration
185	Config,
186}
187
188/// Handle tech referenda commands
189pub async fn handle_tech_referenda_command(
190	command: TechReferendaCommands,
191	node_url: &str,
192) -> crate::error::Result<()> {
193	let quantus_client = crate::chain::client::QuantusClient::new(node_url).await?;
194
195	match command {
196		TechReferendaCommands::Submit { preimage_hash, from, password, password_file } =>
197			submit_runtime_upgrade(&quantus_client, &preimage_hash, &from, password, password_file)
198				.await,
199		TechReferendaCommands::SubmitWithPreimage { wasm_file, from, password, password_file } =>
200			submit_runtime_upgrade_with_preimage(
201				&quantus_client,
202				&wasm_file,
203				&from,
204				password,
205				password_file,
206			)
207			.await,
208		TechReferendaCommands::List => list_proposals(&quantus_client).await,
209		TechReferendaCommands::Get { index } => get_proposal_details(&quantus_client, index).await,
210		TechReferendaCommands::Status { index } =>
211			get_proposal_status(&quantus_client, index).await,
212		TechReferendaCommands::PlaceDecisionDeposit { index, from, password, password_file } =>
213			place_decision_deposit(&quantus_client, index, &from, password, password_file).await,
214		TechReferendaCommands::Cancel { index, from, password, password_file } =>
215			cancel_proposal(&quantus_client, index, &from, password, password_file).await,
216		TechReferendaCommands::Kill { index, from, password, password_file } =>
217			kill_proposal(&quantus_client, index, &from, password, password_file).await,
218		TechReferendaCommands::Nudge { index, from, password, password_file } =>
219			nudge_proposal(&quantus_client, index, &from, password, password_file).await,
220		TechReferendaCommands::RefundSubmissionDeposit { index, from, password, password_file } =>
221			refund_submission_deposit(&quantus_client, index, &from, password, password_file).await,
222		TechReferendaCommands::RefundDecisionDeposit { index, from, password, password_file } =>
223			refund_decision_deposit(&quantus_client, index, &from, password, password_file).await,
224		TechReferendaCommands::Config => get_config(&quantus_client).await,
225	}
226}
227
228/// Submit a runtime upgrade proposal to Tech Referenda (uses existing preimage)
229async fn submit_runtime_upgrade(
230	quantus_client: &crate::chain::client::QuantusClient,
231	preimage_hash: &str,
232	from: &str,
233	password: Option<String>,
234	password_file: Option<String>,
235) -> crate::error::Result<()> {
236	log_print!("📝 Submitting Runtime Upgrade Proposal to Tech Referenda");
237	log_print!("   🔗 Preimage hash: {}", preimage_hash.bright_cyan());
238	log_print!("   🔑 Submitted by: {}", from.bright_yellow());
239
240	// Parse preimage hash (trim 0x)
241	let hash_str = preimage_hash.trim_start_matches("0x");
242	let preimage_hash_parsed: sp_core::H256 = sp_core::H256::from_str(hash_str)
243		.map_err(|_| QuantusError::Generic("Invalid preimage hash format".to_string()))?;
244
245	// Load wallet keypair
246	let keypair = crate::wallet::load_keypair_from_wallet(from, password, password_file)?;
247
248	// Check if preimage exists and get its length
249	log_print!("🔍 Checking preimage status...");
250	let latest_block_hash = quantus_client.get_latest_block().await?;
251	let storage_at = quantus_client.client().storage().at(latest_block_hash);
252
253	let preimage_status = storage_at
254		.fetch(
255			&quantus_subxt::api::storage()
256				.preimage()
257				.request_status_for(preimage_hash_parsed),
258		)
259		.await
260		.map_err(|e| QuantusError::Generic(format!("Failed to fetch preimage status: {:?}", e)))?
261		.ok_or_else(|| QuantusError::Generic("Preimage not found on chain".to_string()))?;
262
263	let preimage_len = match preimage_status {
264		quantus_subxt::api::runtime_types::pallet_preimage::RequestStatus::Unrequested {
265			ticket: _,
266			len,
267		} => len,
268		quantus_subxt::api::runtime_types::pallet_preimage::RequestStatus::Requested {
269			maybe_ticket: _,
270			count: _,
271			maybe_len,
272		} => match maybe_len {
273			Some(len) => len,
274			None => return Err(QuantusError::Generic("Preimage length not available".to_string())),
275		},
276	};
277
278	log_print!("✅ Preimage found! Length: {} bytes", preimage_len);
279
280	// Build TechReferenda::submit call using Lookup preimage reference
281	type ProposalBounded =
282		quantus_subxt::api::runtime_types::frame_support::traits::preimages::Bounded<
283			quantus_subxt::api::runtime_types::quantus_runtime::RuntimeCall,
284			quantus_subxt::api::runtime_types::qp_poseidon::PoseidonHasher,
285		>;
286
287	let preimage_hash_subxt: subxt::utils::H256 = preimage_hash_parsed;
288	let proposal: ProposalBounded =
289		ProposalBounded::Lookup { hash: preimage_hash_subxt, len: preimage_len };
290
291	let raw_origin_root =
292		quantus_subxt::api::runtime_types::frame_support::dispatch::RawOrigin::Root;
293	let origin_caller =
294		quantus_subxt::api::runtime_types::quantus_runtime::OriginCaller::system(raw_origin_root);
295
296	let enactment =
297		quantus_subxt::api::runtime_types::frame_support::traits::schedule::DispatchTime::After(
298			0u32,
299		);
300
301	log_print!("🔧 Creating TechReferenda::submit call...");
302	let submit_call =
303		quantus_subxt::api::tx()
304			.tech_referenda()
305			.submit(origin_caller, proposal, enactment);
306
307	let tx_hash = submit_transaction(quantus_client, &keypair, submit_call, None).await?;
308	log_print!(
309		"✅ {} Runtime upgrade proposal submitted! Hash: {:?}",
310		"SUCCESS".bright_green().bold(),
311		tx_hash
312	);
313
314	let _ = wait_for_tx_confirmation(quantus_client.client(), tx_hash).await?;
315	log_success!("🎉 {} Proposal created!", "FINISHED".bright_green().bold());
316	log_print!("💡 Use 'quantus tech-referenda list' to see active proposals");
317	Ok(())
318}
319
320/// Submit a runtime upgrade proposal to Tech Referenda (creates preimage first)
321async fn submit_runtime_upgrade_with_preimage(
322	quantus_client: &crate::chain::client::QuantusClient,
323	wasm_file: &PathBuf,
324	from: &str,
325	password: Option<String>,
326	password_file: Option<String>,
327) -> crate::error::Result<()> {
328	use qp_poseidon::PoseidonHasher;
329
330	log_print!("📝 Submitting Runtime Upgrade Proposal to Tech Referenda");
331	log_print!("   📂 WASM file: {}", wasm_file.display().to_string().bright_cyan());
332	log_print!("   🔑 Submitted by: {}", from.bright_yellow());
333
334	if !wasm_file.exists() {
335		return Err(QuantusError::Generic(format!("WASM file not found: {}", wasm_file.display())));
336	}
337
338	if let Some(ext) = wasm_file.extension() {
339		if ext != "wasm" {
340			log_verbose!("⚠️  Warning: File doesn't have .wasm extension");
341		}
342	}
343
344	// Read WASM file
345	let wasm_code = std::fs::read(wasm_file)
346		.map_err(|e| QuantusError::Generic(format!("Failed to read WASM file: {}", e)))?;
347
348	log_print!("📊 WASM file size: {} bytes", wasm_code.len());
349
350	// Load wallet keypair
351	let keypair = crate::wallet::load_keypair_from_wallet(from, password, password_file)?;
352
353	// Build a static payload for System::set_code and encode full call data (pallet + call + args)
354	let set_code_payload = quantus_subxt::api::tx().system().set_code(wasm_code.clone());
355	let metadata = quantus_client.client().metadata();
356	let encoded_call = <_ as subxt::tx::Payload>::encode_call_data(&set_code_payload, &metadata)
357		.map_err(|e| QuantusError::Generic(format!("Failed to encode call data: {:?}", e)))?;
358
359	log_verbose!("📝 Encoded call size: {} bytes", encoded_call.len());
360
361	// Compute preimage hash using Poseidon (runtime uses PoseidonHasher)
362	let preimage_hash: sp_core::H256 =
363		<PoseidonHasher as sp_runtime::traits::Hash>::hash(&encoded_call);
364
365	log_print!("🔗 Preimage hash: {:?}", preimage_hash);
366
367	// Submit Preimage::note_preimage with bounded bytes
368	type PreimageBytes = quantus_subxt::api::preimage::calls::types::note_preimage::Bytes;
369	let bounded_bytes: PreimageBytes = encoded_call.clone();
370
371	log_print!("📝 Submitting preimage...");
372	let note_preimage_tx = quantus_subxt::api::tx().preimage().note_preimage(bounded_bytes);
373	let preimage_tx_hash =
374		submit_transaction(quantus_client, &keypair, note_preimage_tx, None).await?;
375	log_print!("✅ Preimage transaction submitted: {:?}", preimage_tx_hash);
376
377	// Wait for preimage transaction confirmation
378	log_print!("⏳ Waiting for preimage transaction confirmation...");
379	let _ = wait_for_tx_confirmation(quantus_client.client(), preimage_tx_hash).await?;
380	log_print!("✅ Preimage transaction confirmed!");
381
382	// Build TechReferenda::submit call using Lookup preimage reference
383	type ProposalBounded =
384		quantus_subxt::api::runtime_types::frame_support::traits::preimages::Bounded<
385			quantus_subxt::api::runtime_types::quantus_runtime::RuntimeCall,
386			quantus_subxt::api::runtime_types::qp_poseidon::PoseidonHasher,
387		>;
388
389	let preimage_hash_subxt: subxt::utils::H256 = preimage_hash;
390	let proposal: ProposalBounded =
391		ProposalBounded::Lookup { hash: preimage_hash_subxt, len: encoded_call.len() as u32 };
392
393	let raw_origin_root =
394		quantus_subxt::api::runtime_types::frame_support::dispatch::RawOrigin::Root;
395	let origin_caller =
396		quantus_subxt::api::runtime_types::quantus_runtime::OriginCaller::system(raw_origin_root);
397
398	let enactment =
399		quantus_subxt::api::runtime_types::frame_support::traits::schedule::DispatchTime::After(
400			0u32,
401		);
402
403	log_print!("🔧 Creating TechReferenda::submit call...");
404	let submit_call =
405		quantus_subxt::api::tx()
406			.tech_referenda()
407			.submit(origin_caller, proposal, enactment);
408
409	let tx_hash = submit_transaction(quantus_client, &keypair, submit_call, None).await?;
410	log_print!(
411		"✅ {} Runtime upgrade proposal submitted! Hash: {:?}",
412		"SUCCESS".bright_green().bold(),
413		tx_hash
414	);
415
416	let _ = wait_for_tx_confirmation(quantus_client.client(), tx_hash).await?;
417	log_success!("🎉 {} Proposal created!", "FINISHED".bright_green().bold());
418	log_print!("💡 Use 'quantus tech-referenda list' to see active proposals");
419	Ok(())
420}
421
422/// List recent Tech Referenda proposals
423async fn list_proposals(
424	quantus_client: &crate::chain::client::QuantusClient,
425) -> crate::error::Result<()> {
426	log_print!("📜 Active Tech Referenda Proposals");
427	log_print!("");
428
429	let addr = quantus_subxt::api::storage().tech_referenda().referendum_count();
430
431	// Get the latest block hash to read from the latest state (not finalized)
432	let latest_block_hash = quantus_client.get_latest_block().await?;
433	let storage_at = quantus_client.client().storage().at(latest_block_hash);
434
435	let count = storage_at.fetch(&addr).await?;
436
437	if let Some(total) = count {
438		log_print!("📊 Total referenda created: {}", total);
439		if total == 0 {
440			log_print!("📭 No active proposals found");
441			return Ok(());
442		}
443		log_print!("🔍 Fetching recent referenda...");
444		for i in (0..total).rev().take(10) {
445			get_proposal_status(quantus_client, i).await?;
446			log_print!("----------------------------------------");
447		}
448	} else {
449		log_print!("📭 No referenda found - Tech Referenda may be empty");
450	}
451
452	Ok(())
453}
454
455/// Get details of a specific Tech Referendum
456async fn get_proposal_details(
457	quantus_client: &crate::chain::client::QuantusClient,
458	index: u32,
459) -> crate::error::Result<()> {
460	log_print!("📄 Tech Referendum #{} Details", index);
461	log_print!("");
462
463	let addr = quantus_subxt::api::storage().tech_referenda().referendum_info_for(index);
464
465	// Get the latest block hash to read from the latest state (not finalized)
466	let latest_block_hash = quantus_client.get_latest_block().await?;
467	let storage_at = quantus_client.client().storage().at(latest_block_hash);
468
469	let info = storage_at.fetch(&addr).await?;
470
471	if let Some(referendum_info) = info {
472		log_print!("📋 Referendum Information (raw):");
473		log_print!("{:#?}", referendum_info);
474	} else {
475		log_print!("📭 Referendum #{} not found", index);
476	}
477	Ok(())
478}
479
480/// Get the status of a Tech Referendum
481async fn get_proposal_status(
482	quantus_client: &crate::chain::client::QuantusClient,
483	index: u32,
484) -> crate::error::Result<()> {
485	use quantus_subxt::api::runtime_types::pallet_referenda::types::ReferendumInfo;
486
487	log_verbose!("📊 Fetching status for Tech Referendum #{}...", index);
488
489	let addr = quantus_subxt::api::storage().tech_referenda().referendum_info_for(index);
490
491	// Get the latest block hash to read from the latest state (not finalized)
492	let latest_block_hash = quantus_client.get_latest_block().await?;
493	let storage_at = quantus_client.client().storage().at(latest_block_hash);
494
495	let info_res = storage_at.fetch(&addr).await;
496
497	match info_res {
498		Ok(Some(info)) => {
499			log_print!("📊 Status for Referendum #{}", index.to_string().bright_yellow());
500			match info {
501				ReferendumInfo::Ongoing(status) => {
502					log_print!("   - Status: {}", "Ongoing".bright_green());
503					log_print!("   - Track: {}", status.track);
504					log_print!("   - Submitted at: block {}", status.submitted);
505					log_print!(
506						"   - Tally: Ayes: {}, Nays: {}",
507						status.tally.ayes,
508						status.tally.nays
509					);
510					log_verbose!("   - Full status: {:#?}", status);
511				},
512				ReferendumInfo::Approved(submitted, ..) => {
513					log_print!("   - Status: {}", "Approved".green());
514					log_print!("   - Submitted at block: {}", submitted);
515				},
516				ReferendumInfo::Rejected(submitted, ..) => {
517					log_print!("   - Status: {}", "Rejected".red());
518					log_print!("   - Submitted at block: {}", submitted);
519				},
520				ReferendumInfo::Cancelled(submitted, ..) => {
521					log_print!("   - Status: {}", "Cancelled".yellow());
522					log_print!("   - Submitted at block: {}", submitted);
523				},
524				ReferendumInfo::TimedOut(submitted, ..) => {
525					log_print!("   - Status: {}", "TimedOut".dimmed());
526					log_print!("   - Submitted at block: {}", submitted);
527				},
528				ReferendumInfo::Killed(submitted) => {
529					log_print!("   - Status: {}", "Killed".red().bold());
530					log_print!("   - Killed at block: {}", submitted);
531				},
532			}
533		},
534		Ok(None) => log_print!("📭 Referendum #{} not found", index),
535		Err(e) => log_error!("❌ Failed to fetch referendum #{}: {:?}", index, e),
536	}
537
538	Ok(())
539}
540
541/// Place a decision deposit for a Tech Referendum
542async fn place_decision_deposit(
543	quantus_client: &crate::chain::client::QuantusClient,
544	index: u32,
545	from: &str,
546	password: Option<String>,
547	password_file: Option<String>,
548) -> crate::error::Result<()> {
549	log_print!("📋 Placing decision deposit for Tech Referendum #{}", index);
550	log_print!("   🔑 Placed by: {}", from.bright_yellow());
551
552	let keypair = crate::wallet::load_keypair_from_wallet(from, password, password_file)?;
553
554	let deposit_call = quantus_subxt::api::tx().tech_referenda().place_decision_deposit(index);
555	let tx_hash = submit_transaction(quantus_client, &keypair, deposit_call, None).await?;
556	log_success!("✅ Decision deposit placed! Hash: {:?}", tx_hash.to_string().bright_yellow());
557	let _ = wait_for_tx_confirmation(quantus_client.client(), tx_hash).await?;
558	Ok(())
559}
560
561/// Cancel a Tech Referendum (sudo)
562async fn cancel_proposal(
563	quantus_client: &crate::chain::client::QuantusClient,
564	index: u32,
565	from: &str,
566	password: Option<String>,
567	password_file: Option<String>,
568) -> crate::error::Result<()> {
569	log_print!("❌ Cancelling Tech Referendum #{}", index);
570	log_print!("   🔑 Cancelled by: {}", from.bright_yellow());
571
572	let keypair = crate::wallet::load_keypair_from_wallet(from, password, password_file)?;
573
574	let inner =
575		quantus_subxt::api::Call::TechReferenda(quantus_subxt::api::tech_referenda::Call::cancel {
576			index,
577		});
578	let sudo_call = quantus_subxt::api::tx().sudo().sudo(inner);
579
580	let tx_hash = submit_transaction(quantus_client, &keypair, sudo_call, None).await?;
581	log_success!("✅ Referendum cancelled! Hash: {:?}", tx_hash.to_string().bright_yellow());
582	let _ = wait_for_tx_confirmation(quantus_client.client(), tx_hash).await?;
583	Ok(())
584}
585
586/// Kill a Tech Referendum (sudo)
587async fn kill_proposal(
588	quantus_client: &crate::chain::client::QuantusClient,
589	index: u32,
590	from: &str,
591	password: Option<String>,
592	password_file: Option<String>,
593) -> crate::error::Result<()> {
594	log_print!("💀 Killing Tech Referendum #{}", index);
595	log_print!("   🔑 Killed by: {}", from.bright_yellow());
596
597	let keypair = crate::wallet::load_keypair_from_wallet(from, password, password_file)?;
598
599	let inner =
600		quantus_subxt::api::Call::TechReferenda(quantus_subxt::api::tech_referenda::Call::kill {
601			index,
602		});
603	let sudo_call = quantus_subxt::api::tx().sudo().sudo(inner);
604
605	let tx_hash = submit_transaction(quantus_client, &keypair, sudo_call, None).await?;
606	log_success!("✅ Referendum killed! Hash: {:?}", tx_hash.to_string().bright_yellow());
607	let _ = wait_for_tx_confirmation(quantus_client.client(), tx_hash).await?;
608	Ok(())
609}
610
611/// Nudge a Tech Referendum to next phase (sudo)
612async fn nudge_proposal(
613	quantus_client: &crate::chain::client::QuantusClient,
614	index: u32,
615	from: &str,
616	password: Option<String>,
617	password_file: Option<String>,
618) -> crate::error::Result<()> {
619	log_print!("🔄 Nudging Tech Referendum #{}", index);
620	log_print!("   🔑 Nudged by: {}", from.bright_yellow());
621
622	let keypair = crate::wallet::load_keypair_from_wallet(from, password, password_file)?;
623
624	let inner = quantus_subxt::api::Call::TechReferenda(
625		quantus_subxt::api::tech_referenda::Call::nudge_referendum { index },
626	);
627	let sudo_call = quantus_subxt::api::tx().sudo().sudo(inner);
628
629	let tx_hash = submit_transaction(quantus_client, &keypair, sudo_call, None).await?;
630	log_success!("✅ Referendum nudged! Hash: {:?}", tx_hash.to_string().bright_yellow());
631	let _ = wait_for_tx_confirmation(quantus_client.client(), tx_hash).await?;
632	Ok(())
633}
634
635/// Get Tech Referenda configuration
636async fn get_config(
637	quantus_client: &crate::chain::client::QuantusClient,
638) -> crate::error::Result<()> {
639	log_print!("⚙️  Tech Referenda Configuration");
640	log_print!("");
641
642	let constants = quantus_client.client().constants();
643	let tracks_addr = quantus_subxt::api::constants().tech_referenda().tracks();
644
645	match constants.at(&tracks_addr) {
646		Ok(tracks) => {
647			log_print!("{}", "📊 Track Configuration:".bold());
648			for (id, info) in tracks.iter() {
649				log_print!("   ------------------------------------");
650				log_print!(
651					"   • {} #{}: {}",
652					"Track".bold(),
653					id,
654					info.name.to_string().bright_cyan()
655				);
656				log_print!("   • Max Deciding: {}", info.max_deciding);
657				log_print!("   • Decision Deposit: {}", info.decision_deposit);
658				log_print!("   • Prepare Period: {} blocks", info.prepare_period);
659				log_print!("   • Decision Period: {} blocks", info.decision_period);
660				log_print!("   • Confirm Period: {} blocks", info.confirm_period);
661				log_print!("   • Min Enactment Period: {} blocks", info.min_enactment_period);
662			}
663			log_print!("   ------------------------------------");
664		},
665		Err(e) => {
666			log_error!("❌ Failed to decode Tracks constant: {:?}", e);
667			log_print!("💡 It's possible the Tracks constant is not in the expected format.");
668		},
669	}
670
671	Ok(())
672}
673
674/// Refund submission deposit for a completed Tech Referendum
675async fn refund_submission_deposit(
676	quantus_client: &crate::chain::client::QuantusClient,
677	index: u32,
678	from: &str,
679	password: Option<String>,
680	password_file: Option<String>,
681) -> crate::error::Result<()> {
682	log_print!("💰 Refunding submission deposit for Tech Referendum #{}", index);
683	log_print!("   🔑 Refund to: {}", from.bright_yellow());
684
685	// Load wallet keypair
686	let keypair = crate::wallet::load_keypair_from_wallet(from, password, password_file)?;
687
688	// Create refund_submission_deposit call for TechReferenda instance
689	let refund_call = quantus_subxt::api::tx().tech_referenda().refund_submission_deposit(index);
690
691	let tx_hash = submit_transaction(quantus_client, &keypair, refund_call, None).await?;
692	log_print!(
693		"✅ {} Refund transaction submitted! Hash: {:?}",
694		"SUCCESS".bright_green().bold(),
695		tx_hash
696	);
697
698	let _ = wait_for_tx_confirmation(quantus_client.client(), tx_hash).await?;
699	log_success!("🎉 {} Submission deposit refunded!", "FINISHED".bright_green().bold());
700	log_print!("💡 Check your balance to confirm the refund");
701	Ok(())
702}
703
704/// Refund decision deposit for a completed Tech Referendum
705async fn refund_decision_deposit(
706	quantus_client: &crate::chain::client::QuantusClient,
707	index: u32,
708	from: &str,
709	password: Option<String>,
710	password_file: Option<String>,
711) -> crate::error::Result<()> {
712	log_print!("💰 Refunding decision deposit for Tech Referendum #{}", index);
713	log_print!("   🔑 Refund to: {}", from.bright_yellow());
714
715	// Load wallet keypair
716	let keypair = crate::wallet::load_keypair_from_wallet(from, password, password_file)?;
717
718	// Create refund_decision_deposit call for TechReferenda instance
719	let refund_call = quantus_subxt::api::tx().tech_referenda().refund_decision_deposit(index);
720
721	let tx_hash = submit_transaction(quantus_client, &keypair, refund_call, None).await?;
722	log_print!(
723		"✅ {} Refund transaction submitted! Hash: {:?}",
724		"SUCCESS".bright_green().bold(),
725		tx_hash
726	);
727
728	let _ = wait_for_tx_confirmation(quantus_client.client(), tx_hash).await?;
729	log_success!("🎉 {} Decision deposit refunded!", "FINISHED".bright_green().bold());
730	log_print!("💡 Check your balance to confirm the refund");
731	Ok(())
732}