quantus_cli/cli/
batch.rs

1//! Batch transfer commands and configuration
2use crate::{
3	chain::client::QuantusClient,
4	cli::send::{
5		batch_transfer, get_batch_limits, load_transfers_from_file, validate_and_format_amount,
6	},
7	error::Result,
8	log_info, log_print, log_success,
9};
10use clap::Subcommand;
11use colored::Colorize;
12
13#[derive(Subcommand, Debug)]
14pub enum BatchCommands {
15	/// Send tokens to multiple recipients in a single batch transaction
16	Send {
17		/// Wallet name to send from
18		#[arg(short, long)]
19		from: String,
20
21		/// Password for the wallet (or use environment variables)
22		#[arg(short, long)]
23		password: Option<String>,
24
25		/// Read password from file (for scripting)
26		#[arg(long)]
27		password_file: Option<String>,
28
29		/// Optional tip amount to prioritize the transaction (e.g., "1", "0.5")
30		#[arg(long)]
31		tip: Option<String>,
32
33		/// Batch file with transfers (JSON format: [{"to": "address", "amount": "1000"}, ...])
34		#[arg(long)]
35		batch_file: Option<String>,
36
37		/// Number of identical transfers to generate for testing
38		#[arg(long)]
39		count: Option<u32>,
40
41		/// Recipient address for generated transfers (required with --count)
42		#[arg(long)]
43		to: Option<String>,
44
45		/// Amount per transfer for generated transfers (required with --count)
46		#[arg(long)]
47		amount: Option<String>,
48	},
49
50	/// Configuration and limits for batch transfers
51	Config {
52		/// Show current batch transfer limits for the connected chain
53		#[arg(long)]
54		limits: bool,
55
56		/// Show batch transfer configuration and recommendations
57		#[arg(long)]
58		info: bool,
59	},
60}
61
62/// Handle batch commands
63pub async fn handle_batch_command(
64	command: BatchCommands,
65	node_url: &str,
66	finalized: bool,
67) -> Result<()> {
68	match command {
69		BatchCommands::Send {
70			from,
71			password,
72			password_file,
73			tip,
74			batch_file,
75			count,
76			to,
77			amount,
78		} =>
79			handle_batch_send_command(
80				from,
81				node_url,
82				password,
83				password_file,
84				tip,
85				batch_file,
86				count,
87				to,
88				amount,
89				finalized,
90			)
91			.await,
92		BatchCommands::Config { limits, info } =>
93			handle_batch_config_command(node_url, limits, info).await,
94	}
95}
96
97/// Handle the batch send command (moved from send.rs)
98async fn handle_batch_send_command(
99	from_wallet: String,
100	node_url: &str,
101	password: Option<String>,
102	password_file: Option<String>,
103	tip: Option<String>,
104	batch_file: Option<String>,
105	count: Option<u32>,
106	to: Option<String>,
107	amount: Option<String>,
108	finalized: bool,
109) -> Result<()> {
110	// Create quantus chain client
111	let quantus_client = QuantusClient::new(node_url).await?;
112
113	// Prepare transfers list
114	let transfers = if let Some(file_path) = batch_file {
115		// Load from JSON file
116		load_transfers_from_file(&file_path).await?
117	} else if let (Some(count_val), Some(to_addr), Some(amount_str)) = (count, to, amount) {
118		// Generate identical transfers
119		let (parsed_amount, _) = validate_and_format_amount(&quantus_client, &amount_str).await?;
120		let mut transfers = Vec::new();
121		for _ in 0..count_val {
122			transfers.push((to_addr.clone(), parsed_amount));
123		}
124		transfers
125	} else {
126		return Err(crate::error::QuantusError::Generic(
127			"Either --batch-file or (--count + --to + --amount) must be provided".to_string(),
128		));
129	};
130
131	if transfers.is_empty() {
132		return Err(crate::error::QuantusError::Generic("No transfers to process".to_string()));
133	}
134
135	log_info!("🚀 Initiating batch transfer with {} transfers", transfers.len());
136
137	// Parse tip if provided
138	let tip_amount = if let Some(tip_str) = tip {
139		let (tip_val, _) = validate_and_format_amount(&quantus_client, &tip_str).await?;
140		Some(tip_val)
141	} else {
142		None
143	};
144
145	// Load wallet
146	let keypair = crate::wallet::load_keypair_from_wallet(&from_wallet, password, password_file)?;
147	let from_account_id = keypair.to_account_id_ss58check();
148
149	// Check balance
150	let balance = crate::cli::send::get_balance(&quantus_client, &from_account_id).await?;
151	let total_amount: u128 = transfers.iter().map(|(_, amount)| amount).sum();
152	let estimated_fee = 50_000_000_000u128; // Rough estimate for batch
153
154	if balance < total_amount + estimated_fee {
155		let formatted_balance =
156			crate::cli::send::format_balance_with_symbol(&quantus_client, balance).await?;
157		let formatted_needed = crate::cli::send::format_balance_with_symbol(
158			&quantus_client,
159			total_amount + estimated_fee,
160		)
161		.await?;
162		return Err(crate::error::QuantusError::Generic(format!(
163			"Insufficient balance. Have: {formatted_balance}, Need: {formatted_needed} (including estimated fees)"
164		)));
165	}
166
167	// Submit batch transaction
168	let tx_hash =
169		batch_transfer(&quantus_client, &keypair, transfers, tip_amount, finalized).await?;
170
171	log_print!(
172		"✅ {} Batch transaction submitted! Hash: {:?}",
173		"SUCCESS".bright_green().bold(),
174		tx_hash
175	);
176
177	log_success!("🎉 {} Batch transaction confirmed!", "FINISHED".bright_green().bold());
178
179	// Show updated balance
180	let new_balance = crate::cli::send::get_balance(&quantus_client, &from_account_id).await?;
181	let formatted_new_balance =
182		crate::cli::send::format_balance_with_symbol(&quantus_client, new_balance).await?;
183	log_print!("💰 New balance: {}", formatted_new_balance.bright_yellow());
184
185	Ok(())
186}
187
188/// Handle batch config command
189async fn handle_batch_config_command(
190	node_url: &str,
191	show_limits: bool,
192	show_info: bool,
193) -> Result<()> {
194	let quantus_client = QuantusClient::new(node_url).await?;
195
196	if show_limits {
197		log_info!("🔍 Checking batch transfer limits for chain...");
198
199		let (safe_limit, recommended_limit) = get_batch_limits(&quantus_client).await?;
200
201		log_print!("📊 {} Batch Transfer Limits", "CHAIN".bright_cyan().bold());
202		log_print!("   • Safe batch size: {} transfers", safe_limit.to_string().bright_green());
203		log_print!(
204			"   • Maximum batch size: {} transfers",
205			recommended_limit.to_string().bright_yellow()
206		);
207		log_print!("   • For larger batches, split into multiple transactions");
208	}
209
210	if show_info {
211		log_print!("ℹ️  {} Batch Transfer Information", "CONFIG".bright_cyan().bold());
212		log_print!("   • Batch transfers use utility.batch() pallet");
213		log_print!("   • All transfers in one transaction (atomic)");
214		log_print!("   • Single nonce used for all transfers");
215		log_print!("   • Lower fees compared to individual transfers");
216		log_print!("   • If one transfer fails, entire batch fails");
217		log_print!("");
218		log_print!("📝 {} Usage Examples", "EXAMPLES".bright_cyan().bold());
219		log_print!("   quantus batch send --from alice --count 100 --to bob --amount 1000");
220		log_print!("   quantus batch send --from alice --batch-file transfers.json");
221		log_print!("   quantus batch config --limits");
222	}
223
224	// If no flags provided, show both
225	if !show_limits && !show_info {
226		// Avoid recursion by calling the logic directly
227		let quantus_client = QuantusClient::new(node_url).await?;
228
229		// Show limits
230		log_info!("🔍 Checking batch transfer limits for chain...");
231		let (safe_limit, recommended_limit) = get_batch_limits(&quantus_client).await?;
232		log_print!("📊 {} Batch Transfer Limits", "CHAIN".bright_cyan().bold());
233		log_print!("   • Safe batch size: {} transfers", safe_limit.to_string().bright_green());
234		log_print!(
235			"   • Maximum batch size: {} transfers",
236			recommended_limit.to_string().bright_yellow()
237		);
238		log_print!("   • For larger batches, split into multiple transactions");
239
240		// Show info
241		log_print!("ℹ️  {} Batch Transfer Information", "CONFIG".bright_cyan().bold());
242		log_print!("   • Batch transfers use utility.batch() pallet");
243		log_print!("   • All transfers in one transaction (atomic)");
244		log_print!("   • Single nonce used for all transfers");
245		log_print!("   • Lower fees compared to individual transfers");
246		log_print!("   • If one transfer fails, entire batch fails");
247		log_print!("");
248		log_print!("📝 {} Usage Examples", "EXAMPLES".bright_cyan().bold());
249		log_print!("   quantus batch send --from alice --count 100 --to bob --amount 1000");
250		log_print!("   quantus batch send --from alice --batch-file transfers.json");
251		log_print!("   quantus batch config --limits");
252	}
253
254	Ok(())
255}