1use crate::{
3 chain::quantus_subxt, error::QuantusError, log_error, log_print, log_success, log_verbose,
4 wallet::QuantumKeyPair,
5};
6use colored::Colorize;
7use serde_json::Value;
8use sp_core::crypto::{AccountId32, Ss58Codec};
9
10pub async fn execute_generic_call(
12 quantus_client: &crate::chain::client::QuantusClient,
13 pallet: &str,
14 call: &str,
15 args: Vec<Value>,
16 from_keypair: &QuantumKeyPair,
17 tip: Option<String>,
18 execution_mode: crate::cli::common::ExecutionMode,
19) -> crate::error::Result<subxt::utils::H256> {
20 log_print!("🚀 Executing generic call");
21 log_print!("Pallet: {}", pallet.bright_green());
22 log_print!("Call: {}", call.bright_cyan());
23 log_print!("From: {}", from_keypair.to_account_id_ss58check().bright_yellow());
24 if let Some(tip) = &tip {
25 log_print!("Tip: {}", tip.bright_magenta());
26 }
27
28 let _signer = from_keypair
30 .to_subxt_signer()
31 .map_err(|e| QuantusError::NetworkError(format!("Failed to convert keypair: {e:?}")))?;
32
33 let metadata = quantus_client.client().metadata();
35 let pallet_metadata = metadata
36 .pallet_by_name(pallet)
37 .ok_or_else(|| QuantusError::Generic(format!("Pallet '{pallet}' not found in metadata")))?;
38
39 log_verbose!("✅ Found pallet '{}' with index {}", pallet, pallet_metadata.index());
40
41 let call_metadata = pallet_metadata.call_variant_by_name(call).ok_or_else(|| {
43 QuantusError::Generic(format!("Call '{call}' not found in pallet '{pallet}'"))
44 })?;
45
46 log_verbose!("✅ Found call '{}' with index {}", call, call_metadata.index);
47
48 let tip_amount = if let Some(tip_str) = &tip { tip_str.parse::<u128>().ok() } else { None };
50
51 log_print!("🔧 Creating extrinsic for {}.{}", pallet, call);
53
54 let tx_hash = match (pallet, call) {
55 ("Balances", "transfer_allow_death") =>
57 submit_balance_transfer(
58 quantus_client,
59 from_keypair,
60 &args,
61 false,
62 tip_amount,
63 execution_mode,
64 )
65 .await?,
66 ("Balances", "transfer_keep_alive") =>
67 submit_balance_transfer(
68 quantus_client,
69 from_keypair,
70 &args,
71 true,
72 tip_amount,
73 execution_mode,
74 )
75 .await?,
76
77 ("System", "remark") =>
79 submit_system_remark(quantus_client, from_keypair, &args, tip_amount, execution_mode)
80 .await?,
81
82 ("Sudo", "sudo") => submit_sudo_call(quantus_client, from_keypair, &args).await?,
84
85 ("TechCollective", "add_member") =>
87 submit_tech_collective_add_member(quantus_client, from_keypair, &args, execution_mode)
88 .await?,
89 ("TechCollective", "remove_member") =>
90 submit_tech_collective_remove_member(
91 quantus_client,
92 from_keypair,
93 &args,
94 execution_mode,
95 )
96 .await?,
97 ("TechCollective", "vote") =>
98 submit_tech_collective_vote(quantus_client, from_keypair, &args, execution_mode).await?,
99
100 ("ReversibleTransfers", "schedule_transfer") =>
102 submit_reversible_transfer(quantus_client, from_keypair, &args, execution_mode).await?,
103
104 ("Scheduler", "schedule") =>
106 submit_scheduler_schedule(quantus_client, from_keypair, &args).await?,
107 ("Scheduler", "cancel") =>
108 submit_scheduler_cancel(quantus_client, from_keypair, &args).await?,
109
110 (_, _) => {
112 log_error!(
113 "❌ Pallet '{}' or call '{}' is not supported yet in SubXT implementation",
114 pallet,
115 call
116 );
117 log_print!("💡 Supported pallets in SubXT:");
118 log_print!(" • Balances: transfer_allow_death, transfer_keep_alive");
119 log_print!(" • System: remark");
120 log_print!(" • Sudo: sudo");
121 log_print!(" • TechCollective: add_member, remove_member, vote");
122 log_print!(" • ReversibleTransfers: schedule_transfer");
123 log_print!(" • Scheduler: schedule, cancel");
124 log_print!("💡 For other calls, use the original 'quantus call' command");
125 return Err(QuantusError::Generic(format!(
126 "Unsupported pallet/call combination in SubXT: {pallet}.{call}"
127 )));
128 },
129 };
130
131 log_success!("🎉 SubXT transaction submitted successfully!");
132 log_print!("📋 Transaction hash: {}", format!("0x{}", hex::encode(tx_hash)).bright_yellow());
133
134 Ok(tx_hash)
135}
136
137async fn submit_balance_transfer(
139 quantus_client: &crate::chain::client::QuantusClient,
140 from_keypair: &QuantumKeyPair,
141 args: &[Value],
142 keep_alive: bool,
143 tip: Option<u128>,
144 execution_mode: crate::cli::common::ExecutionMode,
145) -> crate::error::Result<subxt::utils::H256> {
146 if args.len() != 2 {
147 return Err(QuantusError::Generic(
148 "Balances transfer requires 2 arguments: [to_address, amount]".to_string(),
149 ));
150 }
151
152 let to_address = args[0].as_str().ok_or_else(|| {
153 QuantusError::Generic("First argument must be a string (to_address)".to_string())
154 })?;
155
156 let amount: u128 = args[1].as_str().unwrap_or("0").parse().map_err(|_| {
157 QuantusError::Generic("Second argument must be a number (amount)".to_string())
158 })?;
159
160 let (to_account_id, _) = AccountId32::from_ss58check_with_version(to_address)
162 .map_err(|e| QuantusError::Generic(format!("Invalid to_address: {e:?}")))?;
163
164 let to_account_id_bytes: [u8; 32] = *to_account_id.as_ref();
166 let to_account_id_subxt = subxt::ext::subxt_core::utils::AccountId32::from(to_account_id_bytes);
167
168 if keep_alive {
170 let transfer_call = quantus_subxt::api::tx().balances().transfer_keep_alive(
171 subxt::ext::subxt_core::utils::MultiAddress::Id(to_account_id_subxt),
172 amount,
173 );
174 crate::cli::common::submit_transaction(
175 quantus_client,
176 from_keypair,
177 transfer_call,
178 tip,
179 execution_mode,
180 )
181 .await
182 } else {
183 let transfer_call = quantus_subxt::api::tx().balances().transfer_allow_death(
184 subxt::ext::subxt_core::utils::MultiAddress::Id(to_account_id_subxt),
185 amount,
186 );
187 crate::cli::common::submit_transaction(
188 quantus_client,
189 from_keypair,
190 transfer_call,
191 tip,
192 execution_mode,
193 )
194 .await
195 }
196}
197
198async fn submit_system_remark(
200 quantus_client: &crate::chain::client::QuantusClient,
201 from_keypair: &QuantumKeyPair,
202 args: &[Value],
203 tip: Option<u128>,
204 execution_mode: crate::cli::common::ExecutionMode,
205) -> crate::error::Result<subxt::utils::H256> {
206 if args.len() != 1 {
207 return Err(QuantusError::Generic(
208 "System remark requires 1 argument: [remark]".to_string(),
209 ));
210 }
211
212 let remark = args[0]
213 .as_str()
214 .ok_or_else(|| QuantusError::Generic("Argument must be a string (remark)".to_string()))?;
215
216 let remark_call = quantus_subxt::api::tx().system().remark(remark.as_bytes().to_vec());
217
218 crate::cli::common::submit_transaction(
219 quantus_client,
220 from_keypair,
221 remark_call,
222 tip,
223 execution_mode,
224 )
225 .await
226}
227
228async fn submit_sudo_call(
230 _quantus_client: &crate::chain::client::QuantusClient,
231 _from_keypair: &QuantumKeyPair,
232 _args: &[Value],
233) -> crate::error::Result<subxt::utils::H256> {
234 log_error!("❌ Sudo calls through generic call are complex - use specific sudo wrappers");
236 log_print!("💡 Use dedicated subxt commands that already wrap calls in sudo");
237 Err(QuantusError::Generic(
238 "Sudo calls not supported in generic call - use specific commands".to_string(),
239 ))
240}
241
242async fn submit_tech_collective_add_member(
244 quantus_client: &crate::chain::client::QuantusClient,
245 from_keypair: &QuantumKeyPair,
246 args: &[Value],
247 execution_mode: crate::cli::common::ExecutionMode,
248) -> crate::error::Result<subxt::utils::H256> {
249 if args.len() != 1 {
250 return Err(QuantusError::Generic(
251 "TechCollective add_member requires 1 argument: [member_address]".to_string(),
252 ));
253 }
254
255 let member_address = args[0].as_str().ok_or_else(|| {
256 QuantusError::Generic("Argument must be a string (member_address)".to_string())
257 })?;
258
259 let (member_account_id, _) = AccountId32::from_ss58check_with_version(member_address)
260 .map_err(|e| QuantusError::Generic(format!("Invalid member_address: {e:?}")))?;
261
262 let member_account_id_bytes: [u8; 32] = *member_account_id.as_ref();
264 let member_account_id_subxt =
265 subxt::ext::subxt_core::utils::AccountId32::from(member_account_id_bytes);
266
267 let sudo_call = quantus_subxt::api::tx().sudo().sudo(quantus_subxt::api::Call::TechCollective(
269 quantus_subxt::api::tech_collective::Call::add_member {
270 who: subxt::ext::subxt_core::utils::MultiAddress::Id(member_account_id_subxt),
271 },
272 ));
273
274 crate::cli::common::submit_transaction(
275 quantus_client,
276 from_keypair,
277 sudo_call,
278 None,
279 execution_mode,
280 )
281 .await
282}
283
284async fn submit_tech_collective_remove_member(
286 quantus_client: &crate::chain::client::QuantusClient,
287 from_keypair: &QuantumKeyPair,
288 args: &[Value],
289 execution_mode: crate::cli::common::ExecutionMode,
290) -> crate::error::Result<subxt::utils::H256> {
291 if args.len() != 1 {
292 return Err(QuantusError::Generic(
293 "TechCollective remove_member requires 1 argument: [member_address]".to_string(),
294 ));
295 }
296
297 let member_address = args[0].as_str().ok_or_else(|| {
298 QuantusError::Generic("Argument must be a string (member_address)".to_string())
299 })?;
300
301 let (member_account_id, _) = AccountId32::from_ss58check_with_version(member_address)
302 .map_err(|e| QuantusError::Generic(format!("Invalid member_address: {e:?}")))?;
303
304 let member_account_id_bytes: [u8; 32] = *member_account_id.as_ref();
306 let member_account_id_subxt =
307 subxt::ext::subxt_core::utils::AccountId32::from(member_account_id_bytes);
308
309 let sudo_call = quantus_subxt::api::tx().sudo().sudo(quantus_subxt::api::Call::TechCollective(
311 quantus_subxt::api::tech_collective::Call::remove_member {
312 who: subxt::ext::subxt_core::utils::MultiAddress::Id(member_account_id_subxt),
313 min_rank: 0, },
315 ));
316
317 crate::cli::common::submit_transaction(
318 quantus_client,
319 from_keypair,
320 sudo_call,
321 None,
322 execution_mode,
323 )
324 .await
325}
326
327async fn submit_tech_collective_vote(
329 quantus_client: &crate::chain::client::QuantusClient,
330 from_keypair: &QuantumKeyPair,
331 args: &[Value],
332 execution_mode: crate::cli::common::ExecutionMode,
333) -> crate::error::Result<subxt::utils::H256> {
334 if args.len() != 2 {
335 return Err(QuantusError::Generic(
336 "TechCollective vote requires 2 arguments: [referendum_index, aye]".to_string(),
337 ));
338 }
339
340 let referendum_index: u32 = args[0].as_u64().unwrap_or(0) as u32;
341 let aye = args[1].as_bool().unwrap_or(false);
342
343 let vote_call = quantus_subxt::api::tx().tech_collective().vote(referendum_index, aye);
344
345 crate::cli::common::submit_transaction(
346 quantus_client,
347 from_keypair,
348 vote_call,
349 None,
350 execution_mode,
351 )
352 .await
353}
354
355async fn submit_reversible_transfer(
357 quantus_client: &crate::chain::client::QuantusClient,
358 from_keypair: &QuantumKeyPair,
359 args: &[Value],
360 execution_mode: crate::cli::common::ExecutionMode,
361) -> crate::error::Result<subxt::utils::H256> {
362 if args.len() != 2 {
363 return Err(QuantusError::Generic(
364 "ReversibleTransfers schedule_transfer requires 2 arguments: [to_address, amount]"
365 .to_string(),
366 ));
367 }
368
369 let to_address = args[0].as_str().ok_or_else(|| {
370 QuantusError::Generic("First argument must be a string (to_address)".to_string())
371 })?;
372
373 let amount: u128 = args[1].as_str().unwrap_or("0").parse().map_err(|_| {
374 QuantusError::Generic("Second argument must be a number (amount)".to_string())
375 })?;
376
377 let (to_account_id, _) = AccountId32::from_ss58check_with_version(to_address)
378 .map_err(|e| QuantusError::Generic(format!("Invalid to_address: {e:?}")))?;
379
380 let to_account_id_bytes: [u8; 32] = *to_account_id.as_ref();
382 let to_account_id_subxt = subxt::ext::subxt_core::utils::AccountId32::from(to_account_id_bytes);
383
384 let schedule_call = quantus_subxt::api::tx().reversible_transfers().schedule_transfer(
385 subxt::ext::subxt_core::utils::MultiAddress::Id(to_account_id_subxt),
386 amount,
387 );
388
389 crate::cli::common::submit_transaction(
390 quantus_client,
391 from_keypair,
392 schedule_call,
393 None,
394 execution_mode,
395 )
396 .await
397}
398
399async fn submit_scheduler_schedule(
401 _quantus_client: &crate::chain::client::QuantusClient,
402 _from_keypair: &QuantumKeyPair,
403 _args: &[Value],
404) -> crate::error::Result<subxt::utils::H256> {
405 log_error!("❌ Scheduler calls through generic call are complex");
406 log_print!("💡 Use dedicated scheduler commands for complex scheduling");
407 Err(QuantusError::Generic(
408 "Scheduler calls not supported in generic call - use scheduler commands".to_string(),
409 ))
410}
411
412async fn submit_scheduler_cancel(
414 _quantus_client: &crate::chain::client::QuantusClient,
415 _from_keypair: &QuantumKeyPair,
416 _args: &[Value],
417) -> crate::error::Result<subxt::utils::H256> {
418 log_error!("❌ Scheduler calls through generic call are complex");
419 log_print!("💡 Use dedicated scheduler commands for scheduling operations");
420 Err(QuantusError::Generic(
421 "Scheduler calls not supported in generic call - use scheduler commands".to_string(),
422 ))
423}
424
425pub async fn handle_generic_call(
427 pallet: &str,
428 call: &str,
429 args: Vec<Value>,
430 keypair: &QuantumKeyPair,
431 tip: Option<String>,
432 node_url: &str,
433 execution_mode: crate::cli::common::ExecutionMode,
434) -> crate::error::Result<()> {
435 log_print!("🚀 Generic Call");
436
437 let quantus_client = crate::chain::client::QuantusClient::new(node_url).await?;
438
439 execute_generic_call(&quantus_client, pallet, call, args, keypair, tip, execution_mode).await?;
440
441 Ok(())
442}