234 lines
7.8 KiB
Rust
234 lines
7.8 KiB
Rust
use blockchain::blocks::swap::UnsignedSwapTransaction;
|
|
use blockchain::common::cli_prompts::{prompt_hidden_nonempty, prompt_visible, prompt_wallet_path};
|
|
use blockchain::common::types::SWAP_FEE;
|
|
|
|
use blockchain::json;
|
|
use blockchain::standalone_tools::vanity_resolver::resolve_wallet_address_input;
|
|
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:<width$}"));
|
|
result
|
|
}
|
|
|
|
fn display_fee(value: u64) -> f64 {
|
|
value as f64 / 100_000_000.0
|
|
}
|
|
|
|
#[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;
|
|
|
|
let wallet_path = prompt_wallet_path().await;
|
|
|
|
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, Some(&wallet_path)).await {
|
|
Ok(wallet) => wallet,
|
|
Err(err) => {
|
|
eprintln!("Wallet decryption failed: {err}");
|
|
return;
|
|
}
|
|
};
|
|
|
|
// 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 resolve_wallet_address_input(&sender2, &wallet).await {
|
|
Ok(address) => address,
|
|
Err(err) => {
|
|
println!("receiver wallet is not valid: {err}");
|
|
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 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}");
|
|
}
|