2026-05-24 17:56:57 +00:00
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
2026-05-26 06:24:57 +00:00
let _ = std ::fmt ::write ( & mut result , format_args! ( " {input:<width$} " ) ) ;
2026-05-24 17:56:57 +00:00
result
}
fn display_fee ( value : u64 ) -> f64 {
value as f64 / 100_000_000.0
}
fn normalize_short_address_input ( address : & str ) -> Result < String , String > {
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} " ) ;
}