quantus_cli/cli/
referenda.rs

1//! `quantus referenda` subcommand - manage standard 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::str::FromStr;
9
10/// Standard Referenda management commands
11#[derive(Subcommand, Debug)]
12pub enum ReferendaCommands {
13	/// Submit a simple proposal (System::remark) to test Referenda
14	SubmitRemark {
15		/// Message to include in the remark
16		#[arg(long)]
17		message: String,
18
19		/// Wallet name to sign with
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		/// Origin type: signed (default), none (for signaling track), root
32		#[arg(long, default_value = "signed")]
33		origin: String,
34	},
35
36	/// Submit a proposal using existing preimage hash
37	Submit {
38		/// Preimage hash (must already exist on chain)
39		#[arg(long)]
40		preimage_hash: String,
41
42		/// Wallet name to sign with
43		#[arg(short, long)]
44		from: String,
45
46		/// Password for the wallet
47		#[arg(short, long)]
48		password: Option<String>,
49
50		/// Read password from file
51		#[arg(long)]
52		password_file: Option<String>,
53
54		/// Origin type: signed (default), none (for signaling track), root
55		#[arg(long, default_value = "signed")]
56		origin: String,
57	},
58
59	/// List all active Referenda proposals
60	List,
61
62	/// Get details of a specific Referendum
63	Get {
64		/// Referendum index
65		#[arg(short, long)]
66		index: u32,
67
68		/// Decode and display the proposal call in human-readable format
69		#[arg(long)]
70		decode: bool,
71	},
72
73	/// Check the status of a Referendum
74	Status {
75		/// Referendum index
76		#[arg(short, long)]
77		index: u32,
78	},
79
80	/// Place a decision deposit for a Referendum
81	PlaceDecisionDeposit {
82		/// Referendum index
83		#[arg(short, long)]
84		index: u32,
85
86		/// Wallet name to sign with
87		#[arg(short, long)]
88		from: String,
89
90		/// Password for the wallet
91		#[arg(short, long)]
92		password: Option<String>,
93
94		/// Read password from file
95		#[arg(long)]
96		password_file: Option<String>,
97	},
98
99	/// Vote on a Referendum (uses conviction voting)
100	Vote {
101		/// Referendum index
102		#[arg(short, long)]
103		index: u32,
104
105		/// Vote aye (true) or nay (false)
106		#[arg(long)]
107		aye: bool,
108
109		/// Conviction (0=None, 1=Locked1x, 2=Locked2x, up to 6=Locked6x)
110		#[arg(long, default_value = "0")]
111		conviction: u8,
112
113		/// Amount to vote with
114		#[arg(long)]
115		amount: String,
116
117		/// Wallet name to sign with
118		#[arg(short, long)]
119		from: String,
120
121		/// Password for the wallet
122		#[arg(short, long)]
123		password: Option<String>,
124
125		/// Read password from file
126		#[arg(long)]
127		password_file: Option<String>,
128	},
129
130	/// Refund submission deposit for a completed Referendum
131	RefundSubmissionDeposit {
132		/// Referendum index
133		#[arg(short, long)]
134		index: u32,
135
136		/// Wallet name that submitted the referendum
137		#[arg(short, long)]
138		from: String,
139
140		/// Password for the wallet
141		#[arg(short, long)]
142		password: Option<String>,
143
144		/// Read password from file
145		#[arg(long)]
146		password_file: Option<String>,
147	},
148
149	/// Refund decision deposit for a completed Referendum
150	RefundDecisionDeposit {
151		/// Referendum index
152		#[arg(short, long)]
153		index: u32,
154
155		/// Wallet name that placed the decision deposit
156		#[arg(short, long)]
157		from: String,
158
159		/// Password for the wallet
160		#[arg(short, long)]
161		password: Option<String>,
162
163		/// Read password from file
164		#[arg(long)]
165		password_file: Option<String>,
166	},
167
168	/// Get Referenda configuration
169	Config,
170}
171
172/// Handle referenda commands
173pub async fn handle_referenda_command(
174	command: ReferendaCommands,
175	node_url: &str,
176) -> crate::error::Result<()> {
177	let quantus_client = crate::chain::client::QuantusClient::new(node_url).await?;
178
179	match command {
180		ReferendaCommands::SubmitRemark { message, from, password, password_file, origin } =>
181			submit_remark_proposal(
182				&quantus_client,
183				&message,
184				&from,
185				password,
186				password_file,
187				&origin,
188			)
189			.await,
190		ReferendaCommands::Submit { preimage_hash, from, password, password_file, origin } =>
191			submit_proposal(
192				&quantus_client,
193				&preimage_hash,
194				&from,
195				password,
196				password_file,
197				&origin,
198			)
199			.await,
200		ReferendaCommands::List => list_proposals(&quantus_client).await,
201		ReferendaCommands::Get { index, decode } =>
202			get_proposal_details(&quantus_client, index, decode).await,
203		ReferendaCommands::Status { index } => get_proposal_status(&quantus_client, index).await,
204		ReferendaCommands::PlaceDecisionDeposit { index, from, password, password_file } =>
205			place_decision_deposit(&quantus_client, index, &from, password, password_file).await,
206		ReferendaCommands::Vote {
207			index,
208			aye,
209			conviction,
210			amount,
211			from,
212			password,
213			password_file,
214		} =>
215			vote_on_referendum(
216				&quantus_client,
217				index,
218				aye,
219				conviction,
220				&amount,
221				&from,
222				password,
223				password_file,
224			)
225			.await,
226		ReferendaCommands::RefundSubmissionDeposit { index, from, password, password_file } =>
227			refund_submission_deposit(&quantus_client, index, &from, password, password_file).await,
228		ReferendaCommands::RefundDecisionDeposit { index, from, password, password_file } =>
229			refund_decision_deposit(&quantus_client, index, &from, password, password_file).await,
230		ReferendaCommands::Config => get_config(&quantus_client).await,
231	}
232}
233
234/// Submit a simple System::remark proposal
235async fn submit_remark_proposal(
236	quantus_client: &crate::chain::client::QuantusClient,
237	message: &str,
238	from: &str,
239	password: Option<String>,
240	password_file: Option<String>,
241	origin_type: &str,
242) -> crate::error::Result<()> {
243	use qp_poseidon::PoseidonHasher;
244
245	log_print!("📝 Submitting System::remark Proposal to Referenda");
246	log_print!("   đŸ’Ŧ Message: {}", message.bright_cyan());
247	log_print!("   🔑 Submitted by: {}", from.bright_yellow());
248	log_print!("   đŸŽ¯ Origin type: {}", origin_type.bright_magenta());
249
250	// Load wallet keypair
251	let keypair = crate::wallet::load_keypair_from_wallet(from, password, password_file)?;
252
253	// Build System::remark call and encode it
254	let remark_bytes = message.as_bytes().to_vec();
255	let remark_payload = quantus_subxt::api::tx().system().remark(remark_bytes.clone());
256	let metadata = quantus_client.client().metadata();
257	let encoded_call = <_ as subxt::tx::Payload>::encode_call_data(&remark_payload, &metadata)
258		.map_err(|e| QuantusError::Generic(format!("Failed to encode call data: {:?}", e)))?;
259
260	log_verbose!("📝 Encoded call size: {} bytes", encoded_call.len());
261
262	// Compute preimage hash using Poseidon
263	let preimage_hash: sp_core::H256 =
264		<PoseidonHasher as sp_runtime::traits::Hash>::hash(&encoded_call);
265
266	log_print!("🔗 Preimage hash: {:?}", preimage_hash);
267
268	// Submit Preimage::note_preimage
269	type PreimageBytes = quantus_subxt::api::preimage::calls::types::note_preimage::Bytes;
270	let bounded_bytes: PreimageBytes = encoded_call.clone();
271
272	log_print!("📝 Submitting preimage...");
273	let note_preimage_tx = quantus_subxt::api::tx().preimage().note_preimage(bounded_bytes);
274	let preimage_tx_hash =
275		submit_transaction(quantus_client, &keypair, note_preimage_tx, None).await?;
276	log_print!("✅ Preimage transaction submitted: {:?}", preimage_tx_hash);
277
278	// Wait for preimage transaction confirmation
279	log_print!("âŗ Waiting for preimage transaction confirmation...");
280
281	// Build Referenda::submit call using Lookup preimage reference
282	type ProposalBounded =
283		quantus_subxt::api::runtime_types::frame_support::traits::preimages::Bounded<
284			quantus_subxt::api::runtime_types::quantus_runtime::RuntimeCall,
285			quantus_subxt::api::runtime_types::qp_poseidon::PoseidonHasher,
286		>;
287
288	let preimage_hash_subxt: subxt::utils::H256 = preimage_hash;
289	let proposal: ProposalBounded =
290		ProposalBounded::Lookup { hash: preimage_hash_subxt, len: encoded_call.len() as u32 };
291
292	// Create origin based on origin_type parameter
293	let account_id_sp = keypair.to_account_id_32();
294	let account_id_subxt: subxt::ext::subxt_core::utils::AccountId32 =
295		subxt::ext::subxt_core::utils::AccountId32(*account_id_sp.as_ref());
296
297	let origin_caller = match origin_type.to_lowercase().as_str() {
298		"signed" => {
299			let raw_origin =
300				quantus_subxt::api::runtime_types::frame_support::dispatch::RawOrigin::Signed(
301					account_id_subxt,
302				);
303			quantus_subxt::api::runtime_types::quantus_runtime::OriginCaller::system(raw_origin)
304		},
305		"none" => {
306			let raw_origin =
307				quantus_subxt::api::runtime_types::frame_support::dispatch::RawOrigin::None;
308			quantus_subxt::api::runtime_types::quantus_runtime::OriginCaller::system(raw_origin)
309		},
310		"root" => {
311			let raw_origin =
312				quantus_subxt::api::runtime_types::frame_support::dispatch::RawOrigin::Root;
313			quantus_subxt::api::runtime_types::quantus_runtime::OriginCaller::system(raw_origin)
314		},
315		_ =>
316			return Err(QuantusError::Generic(format!(
317				"Invalid origin type: {}. Must be 'signed', 'none', or 'root'",
318				origin_type
319			))),
320	};
321
322	let enactment =
323		quantus_subxt::api::runtime_types::frame_support::traits::schedule::DispatchTime::After(
324			10u32, // Execute 10 blocks after approval
325		);
326
327	log_print!("🔧 Creating Referenda::submit call...");
328	let submit_call =
329		quantus_subxt::api::tx().referenda().submit(origin_caller, proposal, enactment);
330
331	let tx_hash = submit_transaction(quantus_client, &keypair, submit_call, None).await?;
332	log_print!(
333		"✅ {} Referendum proposal submitted! Hash: {:?}",
334		"SUCCESS".bright_green().bold(),
335		tx_hash
336	);
337
338	log_print!("💡 Use 'quantus referenda list' to see active proposals");
339	Ok(())
340}
341
342/// Submit a proposal using existing preimage hash
343async fn submit_proposal(
344	quantus_client: &crate::chain::client::QuantusClient,
345	preimage_hash: &str,
346	from: &str,
347	password: Option<String>,
348	password_file: Option<String>,
349	origin_type: &str,
350) -> crate::error::Result<()> {
351	log_print!("📝 Submitting Proposal to Referenda");
352	log_print!("   🔗 Preimage hash: {}", preimage_hash.bright_cyan());
353	log_print!("   🔑 Submitted by: {}", from.bright_yellow());
354	log_print!("   đŸŽ¯ Origin type: {}", origin_type.bright_magenta());
355
356	// Parse preimage hash
357	let hash_str = preimage_hash.trim_start_matches("0x");
358	let preimage_hash_parsed: sp_core::H256 = sp_core::H256::from_str(hash_str)
359		.map_err(|_| QuantusError::Generic("Invalid preimage hash format".to_string()))?;
360
361	// Load wallet keypair
362	let keypair = crate::wallet::load_keypair_from_wallet(from, password, password_file)?;
363
364	// Check if preimage exists and get its length
365	log_print!("🔍 Checking preimage status...");
366	let latest_block_hash = quantus_client.get_latest_block().await?;
367	let storage_at = quantus_client.client().storage().at(latest_block_hash);
368
369	let preimage_status = storage_at
370		.fetch(
371			&quantus_subxt::api::storage()
372				.preimage()
373				.request_status_for(preimage_hash_parsed),
374		)
375		.await
376		.map_err(|e| QuantusError::Generic(format!("Failed to fetch preimage status: {:?}", e)))?
377		.ok_or_else(|| QuantusError::Generic("Preimage not found on chain".to_string()))?;
378
379	let preimage_len = match preimage_status {
380		quantus_subxt::api::runtime_types::pallet_preimage::RequestStatus::Unrequested {
381			ticket: _,
382			len,
383		} => len,
384		quantus_subxt::api::runtime_types::pallet_preimage::RequestStatus::Requested {
385			maybe_ticket: _,
386			count: _,
387			maybe_len,
388		} => match maybe_len {
389			Some(len) => len,
390			None => return Err(QuantusError::Generic("Preimage length not available".to_string())),
391		},
392	};
393
394	log_print!("✅ Preimage found! Length: {} bytes", preimage_len);
395
396	// Build Referenda::submit call
397	type ProposalBounded =
398		quantus_subxt::api::runtime_types::frame_support::traits::preimages::Bounded<
399			quantus_subxt::api::runtime_types::quantus_runtime::RuntimeCall,
400			quantus_subxt::api::runtime_types::qp_poseidon::PoseidonHasher,
401		>;
402
403	let preimage_hash_subxt: subxt::utils::H256 = preimage_hash_parsed;
404	let proposal: ProposalBounded =
405		ProposalBounded::Lookup { hash: preimage_hash_subxt, len: preimage_len };
406
407	// Create origin based on origin_type parameter
408	let account_id_sp = keypair.to_account_id_32();
409	let account_id_subxt: subxt::ext::subxt_core::utils::AccountId32 =
410		subxt::ext::subxt_core::utils::AccountId32(*account_id_sp.as_ref());
411
412	let origin_caller = match origin_type.to_lowercase().as_str() {
413		"signed" => {
414			let raw_origin =
415				quantus_subxt::api::runtime_types::frame_support::dispatch::RawOrigin::Signed(
416					account_id_subxt,
417				);
418			quantus_subxt::api::runtime_types::quantus_runtime::OriginCaller::system(raw_origin)
419		},
420		"none" => {
421			let raw_origin =
422				quantus_subxt::api::runtime_types::frame_support::dispatch::RawOrigin::None;
423			quantus_subxt::api::runtime_types::quantus_runtime::OriginCaller::system(raw_origin)
424		},
425		"root" => {
426			let raw_origin =
427				quantus_subxt::api::runtime_types::frame_support::dispatch::RawOrigin::Root;
428			quantus_subxt::api::runtime_types::quantus_runtime::OriginCaller::system(raw_origin)
429		},
430		_ =>
431			return Err(QuantusError::Generic(format!(
432				"Invalid origin type: {}. Must be 'signed', 'none', or 'root'",
433				origin_type
434			))),
435	};
436
437	let enactment =
438		quantus_subxt::api::runtime_types::frame_support::traits::schedule::DispatchTime::After(
439			10u32,
440		);
441
442	log_print!("🔧 Creating Referenda::submit call...");
443	let submit_call =
444		quantus_subxt::api::tx().referenda().submit(origin_caller, proposal, enactment);
445
446	let tx_hash = submit_transaction(quantus_client, &keypair, submit_call, None).await?;
447	log_print!(
448		"✅ {} Referendum proposal submitted! Hash: {:?}",
449		"SUCCESS".bright_green().bold(),
450		tx_hash
451	);
452
453	log_print!("💡 Use 'quantus referenda list' to see active proposals");
454	Ok(())
455}
456
457/// List recent Referenda proposals
458async fn list_proposals(
459	quantus_client: &crate::chain::client::QuantusClient,
460) -> crate::error::Result<()> {
461	log_print!("📜 Active Referenda Proposals");
462	log_print!("");
463
464	let addr = quantus_subxt::api::storage().referenda().referendum_count();
465
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 count = storage_at.fetch(&addr).await?;
470
471	if let Some(total) = count {
472		log_print!("📊 Total referenda created: {}", total);
473		if total == 0 {
474			log_print!("📭 No active proposals found");
475			return Ok(());
476		}
477		log_print!("🔍 Fetching recent referenda...");
478		for i in (0..total).rev().take(10) {
479			get_proposal_status(quantus_client, i).await?;
480			log_print!("----------------------------------------");
481		}
482	} else {
483		log_print!("📭 No referenda found - Referenda may be empty");
484	}
485
486	Ok(())
487}
488
489/// Get details of a specific Referendum
490async fn get_proposal_details(
491	quantus_client: &crate::chain::client::QuantusClient,
492	index: u32,
493	decode: bool,
494) -> crate::error::Result<()> {
495	use quantus_subxt::api::runtime_types::pallet_referenda::types::ReferendumInfo;
496
497	log_print!("📄 Referendum #{} Details", index);
498	log_print!("");
499
500	let addr = quantus_subxt::api::storage().referenda().referendum_info_for(index);
501
502	let latest_block_hash = quantus_client.get_latest_block().await?;
503	let storage_at = quantus_client.client().storage().at(latest_block_hash);
504
505	let info = storage_at.fetch(&addr).await?;
506
507	if let Some(referendum_info) = info {
508		if decode {
509			// Try to decode the proposal
510			match &referendum_info {
511				ReferendumInfo::Ongoing(status) => {
512					log_print!("📊 {} Referendum #{}", "Ongoing".bright_green(), index);
513					log_print!("   đŸ›¤ī¸  Track: {}", status.track);
514					log_print!("   📅 Submitted: Block #{}", status.submitted);
515					log_print!(
516						"   đŸ—ŗī¸  Tally: Ayes: {}, Nays: {}, Support: {}",
517						status.tally.ayes,
518						status.tally.nays,
519						status.tally.support
520					);
521					log_print!("");
522
523					// Extract preimage hash and length from proposal
524					if let quantus_subxt::api::runtime_types::frame_support::traits::preimages::Bounded::Lookup {
525						hash,
526						len,
527					} = &status.proposal
528					{
529						log_print!("📝 Proposal Details:");
530						log_print!("   🔗 Preimage Hash: {:?}", hash);
531						log_print!("   📏 Length: {} bytes", len);
532						log_print!("");
533
534						// Fetch and decode the preimage
535					match crate::cli::referenda_decode::decode_preimage(quantus_client, hash, *len).await {
536						Ok(decoded) => {
537							log_print!("✅ Decoded Proposal:");
538							log_print!("{}", decoded);
539						},
540						Err(e) => {
541							log_print!("âš ī¸  Could not decode proposal: {}", e);
542							log_print!("   Run 'quantus preimage get --hash {:?} --len {}' to see raw data", hash, len);
543						},
544					}
545					} else {
546						log_print!("âš ī¸  Proposal is inline (not a preimage lookup)");
547					}
548				},
549				ReferendumInfo::Approved(..) => {
550					log_print!("📊 {} Referendum #{}", "Approved".green(), index);
551					log_print!(
552						"   â„šī¸  Proposal details no longer available (referendum finalized)"
553					);
554				},
555				ReferendumInfo::Rejected(..) => {
556					log_print!("📊 {} Referendum #{}", "Rejected".red(), index);
557					log_print!(
558						"   â„šī¸  Proposal details no longer available (referendum finalized)"
559					);
560				},
561				ReferendumInfo::Cancelled(..) => {
562					log_print!("📊 {} Referendum #{}", "Cancelled".yellow(), index);
563					log_print!(
564						"   â„šī¸  Proposal details no longer available (referendum finalized)"
565					);
566				},
567				ReferendumInfo::TimedOut(..) => {
568					log_print!("📊 {} Referendum #{}", "TimedOut".dimmed(), index);
569					log_print!(
570						"   â„šī¸  Proposal details no longer available (referendum finalized)"
571					);
572				},
573				ReferendumInfo::Killed(..) => {
574					log_print!("📊 {} Referendum #{}", "Killed".red().bold(), index);
575					log_print!("   â„šī¸  Proposal details no longer available (referendum killed)");
576				},
577			}
578		} else {
579			// Raw output (original behavior)
580			log_print!("📋 Referendum Information (raw):");
581			log_print!("{:#?}", referendum_info);
582		}
583	} else {
584		log_print!("📭 Referendum #{} not found", index);
585	}
586	Ok(())
587}
588
589/// Get the status of a Referendum
590async fn get_proposal_status(
591	quantus_client: &crate::chain::client::QuantusClient,
592	index: u32,
593) -> crate::error::Result<()> {
594	use quantus_subxt::api::runtime_types::pallet_referenda::types::ReferendumInfo;
595
596	log_verbose!("📊 Fetching status for Referendum #{}...", index);
597
598	let addr = quantus_subxt::api::storage().referenda().referendum_info_for(index);
599
600	let latest_block_hash = quantus_client.get_latest_block().await?;
601	let storage_at = quantus_client.client().storage().at(latest_block_hash);
602
603	let info_res = storage_at.fetch(&addr).await;
604
605	match info_res {
606		Ok(Some(info)) => {
607			log_print!("📊 Status for Referendum #{}", index.to_string().bright_yellow());
608			match info {
609				ReferendumInfo::Ongoing(status) => {
610					log_print!("   - Status: {}", "Ongoing".bright_green());
611					log_print!("   - Track: {}", status.track);
612					log_print!("   - Submitted at: block {}", status.submitted);
613					log_print!(
614						"   - Tally: Ayes: {}, Nays: {}",
615						status.tally.ayes,
616						status.tally.nays
617					);
618					log_verbose!("   - Full status: {:#?}", status);
619				},
620				ReferendumInfo::Approved(submitted, ..) => {
621					log_print!("   - Status: {}", "Approved".green());
622					log_print!("   - Submitted at block: {}", submitted);
623				},
624				ReferendumInfo::Rejected(submitted, ..) => {
625					log_print!("   - Status: {}", "Rejected".red());
626					log_print!("   - Submitted at block: {}", submitted);
627				},
628				ReferendumInfo::Cancelled(submitted, ..) => {
629					log_print!("   - Status: {}", "Cancelled".yellow());
630					log_print!("   - Submitted at block: {}", submitted);
631				},
632				ReferendumInfo::TimedOut(submitted, ..) => {
633					log_print!("   - Status: {}", "TimedOut".dimmed());
634					log_print!("   - Submitted at block: {}", submitted);
635				},
636				ReferendumInfo::Killed(submitted) => {
637					log_print!("   - Status: {}", "Killed".red().bold());
638					log_print!("   - Killed at block: {}", submitted);
639				},
640			}
641		},
642		Ok(None) => log_print!("📭 Referendum #{} not found", index),
643		Err(e) => log_error!("❌ Failed to fetch referendum #{}: {:?}", index, e),
644	}
645
646	Ok(())
647}
648
649/// Place a decision deposit for a Referendum
650async fn place_decision_deposit(
651	quantus_client: &crate::chain::client::QuantusClient,
652	index: u32,
653	from: &str,
654	password: Option<String>,
655	password_file: Option<String>,
656) -> crate::error::Result<()> {
657	log_print!("📋 Placing decision deposit for Referendum #{}", index);
658	log_print!("   🔑 Placed by: {}", from.bright_yellow());
659
660	let keypair = crate::wallet::load_keypair_from_wallet(from, password, password_file)?;
661
662	let deposit_call = quantus_subxt::api::tx().referenda().place_decision_deposit(index);
663	let tx_hash = submit_transaction(quantus_client, &keypair, deposit_call, None).await?;
664	log_success!("✅ Decision deposit placed! Hash: {:?}", tx_hash.to_string().bright_yellow());
665	Ok(())
666}
667
668/// Vote on a Referendum
669async fn vote_on_referendum(
670	quantus_client: &crate::chain::client::QuantusClient,
671	index: u32,
672	aye: bool,
673	conviction: u8,
674	amount: &str,
675	from: &str,
676	password: Option<String>,
677	password_file: Option<String>,
678) -> crate::error::Result<()> {
679	log_print!("đŸ—ŗī¸  Voting on Referendum #{}", index);
680	log_print!("   📊 Vote: {}", if aye { "AYE ✅".bright_green() } else { "NAY ❌".bright_red() });
681	log_print!("   💰 Amount: {}", amount.bright_cyan());
682	log_print!("   🔒 Conviction: {}", conviction);
683	log_print!("   🔑 Signed by: {}", from.bright_yellow());
684
685	let keypair = crate::wallet::load_keypair_from_wallet(from, password, password_file)?;
686
687	// Parse amount
688	let amount_value: u128 = (amount
689		.parse::<f64>()
690		.map_err(|_| QuantusError::Generic("Invalid amount format".to_string()))?
691		.max(0.0) *
692		1_000_000_000_000_000_000.0) as u128;
693
694	// Validate conviction
695	if conviction > 6 {
696		return Err(QuantusError::Generic("Invalid conviction (must be 0-6)".to_string()));
697	}
698
699	// Build vote
700	let vote =
701		quantus_subxt::api::runtime_types::pallet_conviction_voting::vote::AccountVote::Standard {
702			vote: quantus_subxt::api::runtime_types::pallet_conviction_voting::vote::Vote(
703				if aye { 128 } else { 0 } | conviction,
704			),
705			balance: amount_value,
706		};
707
708	let vote_call = quantus_subxt::api::tx().conviction_voting().vote(index, vote);
709	let tx_hash = submit_transaction(quantus_client, &keypair, vote_call, None).await?;
710
711	log_print!(
712		"✅ {} Vote transaction submitted! Hash: {:?}",
713		"SUCCESS".bright_green().bold(),
714		tx_hash
715	);
716
717	log_success!("🎉 {} Vote submitted!", "FINISHED".bright_green().bold());
718	Ok(())
719}
720
721/// Get Referenda configuration
722async fn get_config(
723	quantus_client: &crate::chain::client::QuantusClient,
724) -> crate::error::Result<()> {
725	log_print!("âš™ī¸  Referenda Configuration");
726	log_print!("");
727
728	let constants = quantus_client.client().constants();
729	let tracks_addr = quantus_subxt::api::constants().referenda().tracks();
730
731	match constants.at(&tracks_addr) {
732		Ok(tracks) => {
733			log_print!("{}", "📊 Track Configuration:".bold());
734			for (id, info) in tracks.iter() {
735				log_print!("   ------------------------------------");
736				log_print!(
737					"   â€ĸ {} #{}: {}",
738					"Track".bold(),
739					id,
740					info.name.to_string().bright_cyan()
741				);
742				log_print!("   â€ĸ Max Deciding: {}", info.max_deciding);
743				log_print!("   â€ĸ Decision Deposit: {}", info.decision_deposit);
744				log_print!("   â€ĸ Prepare Period: {} blocks", info.prepare_period);
745				log_print!("   â€ĸ Decision Period: {} blocks", info.decision_period);
746				log_print!("   â€ĸ Confirm Period: {} blocks", info.confirm_period);
747				log_print!("   â€ĸ Min Enactment Period: {} blocks", info.min_enactment_period);
748			}
749			log_print!("   ------------------------------------");
750		},
751		Err(e) => {
752			log_error!("❌ Failed to decode Tracks constant: {:?}", e);
753			log_print!("💡 It's possible the Tracks constant is not in the expected format.");
754		},
755	}
756
757	Ok(())
758}
759
760/// Refund submission deposit for a completed Referendum
761async fn refund_submission_deposit(
762	quantus_client: &crate::chain::client::QuantusClient,
763	index: u32,
764	from: &str,
765	password: Option<String>,
766	password_file: Option<String>,
767) -> crate::error::Result<()> {
768	log_print!("💰 Refunding submission deposit for Referendum #{}", index);
769	log_print!("   🔑 Refund to: {}", from.bright_yellow());
770
771	// Load wallet keypair
772	let keypair = crate::wallet::load_keypair_from_wallet(from, password, password_file)?;
773
774	// Create refund_submission_deposit call
775	let refund_call = quantus_subxt::api::tx().referenda().refund_submission_deposit(index);
776
777	let tx_hash = submit_transaction(quantus_client, &keypair, refund_call, None).await?;
778	log_print!(
779		"✅ {} Refund transaction submitted! Hash: {:?}",
780		"SUCCESS".bright_green().bold(),
781		tx_hash
782	);
783
784	log_print!("💡 Check your balance to confirm the refund");
785	Ok(())
786}
787
788/// Refund decision deposit for a completed Referendum
789async fn refund_decision_deposit(
790	quantus_client: &crate::chain::client::QuantusClient,
791	index: u32,
792	from: &str,
793	password: Option<String>,
794	password_file: Option<String>,
795) -> crate::error::Result<()> {
796	log_print!("💰 Refunding decision deposit for Referendum #{}", index);
797	log_print!("   🔑 Refund to: {}", from.bright_yellow());
798
799	// Load wallet keypair
800	let keypair = crate::wallet::load_keypair_from_wallet(from, password, password_file)?;
801
802	// Create refund_decision_deposit call
803	let refund_call = quantus_subxt::api::tx().referenda().refund_decision_deposit(index);
804
805	let tx_hash = submit_transaction(quantus_client, &keypair, refund_call, None).await?;
806	log_print!(
807		"✅ {} Refund transaction submitted! Hash: {:?}",
808		"SUCCESS".bright_green().bold(),
809		tx_hash
810	);
811
812	log_print!("💡 Check your balance to confirm the refund");
813	Ok(())
814}