1use crate::{
3 chain::quantus_subxt,
4 cli::{
5 address_format::QuantusSS58, common::resolve_address,
6 progress_spinner::wait_for_tx_confirmation,
7 },
8 error::QuantusError,
9 log_error, log_print, log_success, log_verbose,
10};
11use clap::Subcommand;
12use colored::Colorize;
13use sp_core::crypto::{AccountId32, Ss58Codec};
14
15#[derive(Subcommand, Debug)]
17pub enum TechCollectiveCommands {
18 AddMember {
20 #[arg(short, long)]
22 who: String,
23
24 #[arg(short, long)]
26 from: String,
27
28 #[arg(short, long)]
30 password: Option<String>,
31
32 #[arg(long)]
34 password_file: Option<String>,
35 },
36
37 RemoveMember {
39 #[arg(short, long)]
41 who: String,
42
43 #[arg(short, long)]
45 from: String,
46
47 #[arg(short, long)]
49 password: Option<String>,
50
51 #[arg(long)]
53 password_file: Option<String>,
54 },
55
56 Vote {
58 #[arg(short, long)]
60 referendum_index: u32,
61
62 #[arg(short, long)]
64 aye: bool,
65
66 #[arg(short, long)]
68 from: String,
69
70 #[arg(short, long)]
72 password: Option<String>,
73
74 #[arg(long)]
76 password_file: Option<String>,
77 },
78
79 ListMembers,
81
82 IsMember {
84 #[arg(short, long)]
86 address: String,
87 },
88
89 CheckSudo {
91 #[arg(short, long)]
93 address: Option<String>,
94 },
95
96 ListReferenda,
98
99 GetReferendum {
101 #[arg(short, long)]
103 index: u32,
104 },
105}
106
107pub async fn add_member(
109 quantus_client: &crate::chain::client::QuantusClient,
110 from_keypair: &crate::wallet::QuantumKeyPair,
111 who_address: &str,
112) -> crate::error::Result<subxt::utils::H256> {
113 log_verbose!("🏛️ Adding member to Tech Collective...");
114 log_verbose!(" Member: {}", who_address.bright_cyan());
115
116 let (member_account_sp, _) = AccountId32::from_ss58check_with_version(who_address)
118 .map_err(|e| QuantusError::Generic(format!("Invalid member address: {e:?}")))?;
119
120 let member_account_bytes: [u8; 32] = *member_account_sp.as_ref();
122 let member_account_id = subxt::ext::subxt_core::utils::AccountId32::from(member_account_bytes);
123
124 log_verbose!("✍️ Creating add_member transaction...");
125
126 let add_member_call = quantus_subxt::api::Call::TechCollective(
128 quantus_subxt::api::tech_collective::Call::add_member {
129 who: subxt::ext::subxt_core::utils::MultiAddress::Id(member_account_id),
130 },
131 );
132
133 let sudo_call = quantus_subxt::api::tx().sudo().sudo(add_member_call);
135
136 let tx_hash =
137 crate::cli::common::submit_transaction(quantus_client, from_keypair, sudo_call, None)
138 .await?;
139
140 log_verbose!("📋 Add member transaction submitted: {:?}", tx_hash);
141
142 Ok(tx_hash)
143}
144
145pub async fn remove_member(
147 quantus_client: &crate::chain::client::QuantusClient,
148 from_keypair: &crate::wallet::QuantumKeyPair,
149 who_address: &str,
150) -> crate::error::Result<subxt::utils::H256> {
151 log_verbose!("🏛️ Removing member from Tech Collective...");
152 log_verbose!(" Member: {}", who_address.bright_cyan());
153
154 let (member_account_sp, _) = AccountId32::from_ss58check_with_version(who_address)
156 .map_err(|e| QuantusError::Generic(format!("Invalid member address: {e:?}")))?;
157
158 let member_account_bytes: [u8; 32] = *member_account_sp.as_ref();
160 let member_account_id = subxt::ext::subxt_core::utils::AccountId32::from(member_account_bytes);
161
162 log_verbose!("✍️ Creating remove_member transaction...");
163
164 let remove_member_call = quantus_subxt::api::Call::TechCollective(
166 quantus_subxt::api::tech_collective::Call::remove_member {
167 who: subxt::ext::subxt_core::utils::MultiAddress::Id(member_account_id),
168 min_rank: 0u16, },
170 );
171
172 let sudo_call = quantus_subxt::api::tx().sudo().sudo(remove_member_call);
174
175 let tx_hash =
176 crate::cli::common::submit_transaction(quantus_client, from_keypair, sudo_call, None)
177 .await?;
178
179 log_verbose!("📋 Remove member transaction submitted: {:?}", tx_hash);
180
181 Ok(tx_hash)
182}
183
184pub async fn vote_on_referendum(
186 quantus_client: &crate::chain::client::QuantusClient,
187 from_keypair: &crate::wallet::QuantumKeyPair,
188 referendum_index: u32,
189 aye: bool,
190) -> crate::error::Result<subxt::utils::H256> {
191 log_verbose!("🗳️ Voting on referendum...");
192 log_verbose!(" Referendum: {}", referendum_index);
193 log_verbose!(" Vote: {}", if aye { "AYE" } else { "NAY" });
194
195 log_verbose!("✍️ Creating vote transaction...");
196
197 let vote_call = quantus_subxt::api::tx().tech_collective().vote(referendum_index, aye);
199
200 let tx_hash =
201 crate::cli::common::submit_transaction(quantus_client, from_keypair, vote_call, None)
202 .await?;
203
204 log_verbose!("📋 Vote transaction submitted: {:?}", tx_hash);
205
206 Ok(tx_hash)
207}
208
209pub async fn is_member(
211 quantus_client: &crate::chain::client::QuantusClient,
212 address: &str,
213) -> crate::error::Result<bool> {
214 log_verbose!("🔍 Checking membership...");
215 log_verbose!(" Address: {}", address.bright_cyan());
216
217 let (account_sp, _) = AccountId32::from_ss58check_with_version(address)
219 .map_err(|e| QuantusError::Generic(format!("Invalid address: {e:?}")))?;
220
221 let account_bytes: [u8; 32] = *account_sp.as_ref();
223 let account_id = subxt::ext::subxt_core::utils::AccountId32::from(account_bytes);
224
225 let storage_addr = quantus_subxt::api::storage().tech_collective().members(account_id);
227
228 let latest_block_hash = quantus_client.get_latest_block().await?;
230
231 let storage_at = quantus_client.client().storage().at(latest_block_hash);
232
233 let member_data = storage_at
234 .fetch(&storage_addr)
235 .await
236 .map_err(|e| QuantusError::NetworkError(format!("Failed to fetch member data: {e:?}")))?;
237
238 Ok(member_data.is_some())
239}
240
241pub async fn get_member_count(
243 quantus_client: &crate::chain::client::QuantusClient,
244) -> crate::error::Result<Option<u32>> {
245 log_verbose!("🔍 Getting member count...");
246
247 let storage_addr = quantus_subxt::api::storage().tech_collective().member_count(0u16);
249
250 let latest_block_hash = quantus_client.get_latest_block().await?;
252
253 let storage_at = quantus_client.client().storage().at(latest_block_hash);
254
255 let count_data = storage_at
256 .fetch(&storage_addr)
257 .await
258 .map_err(|e| QuantusError::NetworkError(format!("Failed to fetch member count: {e:?}")))?;
259
260 Ok(count_data)
261}
262
263pub async fn get_member_list(
265 quantus_client: &crate::chain::client::QuantusClient,
266) -> crate::error::Result<Vec<AccountId32>> {
267 log_verbose!("🔍 Getting member list...");
268
269 let latest_block_hash = quantus_client.get_latest_block().await?;
271
272 let storage_at = quantus_client.client().storage().at(latest_block_hash);
273
274 let members_storage = quantus_subxt::api::storage().tech_collective().members_iter();
276
277 let mut members = Vec::new();
278 let mut iter = storage_at.iter(members_storage).await.map_err(|e| {
279 QuantusError::NetworkError(format!("Failed to create members iterator: {e:?}"))
280 })?;
281
282 while let Some(result) = iter.next().await {
283 match result {
284 Ok(storage_entry) => {
285 let key = storage_entry.key_bytes;
286 if key.len() >= 32 {
289 let account_bytes: [u8; 32] =
291 key[key.len() - 32..].try_into().unwrap_or([0u8; 32]);
292 let sp_account = AccountId32::from(account_bytes);
293 log_verbose!("Found member: {}", sp_account.to_quantus_ss58());
294 members.push(sp_account);
295 }
296 },
297 Err(e) => {
298 log_verbose!("⚠️ Error reading member entry: {:?}", e);
299 },
300 }
301 }
302
303 log_verbose!("Found {} total members", members.len());
304 Ok(members)
305}
306
307pub async fn get_sudo_account(
309 quantus_client: &crate::chain::client::QuantusClient,
310) -> crate::error::Result<Option<AccountId32>> {
311 log_verbose!("🔍 Getting sudo account...");
312
313 let storage_addr = quantus_subxt::api::storage().sudo().key();
315
316 let latest_block_hash = quantus_client.get_latest_block().await?;
318
319 let storage_at = quantus_client.client().storage().at(latest_block_hash);
320
321 let sudo_account = storage_at
322 .fetch(&storage_addr)
323 .await
324 .map_err(|e| QuantusError::NetworkError(format!("Failed to fetch sudo account: {e:?}")))?;
325
326 if let Some(subxt_account) = sudo_account {
328 let account_bytes: [u8; 32] = *subxt_account.as_ref();
329 let sp_account = AccountId32::from(account_bytes);
330 Ok(Some(sp_account))
331 } else {
332 Ok(None)
333 }
334}
335
336pub async fn handle_tech_collective_command(
338 command: TechCollectiveCommands,
339 node_url: &str,
340) -> crate::error::Result<()> {
341 log_print!("🏛️ Tech Collective");
342
343 let quantus_client = crate::chain::client::QuantusClient::new(node_url).await?;
344
345 match command {
346 TechCollectiveCommands::AddMember { who, from, password, password_file } => {
347 log_print!("🏛️ Adding member to Tech Collective");
348 log_print!(" 👤 Member: {}", who.bright_cyan());
349 log_print!(" 🔑 Signed by: {}", from.bright_yellow());
350
351 let keypair = crate::wallet::load_keypair_from_wallet(&from, password, password_file)?;
353
354 let tx_hash = add_member(&quantus_client, &keypair, &who).await?;
356
357 log_print!(
358 "✅ {} Add member transaction submitted! Hash: {:?}",
359 "SUCCESS".bright_green().bold(),
360 tx_hash
361 );
362
363 let success = wait_for_tx_confirmation(quantus_client.client(), tx_hash).await?;
364
365 if success {
366 log_success!(
367 "🎉 {} Member added to Tech Collective!",
368 "FINISHED".bright_green().bold()
369 );
370 } else {
371 log_error!("Transaction failed!");
372 }
373
374 Ok(())
375 },
376
377 TechCollectiveCommands::RemoveMember { who, from, password, password_file } => {
378 log_print!("🏛️ Removing member from Tech Collective ");
379 log_print!(" 👤 Member: {}", who.bright_cyan());
380 log_print!(" 🔑 Signed by: {}", from.bright_yellow());
381
382 let keypair = crate::wallet::load_keypair_from_wallet(&from, password, password_file)?;
384
385 let tx_hash = remove_member(&quantus_client, &keypair, &who).await?;
387
388 log_print!(
389 "✅ {} Remove member transaction submitted! Hash: {:?}",
390 "SUCCESS".bright_green().bold(),
391 tx_hash
392 );
393
394 let success = wait_for_tx_confirmation(quantus_client.client(), tx_hash).await?;
395
396 if success {
397 log_success!(
398 "🎉 {} Member removed from Tech Collective!",
399 "FINISHED".bright_green().bold()
400 );
401 } else {
402 log_error!("Transaction failed!");
403 }
404
405 Ok(())
406 },
407
408 TechCollectiveCommands::Vote { referendum_index, aye, from, password, password_file } => {
409 log_print!("🗳️ Voting on Tech Referendum #{} ", referendum_index);
410 log_print!(
411 " 📊 Vote: {}",
412 if aye { "AYE ✅".bright_green() } else { "NAY ❌".bright_red() }
413 );
414 log_print!(" 🔑 Signed by: {}", from.bright_yellow());
415
416 let keypair = crate::wallet::load_keypair_from_wallet(&from, password, password_file)?;
418
419 let tx_hash =
421 vote_on_referendum(&quantus_client, &keypair, referendum_index, aye).await?;
422
423 log_print!(
424 "✅ {} Vote transaction submitted! Hash: {:?}",
425 "SUCCESS".bright_green().bold(),
426 tx_hash
427 );
428
429 let success = wait_for_tx_confirmation(quantus_client.client(), tx_hash).await?;
430
431 if success {
432 log_success!("🎉 {} Vote submitted!", "FINISHED".bright_green().bold());
433 } else {
434 log_error!("Transaction failed!");
435 }
436
437 Ok(())
438 },
439
440 TechCollectiveCommands::ListMembers => {
441 log_print!("🏛️ Tech Collective Members ");
442 log_print!("");
443
444 match get_member_list(&quantus_client).await {
446 Ok(members) =>
447 if members.is_empty() {
448 log_print!("📭 No members in Tech Collective");
449 } else {
450 log_print!("👥 Total members: {}", members.len());
451 log_print!("");
452
453 for (index, member) in members.iter().enumerate() {
454 log_print!(
455 "{}. {}",
456 (index + 1).to_string().bright_blue(),
457 member.to_quantus_ss58().bright_green()
458 );
459 }
460 },
461 Err(e) => {
462 log_verbose!("⚠️ Failed to get member list: {:?}", e);
463 match get_member_count(&quantus_client).await? {
465 Some(count_data) => {
466 log_verbose!("✅ Got member count data: {:?}", count_data);
467 if count_data > 0 {
468 log_print!(
469 "👥 Total members: {} (detailed list unavailable)",
470 count_data
471 );
472 } else {
473 log_print!("📭 No members in Tech Collective");
474 }
475 },
476 None => {
477 log_print!("📭 No member data found - Tech Collective may be empty");
478 },
479 }
480 },
481 }
482
483 log_print!("");
484 log_print!("💡 To check specific membership:");
485 log_print!(" quantus tech-collective is-member --address <ADDRESS>");
486 log_print!("💡 To add a member (requires sudo):");
487 log_print!(
488 " quantus tech-collective add-member --who <ADDRESS> --from <SUDO_WALLET>"
489 );
490
491 Ok(())
492 },
493
494 TechCollectiveCommands::IsMember { address } => {
495 log_print!("🔍 Checking Tech Collective membership ");
496
497 let resolved_address = resolve_address(&address)?;
499
500 log_print!(" 👤 Address: {}", resolved_address.bright_cyan());
501
502 if is_member(&quantus_client, &resolved_address).await? {
503 log_success!("✅ Address IS a member of Tech Collective!");
504 log_print!("👥 Member data found in storage");
505 } else {
506 log_print!("❌ Address is NOT a member of Tech Collective");
507 log_print!("💡 No membership record found for this address");
508 }
509
510 Ok(())
511 },
512
513 TechCollectiveCommands::CheckSudo { address } => {
514 log_print!("🏛️ Checking sudo permissions ");
515
516 match get_sudo_account(&quantus_client).await? {
517 Some(sudo_account) => {
518 let sudo_address = sudo_account.to_quantus_ss58();
519 log_verbose!("🔍 Found sudo account: {}", sudo_address);
520 log_success!("✅ Found sudo account: {}", sudo_address.bright_green());
521
522 if let Some(check_address) = address {
524 log_verbose!("🔍 Checking if provided address is sudo...");
525
526 let resolved_address = resolve_address(&check_address)?;
528 log_verbose!(" 👤 Address to check: {}", resolved_address);
529
530 if sudo_address == resolved_address {
531 log_success!("✅ Provided address IS the sudo account!");
532 } else {
533 log_print!("❌ Provided address is NOT the sudo account");
534 log_verbose!("💡 Provided address: {}", resolved_address);
535 log_verbose!("💡 Actual sudo address: {}", sudo_address);
536 }
537 } else {
538 log_verbose!("💡 Use 'quantus tech-collective check-sudo --address <ADDRESS>' to check if a specific address is sudo");
540 }
541 },
542 None => {
543 log_print!("📭 No sudo account found in network");
544 log_verbose!("💡 The network may not have sudo configured");
545 },
546 }
547
548 Ok(())
549 },
550
551 TechCollectiveCommands::ListReferenda => {
552 log_print!("📜 Active Tech Referenda ");
553 log_print!("");
554
555 log_print!("💡 Referenda listing requires TechReferenda pallet storage queries");
556 log_print!(
557 "💡 Use 'quantus call --pallet TechReferenda --call <method>' for direct interaction"
558 );
559
560 Ok(())
561 },
562
563 TechCollectiveCommands::GetReferendum { index } => {
564 log_print!("📄 Tech Referendum #{} Details ", index);
565 log_print!("");
566
567 log_print!("💡 Referendum details require TechReferenda storage access");
568 log_print!("💡 Query ReferendumInfoFor storage with index {}", index);
569
570 Ok(())
571 },
572 }
573}