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) -> crate::error::Result<()> {
191	let quantus_client = crate::chain::client::QuantusClient::new(node_url).await?;
192
193	match command {
194		TechReferendaCommands::Submit { preimage_hash, from, password, password_file } =>
195			submit_runtime_upgrade(&quantus_client, &preimage_hash, &from, password, password_file)
196				.await,
197		TechReferendaCommands::SubmitWithPreimage { wasm_file, from, password, password_file } =>
198			submit_runtime_upgrade_with_preimage(
199				&quantus_client,
200				&wasm_file,
201				&from,
202				password,
203				password_file,
204			)
205			.await,
206		TechReferendaCommands::List => list_proposals(&quantus_client).await,
207		TechReferendaCommands::Get { index } => get_proposal_details(&quantus_client, index).await,
208		TechReferendaCommands::Status { index } =>
209			get_proposal_status(&quantus_client, index).await,
210		TechReferendaCommands::PlaceDecisionDeposit { index, from, password, password_file } =>
211			place_decision_deposit(&quantus_client, index, &from, password, password_file).await,
212		TechReferendaCommands::Cancel { index, from, password, password_file } =>
213			cancel_proposal(&quantus_client, index, &from, password, password_file).await,
214		TechReferendaCommands::Kill { index, from, password, password_file } =>
215			kill_proposal(&quantus_client, index, &from, password, password_file).await,
216		TechReferendaCommands::Nudge { index, from, password, password_file } =>
217			nudge_proposal(&quantus_client, index, &from, password, password_file).await,
218		TechReferendaCommands::RefundSubmissionDeposit { index, from, password, password_file } =>
219			refund_submission_deposit(&quantus_client, index, &from, password, password_file).await,
220		TechReferendaCommands::RefundDecisionDeposit { index, from, password, password_file } =>
221			refund_decision_deposit(&quantus_client, index, &from, password, password_file).await,
222		TechReferendaCommands::Config => get_config(&quantus_client).await,
223	}
224}
225
226/// Submit a runtime upgrade proposal to Tech Referenda (uses existing preimage)
227async fn submit_runtime_upgrade(
228	quantus_client: &crate::chain::client::QuantusClient,
229	preimage_hash: &str,
230	from: &str,
231	password: Option<String>,
232	password_file: Option<String>,
233) -> crate::error::Result<()> {
234	log_print!("📝 Submitting Runtime Upgrade Proposal to Tech Referenda");
235	log_print!("   🔗 Preimage hash: {}", preimage_hash.bright_cyan());
236	log_print!("   🔑 Submitted by: {}", from.bright_yellow());
237
238	// Parse preimage hash (trim 0x)
239	let hash_str = preimage_hash.trim_start_matches("0x");
240	let preimage_hash_parsed: sp_core::H256 = sp_core::H256::from_str(hash_str)
241		.map_err(|_| QuantusError::Generic("Invalid preimage hash format".to_string()))?;
242
243	// Load wallet keypair
244	let keypair = crate::wallet::load_keypair_from_wallet(from, password, password_file)?;
245
246	// Check if preimage exists and get its length
247	log_print!("🔍 Checking preimage status...");
248	let latest_block_hash = quantus_client.get_latest_block().await?;
249	let storage_at = quantus_client.client().storage().at(latest_block_hash);
250
251	let preimage_status = storage_at
252		.fetch(
253			&quantus_subxt::api::storage()
254				.preimage()
255				.request_status_for(preimage_hash_parsed),
256		)
257		.await
258		.map_err(|e| QuantusError::Generic(format!("Failed to fetch preimage status: {:?}", e)))?
259		.ok_or_else(|| QuantusError::Generic("Preimage not found on chain".to_string()))?;
260
261	let preimage_len = match preimage_status {
262		quantus_subxt::api::runtime_types::pallet_preimage::RequestStatus::Unrequested {
263			ticket: _,
264			len,
265		} => len,
266		quantus_subxt::api::runtime_types::pallet_preimage::RequestStatus::Requested {
267			maybe_ticket: _,
268			count: _,
269			maybe_len,
270		} => match maybe_len {
271			Some(len) => len,
272			None => return Err(QuantusError::Generic("Preimage length not available".to_string())),
273		},
274	};
275
276	log_print!("✅ Preimage found! Length: {} bytes", preimage_len);
277
278	// Build TechReferenda::submit call using Lookup preimage reference
279	type ProposalBounded =
280		quantus_subxt::api::runtime_types::frame_support::traits::preimages::Bounded<
281			quantus_subxt::api::runtime_types::quantus_runtime::RuntimeCall,
282			quantus_subxt::api::runtime_types::qp_poseidon::PoseidonHasher,
283		>;
284
285	let preimage_hash_subxt: subxt::utils::H256 = preimage_hash_parsed;
286	let proposal: ProposalBounded =
287		ProposalBounded::Lookup { hash: preimage_hash_subxt, len: preimage_len };
288
289	let raw_origin_root =
290		quantus_subxt::api::runtime_types::frame_support::dispatch::RawOrigin::Root;
291	let origin_caller =
292		quantus_subxt::api::runtime_types::quantus_runtime::OriginCaller::system(raw_origin_root);
293
294	let enactment =
295		quantus_subxt::api::runtime_types::frame_support::traits::schedule::DispatchTime::After(
296			0u32,
297		);
298
299	log_print!("🔧 Creating TechReferenda::submit call...");
300	let submit_call =
301		quantus_subxt::api::tx()
302			.tech_referenda()
303			.submit(origin_caller, proposal, enactment);
304
305	let tx_hash = submit_transaction(quantus_client, &keypair, submit_call, None).await?;
306	log_print!(
307		"✅ {} Runtime upgrade proposal submitted! Hash: {:?}",
308		"SUCCESS".bright_green().bold(),
309		tx_hash
310	);
311
312	log_print!("💡 Use 'quantus tech-referenda list' to see active proposals");
313	Ok(())
314}
315
316/// Submit a runtime upgrade proposal to Tech Referenda (creates preimage first)
317async fn submit_runtime_upgrade_with_preimage(
318	quantus_client: &crate::chain::client::QuantusClient,
319	wasm_file: &PathBuf,
320	from: &str,
321	password: Option<String>,
322	password_file: Option<String>,
323) -> crate::error::Result<()> {
324	use qp_poseidon::PoseidonHasher;
325
326	log_print!("📝 Submitting Runtime Upgrade Proposal to Tech Referenda");
327	log_print!("   📂 WASM file: {}", wasm_file.display().to_string().bright_cyan());
328	log_print!("   🔑 Submitted by: {}", from.bright_yellow());
329
330	if !wasm_file.exists() {
331		return Err(QuantusError::Generic(format!("WASM file not found: {}", wasm_file.display())));
332	}
333
334	if let Some(ext) = wasm_file.extension() {
335		if ext != "wasm" {
336			log_verbose!("⚠️  Warning: File doesn't have .wasm extension");
337		}
338	}
339
340	// Read WASM file
341	let wasm_code = std::fs::read(wasm_file)
342		.map_err(|e| QuantusError::Generic(format!("Failed to read WASM file: {}", e)))?;
343
344	log_print!("📊 WASM file size: {} bytes", wasm_code.len());
345
346	// Load wallet keypair
347	let keypair = crate::wallet::load_keypair_from_wallet(from, password, password_file)?;
348
349	// Build a static payload for System::set_code and encode full call data (pallet + call + args)
350	let set_code_payload = quantus_subxt::api::tx().system().set_code(wasm_code.clone());
351	let metadata = quantus_client.client().metadata();
352	let encoded_call = <_ as subxt::tx::Payload>::encode_call_data(&set_code_payload, &metadata)
353		.map_err(|e| QuantusError::Generic(format!("Failed to encode call data: {:?}", e)))?;
354
355	log_verbose!("📝 Encoded call size: {} bytes", encoded_call.len());
356
357	// Compute preimage hash using Poseidon (runtime uses PoseidonHasher)
358	let preimage_hash: sp_core::H256 =
359		<PoseidonHasher as sp_runtime::traits::Hash>::hash(&encoded_call);
360
361	log_print!("🔗 Preimage hash: {:?}", preimage_hash);
362
363	// Submit Preimage::note_preimage with bounded bytes
364	type PreimageBytes = quantus_subxt::api::preimage::calls::types::note_preimage::Bytes;
365	let bounded_bytes: PreimageBytes = encoded_call.clone();
366
367	log_print!("📝 Submitting preimage...");
368	let note_preimage_tx = quantus_subxt::api::tx().preimage().note_preimage(bounded_bytes);
369	let preimage_tx_hash =
370		submit_transaction(quantus_client, &keypair, note_preimage_tx, None).await?;
371	log_print!("✅ Preimage transaction submitted: {:?}", preimage_tx_hash);
372
373	// Wait for preimage transaction confirmation
374	log_print!("⏳ Waiting for preimage transaction confirmation...");
375
376	// Build TechReferenda::submit call using Lookup preimage reference
377	type ProposalBounded =
378		quantus_subxt::api::runtime_types::frame_support::traits::preimages::Bounded<
379			quantus_subxt::api::runtime_types::quantus_runtime::RuntimeCall,
380			quantus_subxt::api::runtime_types::qp_poseidon::PoseidonHasher,
381		>;
382
383	let preimage_hash_subxt: subxt::utils::H256 = preimage_hash;
384	let proposal: ProposalBounded =
385		ProposalBounded::Lookup { hash: preimage_hash_subxt, len: encoded_call.len() as u32 };
386
387	let raw_origin_root =
388		quantus_subxt::api::runtime_types::frame_support::dispatch::RawOrigin::Root;
389	let origin_caller =
390		quantus_subxt::api::runtime_types::quantus_runtime::OriginCaller::system(raw_origin_root);
391
392	let enactment =
393		quantus_subxt::api::runtime_types::frame_support::traits::schedule::DispatchTime::After(
394			0u32,
395		);
396
397	log_print!("🔧 Creating TechReferenda::submit call...");
398	let submit_call =
399		quantus_subxt::api::tx()
400			.tech_referenda()
401			.submit(origin_caller, proposal, enactment);
402
403	let tx_hash = submit_transaction(quantus_client, &keypair, submit_call, None).await?;
404	log_print!(
405		"✅ {} Runtime upgrade proposal submitted! Hash: {:?}",
406		"SUCCESS".bright_green().bold(),
407		tx_hash
408	);
409
410	log_print!("💡 Use 'quantus tech-referenda list' to see active proposals");
411	Ok(())
412}
413
414/// List recent Tech Referenda proposals
415async fn list_proposals(
416	quantus_client: &crate::chain::client::QuantusClient,
417) -> crate::error::Result<()> {
418	log_print!("📜 Active Tech Referenda Proposals");
419	log_print!("");
420
421	let addr = quantus_subxt::api::storage().tech_referenda().referendum_count();
422
423	// Get the latest block hash to read from the latest state (not finalized)
424	let latest_block_hash = quantus_client.get_latest_block().await?;
425	let storage_at = quantus_client.client().storage().at(latest_block_hash);
426
427	let count = storage_at.fetch(&addr).await?;
428
429	if let Some(total) = count {
430		log_print!("📊 Total referenda created: {}", total);
431		if total == 0 {
432			log_print!("📭 No active proposals found");
433			return Ok(());
434		}
435		log_print!("🔍 Fetching recent referenda...");
436		for i in (0..total).rev().take(10) {
437			get_proposal_status(quantus_client, i).await?;
438			log_print!("----------------------------------------");
439		}
440	} else {
441		log_print!("📭 No referenda found - Tech Referenda may be empty");
442	}
443
444	Ok(())
445}
446
447/// Get details of a specific Tech Referendum
448async fn get_proposal_details(
449	quantus_client: &crate::chain::client::QuantusClient,
450	index: u32,
451) -> crate::error::Result<()> {
452	log_print!("📄 Tech Referendum #{} Details", index);
453	log_print!("");
454
455	let addr = quantus_subxt::api::storage().tech_referenda().referendum_info_for(index);
456
457	// Get the latest block hash to read from the latest state (not finalized)
458	let latest_block_hash = quantus_client.get_latest_block().await?;
459	let storage_at = quantus_client.client().storage().at(latest_block_hash);
460
461	let info = storage_at.fetch(&addr).await?;
462
463	if let Some(referendum_info) = info {
464		log_print!("📋 Referendum Information (raw):");
465		log_print!("{:#?}", referendum_info);
466	} else {
467		log_print!("📭 Referendum #{} not found", index);
468	}
469	Ok(())
470}
471
472/// Get the status of a Tech Referendum
473async fn get_proposal_status(
474	quantus_client: &crate::chain::client::QuantusClient,
475	index: u32,
476) -> crate::error::Result<()> {
477	use quantus_subxt::api::runtime_types::pallet_referenda::types::ReferendumInfo;
478
479	log_verbose!("📊 Fetching status for Tech Referendum #{}...", index);
480
481	let addr = quantus_subxt::api::storage().tech_referenda().referendum_info_for(index);
482
483	// Get the latest block hash to read from the latest state (not finalized)
484	let latest_block_hash = quantus_client.get_latest_block().await?;
485	let storage_at = quantus_client.client().storage().at(latest_block_hash);
486
487	let info_res = storage_at.fetch(&addr).await;
488
489	match info_res {
490		Ok(Some(info)) => {
491			log_print!("📊 Status for Referendum #{}", index.to_string().bright_yellow());
492			match info {
493				ReferendumInfo::Ongoing(status) => {
494					log_print!("   - Status: {}", "Ongoing".bright_green());
495					log_print!("   - Track: {}", status.track);
496					log_print!("   - Submitted at: block {}", status.submitted);
497					log_print!(
498						"   - Tally: Ayes: {}, Nays: {}",
499						status.tally.ayes,
500						status.tally.nays
501					);
502					log_verbose!("   - Full status: {:#?}", status);
503				},
504				ReferendumInfo::Approved(submitted, ..) => {
505					log_print!("   - Status: {}", "Approved".green());
506					log_print!("   - Submitted at block: {}", submitted);
507				},
508				ReferendumInfo::Rejected(submitted, ..) => {
509					log_print!("   - Status: {}", "Rejected".red());
510					log_print!("   - Submitted at block: {}", submitted);
511				},
512				ReferendumInfo::Cancelled(submitted, ..) => {
513					log_print!("   - Status: {}", "Cancelled".yellow());
514					log_print!("   - Submitted at block: {}", submitted);
515				},
516				ReferendumInfo::TimedOut(submitted, ..) => {
517					log_print!("   - Status: {}", "TimedOut".dimmed());
518					log_print!("   - Submitted at block: {}", submitted);
519				},
520				ReferendumInfo::Killed(submitted) => {
521					log_print!("   - Status: {}", "Killed".red().bold());
522					log_print!("   - Killed at block: {}", submitted);
523				},
524			}
525		},
526		Ok(None) => log_print!("📭 Referendum #{} not found", index),
527		Err(e) => log_error!("❌ Failed to fetch referendum #{}: {:?}", index, e),
528	}
529
530	Ok(())
531}
532
533/// Place a decision deposit for a Tech Referendum
534async fn place_decision_deposit(
535	quantus_client: &crate::chain::client::QuantusClient,
536	index: u32,
537	from: &str,
538	password: Option<String>,
539	password_file: Option<String>,
540) -> crate::error::Result<()> {
541	log_print!("📋 Placing decision deposit for Tech Referendum #{}", index);
542	log_print!("   🔑 Placed by: {}", from.bright_yellow());
543
544	let keypair = crate::wallet::load_keypair_from_wallet(from, password, password_file)?;
545
546	let deposit_call = quantus_subxt::api::tx().tech_referenda().place_decision_deposit(index);
547	let tx_hash = submit_transaction(quantus_client, &keypair, deposit_call, None).await?;
548	log_success!("✅ Decision deposit placed! Hash: {:?}", tx_hash.to_string().bright_yellow());
549	Ok(())
550}
551
552/// Cancel a Tech Referendum (sudo)
553async fn cancel_proposal(
554	quantus_client: &crate::chain::client::QuantusClient,
555	index: u32,
556	from: &str,
557	password: Option<String>,
558	password_file: Option<String>,
559) -> crate::error::Result<()> {
560	log_print!("❌ Cancelling Tech Referendum #{}", index);
561	log_print!("   🔑 Cancelled by: {}", from.bright_yellow());
562
563	let keypair = crate::wallet::load_keypair_from_wallet(from, password, password_file)?;
564
565	let inner =
566		quantus_subxt::api::Call::TechReferenda(quantus_subxt::api::tech_referenda::Call::cancel {
567			index,
568		});
569	let sudo_call = quantus_subxt::api::tx().sudo().sudo(inner);
570
571	let tx_hash = submit_transaction(quantus_client, &keypair, sudo_call, None).await?;
572	log_success!("✅ Referendum cancelled! Hash: {:?}", tx_hash.to_string().bright_yellow());
573	Ok(())
574}
575
576/// Kill a Tech Referendum (sudo)
577async fn kill_proposal(
578	quantus_client: &crate::chain::client::QuantusClient,
579	index: u32,
580	from: &str,
581	password: Option<String>,
582	password_file: Option<String>,
583) -> crate::error::Result<()> {
584	log_print!("💀 Killing Tech Referendum #{}", index);
585	log_print!("   🔑 Killed by: {}", from.bright_yellow());
586
587	let keypair = crate::wallet::load_keypair_from_wallet(from, password, password_file)?;
588
589	let inner =
590		quantus_subxt::api::Call::TechReferenda(quantus_subxt::api::tech_referenda::Call::kill {
591			index,
592		});
593	let sudo_call = quantus_subxt::api::tx().sudo().sudo(inner);
594
595	let tx_hash = submit_transaction(quantus_client, &keypair, sudo_call, None).await?;
596	log_success!("✅ Referendum killed! Hash: {:?}", tx_hash.to_string().bright_yellow());
597	Ok(())
598}
599
600/// Nudge a Tech Referendum to next phase (sudo)
601async fn nudge_proposal(
602	quantus_client: &crate::chain::client::QuantusClient,
603	index: u32,
604	from: &str,
605	password: Option<String>,
606	password_file: Option<String>,
607) -> crate::error::Result<()> {
608	log_print!("🔄 Nudging Tech Referendum #{}", index);
609	log_print!("   🔑 Nudged by: {}", from.bright_yellow());
610
611	let keypair = crate::wallet::load_keypair_from_wallet(from, password, password_file)?;
612
613	let inner = quantus_subxt::api::Call::TechReferenda(
614		quantus_subxt::api::tech_referenda::Call::nudge_referendum { index },
615	);
616	let sudo_call = quantus_subxt::api::tx().sudo().sudo(inner);
617
618	let tx_hash = submit_transaction(quantus_client, &keypair, sudo_call, None).await?;
619	log_success!("✅ Referendum nudged! Hash: {:?}", tx_hash.to_string().bright_yellow());
620	Ok(())
621}
622
623/// Get Tech Referenda configuration
624async fn get_config(
625	quantus_client: &crate::chain::client::QuantusClient,
626) -> crate::error::Result<()> {
627	log_print!("⚙️  Tech Referenda Configuration");
628	log_print!("");
629
630	let constants = quantus_client.client().constants();
631	let tracks_addr = quantus_subxt::api::constants().tech_referenda().tracks();
632
633	match constants.at(&tracks_addr) {
634		Ok(tracks) => {
635			log_print!("{}", "📊 Track Configuration:".bold());
636			for (id, info) in tracks.iter() {
637				log_print!("   ------------------------------------");
638				log_print!(
639					"   • {} #{}: {}",
640					"Track".bold(),
641					id,
642					info.name.to_string().bright_cyan()
643				);
644				log_print!("   • Max Deciding: {}", info.max_deciding);
645				log_print!("   • Decision Deposit: {}", info.decision_deposit);
646				log_print!("   • Prepare Period: {} blocks", info.prepare_period);
647				log_print!("   • Decision Period: {} blocks", info.decision_period);
648				log_print!("   • Confirm Period: {} blocks", info.confirm_period);
649				log_print!("   • Min Enactment Period: {} blocks", info.min_enactment_period);
650			}
651			log_print!("   ------------------------------------");
652		},
653		Err(e) => {
654			log_error!("❌ Failed to decode Tracks constant: {:?}", e);
655			log_print!("💡 It's possible the Tracks constant is not in the expected format.");
656		},
657	}
658
659	Ok(())
660}
661
662/// Refund submission deposit for a completed Tech Referendum
663async fn refund_submission_deposit(
664	quantus_client: &crate::chain::client::QuantusClient,
665	index: u32,
666	from: &str,
667	password: Option<String>,
668	password_file: Option<String>,
669) -> crate::error::Result<()> {
670	log_print!("💰 Refunding submission deposit for Tech Referendum #{}", index);
671	log_print!("   🔑 Refund to: {}", from.bright_yellow());
672
673	// Load wallet keypair
674	let keypair = crate::wallet::load_keypair_from_wallet(from, password, password_file)?;
675
676	// Create refund_submission_deposit call for TechReferenda instance
677	let refund_call = quantus_subxt::api::tx().tech_referenda().refund_submission_deposit(index);
678
679	let tx_hash = submit_transaction(quantus_client, &keypair, refund_call, None).await?;
680	log_print!(
681		"✅ {} Refund transaction submitted! Hash: {:?}",
682		"SUCCESS".bright_green().bold(),
683		tx_hash
684	);
685
686	log_success!("🎉 {} Submission deposit refunded!", "FINISHED".bright_green().bold());
687	log_print!("💡 Check your balance to confirm the refund");
688	Ok(())
689}
690
691/// Refund decision deposit for a completed Tech Referendum
692async fn refund_decision_deposit(
693	quantus_client: &crate::chain::client::QuantusClient,
694	index: u32,
695	from: &str,
696	password: Option<String>,
697	password_file: Option<String>,
698) -> crate::error::Result<()> {
699	log_print!("💰 Refunding decision deposit for Tech Referendum #{}", index);
700	log_print!("   🔑 Refund to: {}", from.bright_yellow());
701
702	// Load wallet keypair
703	let keypair = crate::wallet::load_keypair_from_wallet(from, password, password_file)?;
704
705	// Create refund_decision_deposit call for TechReferenda instance
706	let refund_call = quantus_subxt::api::tx().tech_referenda().refund_decision_deposit(index);
707
708	let tx_hash = submit_transaction(quantus_client, &keypair, refund_call, None).await?;
709	log_print!(
710		"✅ {} Refund transaction submitted! Hash: {:?}",
711		"SUCCESS".bright_green().bold(),
712		tx_hash
713	);
714
715	log_success!("🎉 {} Decision deposit refunded!", "FINISHED".bright_green().bold());
716	log_print!("💡 Check your balance to confirm the refund");
717	Ok(())
718}