quantus_cli/cli/
referenda.rs

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