use crate::{
chain::client::QuantusClient,
cli::send::{
build_batch_transfer_call, checked_add, ensure_balance_covers_call, get_batch_limits,
load_transfers_from_file, submit_prebuilt_batch_transfer_call, validate_and_format_amount,
validate_batch_transfer_request,
},
error::Result,
log_info, log_print, log_success, log_verbose,
};
use clap::Subcommand;
use colored::Colorize;
#[derive(Subcommand, Debug)]
pub enum BatchCommands {
Send {
#[arg(short, long)]
from: String,
#[arg(short, long)]
password: Option<String>,
#[arg(long)]
password_file: Option<String>,
#[arg(long)]
tip: Option<String>,
#[arg(long)]
batch_file: Option<String>,
#[arg(long)]
count: Option<u32>,
#[arg(long)]
to: Option<String>,
#[arg(long)]
amount: Option<String>,
},
Config {
#[arg(long)]
limits: bool,
#[arg(long)]
info: bool,
},
}
pub async fn handle_batch_command(
command: BatchCommands,
node_url: &str,
execution_mode: crate::cli::common::ExecutionMode,
) -> Result<()> {
match command {
BatchCommands::Send {
from,
password,
password_file,
tip,
batch_file,
count,
to,
amount,
} =>
handle_batch_send_command(
from,
node_url,
password,
password_file,
tip,
batch_file,
count,
to,
amount,
execution_mode,
)
.await,
BatchCommands::Config { limits, info } =>
handle_batch_config_command(node_url, limits, info).await,
}
}
async fn handle_batch_send_command(
from_wallet: String,
node_url: &str,
password: Option<String>,
password_file: Option<String>,
tip: Option<String>,
batch_file: Option<String>,
count: Option<u32>,
to: Option<String>,
amount: Option<String>,
execution_mode: crate::cli::common::ExecutionMode,
) -> Result<()> {
let quantus_client = QuantusClient::new(node_url).await?;
let transfers = if let Some(file_path) = batch_file {
load_transfers_from_file(&file_path).await?
} else if let (Some(count_val), Some(to_addr), Some(amount_str)) = (count, to, amount) {
let (parsed_amount, _) = validate_and_format_amount(&quantus_client, &amount_str).await?;
let mut transfers = Vec::new();
for _ in 0..count_val {
transfers.push((to_addr.clone(), parsed_amount));
}
transfers
} else {
return Err(crate::error::QuantusError::Generic(
"Either --batch-file or (--count + --to + --amount) must be provided".to_string(),
));
};
if transfers.is_empty() {
return Err(crate::error::QuantusError::Generic("No transfers to process".to_string()));
}
log_info!("🚀 Initiating batch transfer with {} transfers", transfers.len());
let tip_amount = if let Some(tip_str) = tip {
let (tip_val, _) = validate_and_format_amount(&quantus_client, &tip_str).await?;
Some(tip_val)
} else {
None
};
let keypair = crate::wallet::load_keypair_from_wallet(&from_wallet, password, password_file)?;
let from_account_id = keypair.to_account_id_ss58check();
validate_batch_transfer_request(&quantus_client, &keypair, &transfers).await?;
let effective_tip = crate::cli::send::effective_tip_amount(tip_amount);
let submit_tip = crate::cli::send::positive_tip_amount(tip_amount);
let balance = crate::cli::send::get_balance(&quantus_client, &from_account_id).await?;
let total_amount = transfers
.iter()
.try_fold(0u128, |acc, (_, amount)| checked_add(acc, *amount, "batch transfer total"))?;
let exact_required = checked_add(total_amount, effective_tip, "required batch balance")?;
log_verbose!("✍️ Creating batch extrinsic with {} calls...", transfers.len());
let batch_call = build_batch_transfer_call(&transfers)?;
ensure_balance_covers_call(
&quantus_client,
&keypair,
&batch_call,
balance,
exact_required,
submit_tip,
"batch",
)
.await?;
let tx_hash = submit_prebuilt_batch_transfer_call(
&quantus_client,
&keypair,
&transfers,
batch_call,
tip_amount,
execution_mode,
)
.await?;
let transaction_stage = execution_mode.transaction_stage();
log_print!(
"✅ {} Batch transaction {}. Hash: {:?}",
"SUCCESS".bright_green().bold(),
transaction_stage.status_label(),
tx_hash
);
if !execution_mode.should_watch_transaction() {
log_print!(
"ℹ️ The batch transaction was {} but this command did not wait for block inclusion. Use --wait-for-transaction or --finalized-tx to wait before returning.",
transaction_stage.success_detail()
);
return Ok(());
}
log_success!(
"🎉 {} Batch transaction {}.",
"FINISHED".bright_green().bold(),
transaction_stage.success_detail()
);
let new_balance = crate::cli::send::get_balance(&quantus_client, &from_account_id).await?;
let formatted_new_balance =
crate::cli::send::format_balance_with_symbol(&quantus_client, new_balance).await?;
log_print!("💰 New balance: {}", formatted_new_balance.bright_yellow());
Ok(())
}
async fn handle_batch_config_command(
node_url: &str,
show_limits: bool,
show_info: bool,
) -> Result<()> {
let quantus_client = QuantusClient::new(node_url).await?;
if show_limits {
log_info!("🔍 Checking batch transfer limits for chain...");
let (safe_limit, recommended_limit) = get_batch_limits(&quantus_client).await?;
log_print!("📊 {} Batch Transfer Limits", "CHAIN".bright_cyan().bold());
log_print!(" • Safe batch size: {} transfers", safe_limit.to_string().bright_green());
log_print!(
" • Maximum batch size: {} transfers",
recommended_limit.to_string().bright_yellow()
);
log_print!(" • For larger batches, split into multiple transactions");
}
if show_info {
log_print!("ℹ️ {} Batch Transfer Information", "CONFIG".bright_cyan().bold());
log_print!(" • Batch transfers use utility.batch() pallet");
log_print!(" • All transfers in one transaction (atomic)");
log_print!(" • Single nonce used for all transfers");
log_print!(" • Lower fees compared to individual transfers");
log_print!(" • If one transfer fails, entire batch fails");
log_print!("");
log_print!("📝 {} Usage Examples", "EXAMPLES".bright_cyan().bold());
log_print!(" quantus batch send --from alice --count 100 --to bob --amount 1000");
log_print!(" quantus batch send --from alice --batch-file transfers.json");
log_print!(" quantus batch config --limits");
}
if !show_limits && !show_info {
let quantus_client = QuantusClient::new(node_url).await?;
log_info!("🔍 Checking batch transfer limits for chain...");
let (safe_limit, recommended_limit) = get_batch_limits(&quantus_client).await?;
log_print!("📊 {} Batch Transfer Limits", "CHAIN".bright_cyan().bold());
log_print!(" • Safe batch size: {} transfers", safe_limit.to_string().bright_green());
log_print!(
" • Maximum batch size: {} transfers",
recommended_limit.to_string().bright_yellow()
);
log_print!(" • For larger batches, split into multiple transactions");
log_print!("ℹ️ {} Batch Transfer Information", "CONFIG".bright_cyan().bold());
log_print!(" • Batch transfers use utility.batch() pallet");
log_print!(" • All transfers in one transaction (atomic)");
log_print!(" • Single nonce used for all transfers");
log_print!(" • Lower fees compared to individual transfers");
log_print!(" • If one transfer fails, entire batch fails");
log_print!("");
log_print!("📝 {} Usage Examples", "EXAMPLES".bright_cyan().bold());
log_print!(" quantus batch send --from alice --count 100 --to bob --amount 1000");
log_print!(" quantus batch send --from alice --batch-file transfers.json");
log_print!(" quantus batch config --limits");
}
Ok(())
}