use blockchain::blocks::burn::{BurnTransaction, UnsignedBurnTransaction}; use blockchain::common::cli_prompts::{prompt_hidden_nonempty, prompt_visible}; use blockchain::common::network_paths_and_settings::block_extension_and_paths; use blockchain::common::types::BURN_FEE; use blockchain::json; use blockchain::wallets::structures::Wallet; use blockchain::File; use blockchain::Utc; use blockchain::{create_dir_all, AsyncWriteExt}; // Pad the asset name so it matches the 15-byte on-chain asset format. fn pad_to_width(input: &str, width: usize) -> String { let mut result = String::with_capacity(width); let _ = std::fmt::write( &mut result, format_args!("{input: f64 { // Fees are stored as atomic units and displayed as whole-coin decimals for the CLI. value as f64 / 100_000_000.0 } #[tokio::main] async fn main() { // Burn transactions use type 10 and the current local timestamp. let txtype = 10; let timestamp = Utc::now().timestamp() as u32; // Burnable assets are tokens or NFTs, never the base coin for this transaction type. let coin_name = prompt_visible("Please enter the token or NFT name you wish to burn: ").await; let coin = pad_to_width(&coin_name.trim().to_lowercase(), 15); if coin.trim().to_lowercase() == block_extension_and_paths().1.trim().to_lowercase() { println!("Base coin cannot be burned with this transaction type."); return; } // Series 0 is used for fungible tokens and 1/1 NFTs; series NFTs use their item number. let nft_series_string = prompt_visible( "Please enter the NFT series number to burn, or 0 for standard tokens / 1of1 NFTs: ", ) .await; let nft_series: u32 = nft_series_string .trim() .parse() .expect("Please enter a valid NFT series number."); // Values are entered as display units and saved as atomic units. let value_string = prompt_visible("Please enter the burn amount: (use 1.0 for NFTs) ").await; let value_f64: f64 = value_string .trim() .parse() .expect("Please enter a valid amount."); let value = (value_f64 * 100_000_000.0).round() as u64; let txfee_prompt = format!( "Please enter the amount for the fee: (eg. 0.0001, minimum fee {:.8})", display_fee(BURN_FEE) ); let txfee_string = prompt_visible(&format!("{txfee_prompt}: ")).await; let txfee_f64: f64 = txfee_string .trim() .parse() .expect("Please enter a valid fee."); let txfee = (txfee_f64 * 100_000_000.0).round() as u64; let decryption_key = prompt_hidden_nonempty( "What is your wallet decryption key? ", "Wallet key cannot be empty. Please try again.", ) .await; // Load the wallet that owns the asset being burned. let wallet = match Wallet::try_obtain_wallet(decryption_key, None).await { Ok(wallet) => wallet, Err(err) => { eprintln!("Wallet decryption failed: {err}"); return; } }; let private_key = &wallet.saved.private_key; let address = &wallet.saved.short_address; // The burn address must be a valid current-network short address. if !Wallet::short_address_validation(address.trim_matches('"')) { println!("burner wallet invalid: {}", &address); return; } // Build and sign the burn transaction before writing the broadcast JSON. let unsigned_burn = UnsignedBurnTransaction::new( txtype, timestamp, address.trim_matches('"'), &coin, nft_series, value, txfee, ) .await; let burn = match BurnTransaction::new(unsigned_burn, private_key).await { Ok(tx) => tx, Err(err) => { eprintln!("Failed to sign burn transaction: {err}"); return; } }; let hashed_data = burn.unsigned_burn.hash().await; let signature = burn.signature.clone(); // Save the signed transaction as JSON so broadcast_transaction can submit it later. let output = json!({ "txtype": txtype, "timestamp": timestamp, "address": address.trim_matches('"'), "coin": coin, "nft_series": nft_series, "value": value, "txfee": txfee, "hash": hashed_data, "signature": signature, }); let output_str = serde_json::to_string_pretty(&output).expect("Failed to serialize JSON"); // Save the transaction JSON into the standard transactions folder for broadcasting. let dir_path = "./transactions"; if let Err(error) = create_dir_all(&dir_path).await { eprintln!("Failed to create directory: {error}"); return; } let file_path = format!("{dir_path}/{hashed_data}.json"); let mut file = File::create(&file_path) .await .expect("Failed to create file"); if let Err(error) = file.write_all(output_str.as_bytes()).await { eprintln!("Failed to write file: {error}"); return; } println!("transaction: {output_str}"); }