quantus_cli/cli/
tech_referenda.rs

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