use blockchain::blocks::swap::UnsignedSwapTransaction; use blockchain::common::cli_prompts::{prompt_hidden_nonempty, prompt_visible}; use blockchain::common::types::SWAP_FEE; use blockchain::json; use blockchain::records::wallet_registry::resolve_local_input_short_address; use blockchain::wallets::structures::Wallet; use blockchain::Duration; use blockchain::File; use blockchain::Utc; use blockchain::{create_dir_all, AsyncWriteExt}; // pad the coin to ensure 15 characters fn pad_to_width(input: &str, width: usize) -> String { let mut result = String::with_capacity(width); // Pre-allocate string with capacity let _ = std::fmt::write(&mut result, format_args!("{input: f64 { value as f64 / 100_000_000.0 } fn normalize_short_address_input(address: &str) -> Result { resolve_local_input_short_address(address.trim()) } #[tokio::main] async fn main() { // set type and timestampe let txtype = 6; let timestamp = Utc::now().timestamp() as u32; // get user input tocken1 let ticker1_name = prompt_visible("Please enter the coin or token you wish to send during the swap: ").await; let ticker1_name = ticker1_name.trim().to_lowercase(); let ticker1 = pad_to_width(&ticker1_name, 15); let nft_series1_string = prompt_visible("Please enter the NFT series number for the asset you are sending, or 0 for coins/tokens/1of1 NFTs: ").await; let nft_series1: u32 = nft_series1_string .trim() .parse() .expect("Please enter a valid number"); // get user input value1 let value1_string = prompt_visible("Please enter the amount of coins or tokens to send: (eg. 23.4): ").await; let value1_f64: f64 = value1_string .trim() .parse() .expect("Please enter a valid age"); let value1 = (value1_f64 * (100000000_f64)).round() as u64; // get user input tocken2 let ticker2_name = prompt_visible("Please enter the coin or token you wish to receive during the swap: ") .await; let ticker2_name = ticker2_name.trim().to_lowercase(); let ticker2 = pad_to_width(&ticker2_name, 15); let nft_series2_string = prompt_visible("Please enter the NFT series number for the asset you expect to receive, or 0 for coins/tokens/1of1 NFTs: ").await; let nft_series2: u32 = nft_series2_string .trim() .parse() .expect("Please enter a valid number"); // get user input value2 let value2_string = prompt_visible( "Please enter the amount of coins or tokens you expect to receive: (eg. 23.4): ", ) .await; let value2_f64: f64 = value2_string .trim() .parse() .expect("Please enter a valid age"); let value2 = (value2_f64 * (100000000_f64)).round() as u64; // get user input swapper address let sender2 = prompt_visible("Please enter the wallet address of the account you are swapping with: ") .await; let sender2 = match normalize_short_address_input(&sender2) { Ok(address) => address, Err(_) => { println!("reciver wallet is not valid"); return; } }; // get user 1 tip let tip1 = prompt_visible( "Pleased enter the amount for the first miner tip: (must be at least 1% of token 1): ", ) .await; let tip1_f64: f64 = tip1.trim().parse().expect("Please enter a valid value."); let txtip1 = (tip1_f64 * (100000000_f64)).round() as u64; // get user user 2 tip let tip2 = prompt_visible( "Please enter the amount for the second miner tip: (must be at least 1% of token 2): ", ) .await; let tip2_f32: f64 = tip2.trim().parse().expect("Please enter a valid value."); let txtip2 = (tip2_f32 * (100000000_f64)).round() as u64; // get user input txfee1 let txfee1_prompt = format!( "Please enter the amount for the fee you will pay: (eg. 0.001, minimum fee {:.8} each party)", display_fee(SWAP_FEE) ); let txfee1_string = prompt_visible(&format!("{txfee1_prompt}: ")).await; let txfee1_f32: f64 = txfee1_string .trim() .parse() .expect("Please enter a valid value."); let txfee1 = (txfee1_f32 * (100000000_f64)).round() as u64; // get user input txfee2 let txfee2_prompt = format!( "Please enter the amount for the fee other party will pay pay: (eg. 0.001, minimum fee {:.8} each party)", display_fee(SWAP_FEE) ); let txfee2_string = prompt_visible(&format!("{txfee2_prompt}: ")).await; let txfee2_f32: f64 = txfee2_string .trim() .parse() .expect("Please enter a valid value."); let txfee2 = (txfee2_f32 * (100000000_f64)).round() as u64; // get expiration time in hours let expiration_time = prompt_visible("Please enter how many hours until the offer expires (eg. 72): ").await; let expiration_time: u64 = expiration_time.trim().parse().expect("Invalid input"); let expiration_duration = Duration::from_secs(expiration_time * 60 * 60); let expiration_time: u32 = expiration_duration.as_secs() as u32; let expires = timestamp + expiration_time; let decryption_key = prompt_hidden_nonempty( "What is your wallet decryption key? ", "Wallet key cannot be empty. Please try again.", ) .await; 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; // validate wallets if !Wallet::short_address_validation(address.trim_matches('"')) { println!("sender wallet invalid: {}", &address); return; } let unsigned_swap = UnsignedSwapTransaction::new( txtype, timestamp, expires, &ticker1, nft_series1, value1, &ticker2, nft_series2, value2, address.trim_matches('"'), &sender2, txtip1, txtip2, txfee1, txfee2, ) .await; let hashed_data = unsigned_swap.hash().await; let signature1 = match unsigned_swap.hash_and_sign(private_key).await { Ok(signature) => signature, Err(err) => { eprintln!("Failed to sign swap transaction: {err}"); return; } }; //add the signature and hash let output = json!({ "txtype": txtype, "timestamp": timestamp, "offer_expiration": expires, "ticker1": ticker1, "nft_series1": nft_series1, "value1": value1, "ticker2": ticker2, "nft_series2": nft_series2, "value2": value2, "sender1": address.trim_matches('"'), "sender2": sender2, "tip1": txtip1, "tip2": txtip2, "txfee1": txfee1, "txfee2": txfee2, "hash": hashed_data, "signature1": signature1, }); let output_str = serde_json::to_string_pretty(&output).expect("Failed to serialize JSON"); // Define the directory path let dir_path = "./transactions"; // Create the directory if it doesn't exist if let Err(e) = create_dir_all(&dir_path).await { eprintln!("Failed to create directory: {e}"); return; } // Define the file path let file_path = format!("{dir_path}/{hashed_data}.json"); // Open the file for writing asynchronously let mut file = File::create(&file_path) .await .expect("Failed to create file"); // Write the JSON string to the file asynchronously if let Err(e) = file.write_all(output_str.as_bytes()).await { eprintln!("Failed to write file: {e}"); return; } println!("transaction: {output_str}"); }