2026-05-24 17:56:57 +00:00
use blockchain ::blocks ::nft ::{ CreateNftTransaction , UnsignedCreateNftTransaction } ;
use blockchain ::common ::cli_prompts ::{ prompt_hidden_nonempty , prompt_visible } ;
use blockchain ::common ::types ::CREATE_NFT_FEE ;
use blockchain ::json ;
use blockchain ::wallets ::structures ::Wallet ;
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
}
#[ tokio::main ]
async fn main ( ) {
// get user input series
let series_data = prompt_visible ( " Are you creating a 1/1 or a collection? " ) . await ;
let series = if series_data . trim ( ) = = " collection " {
1
} else {
0
} ;
// get user input ticker
let nft_ticker_name = prompt_visible ( " Please enter the name for your NFT. This can be 3 to 15 characters and must be 100% unique. First come first serve: " ) . await ;
let nft_name = pad_to_width ( nft_ticker_name . trim ( ) , 15 ) ;
// get the padded CID string
let ipfs_hash = prompt_visible ( " Enter your IPFS CID for your NFT. This will be stored in a fixed 100 character field, so shorter CIDs are padded with spaces: " ) . await ;
let item_ipfs = pad_to_width ( ipfs_hash . trim ( ) , 100 ) ;
// how many items in the collection or 1 for 1/1s
let count_string =
prompt_visible ( " Please enter the number of items in your collection, enter 1 for 1/1s: " )
. await ;
let count : u32 = count_string
. trim ( )
. parse ( )
. expect ( " Please enter a valid number " ) ;
// get the nft desc
let nft_desc = prompt_visible ( " Enter your nft description, A max of 100 characters: " ) . await ;
let desc = pad_to_width ( nft_desc . trim ( ) , 100 ) ;
// get user input txfee
let txfee_prompt = format! (
" Please enter the amount for the fee: (eg. 0.005, minimum fee {:.8}) " ,
display_fee ( CREATE_NFT_FEE )
) ;
let txfee_string = prompt_visible ( & format! ( " {txfee_prompt} : " ) ) . await ;
let txfee_f32 : f32 = txfee_string
. trim ( )
. parse ( )
. expect ( " Please enter a valid value. " ) ;
let txfee = ( ( txfee_f32 as f64 ) * ( 100000000_ f64 ) ) . round ( ) as u64 ;
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 ;
// set type and timestampe
let txtype = 4 ;
let timestamp = Utc ::now ( ) . timestamp ( ) as u32 ;
// validate wallets
if ! Wallet ::short_address_validation ( address . trim_matches ( '"' ) ) {
println! ( " creator wallet invalid: {} " , & address ) ;
return ;
}
let unsigned_create_nft = UnsignedCreateNftTransaction ::new (
txtype ,
timestamp ,
address . trim_matches ( '"' ) ,
series ,
& nft_name ,
& item_ipfs ,
count ,
& desc ,
txfee ,
)
. await ;
let create_nft = match CreateNftTransaction ::new ( unsigned_create_nft , private_key ) . await {
Ok ( tx ) = > tx ,
Err ( err ) = > {
eprintln! ( " Failed to sign NFT transaction: {err} " ) ;
return ;
}
} ;
let hashed_data = create_nft . unsigned_create_nft . hash ( ) . await ;
let signature = create_nft . signature . clone ( ) ;
//add the signature and hash
let output = json! ( {
" txtype " : txtype ,
" timestamp " : timestamp ,
" creator " : address . trim_matches ( '"' ) ,
" series " : series ,
" nft_name " : nft_name ,
" item_ipfs " : item_ipfs ,
" count " : count ,
" desc " : desc ,
" txfee " : txfee ,
" hash " : hashed_data ,
" signature " : signature
} ) ;
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} " ) ;
}