pea-wallet 0.2.0

エンドウ
Documentation
use crate::Wallet;
use chrono::{Local, TimeZone};
use colored::*;
use crossterm::{event, terminal};
use inquire::{Confirm, CustomType, Select};
use pea_address as address;
use pea_amount as amount;
use pea_api::{get, post};
use pea_core::{constants::DECIMAL_PRECISION, stake::Stake, transaction::Transaction, types};
use std::process;
pub async fn main(wallet: &Wallet, api: &str) {
    match Select::new(
        ">>",
        vec![
            "Search",
            "Address",
            "Secret key",
            "Data",
            "Balance",
            "Height",
            "Transaction",
            "Stake",
            "Validator",
            "Exit",
        ],
    )
    .prompt()
    .unwrap_or_else(|err| {
        println!("{}", err.to_string().red());
        process::exit(0)
    }) {
        "Search" => search(api).await,
        "Address" => address(wallet),
        "Secret key" => key(wallet),
        "Data" => data(wallet),
        "Balance" => balance(api, &wallet.address()).await,
        "Height" => height(api).await,
        "Transaction" => transaction(api, wallet).await,
        "Stake" => stake(api, wallet).await,
        "Validator" => validator(api).await,
        "Exit" => exit(),
        _ => {}
    }
}
pub fn press_any_key_to_continue() {
    println!("{}", "Press any key to continue...".magenta().italic());
    terminal::enable_raw_mode().unwrap();
    event::read().unwrap();
    terminal::disable_raw_mode().unwrap();
    clear();
}
pub fn clear() {
    print!("\x1B[2J\x1B[1;1H");
}
async fn validator(api: &str) {
    match get::index(api).await {
        Ok(info) => println!("{}", info.green()),
        Err(err) => println!("{}", err.to_string().red()),
    };
}
async fn balance(api: &str, address: &str) {
    match get::balance(api, address).await {
        Ok(balance) => match get::balance_staked(api, address).await {
            Ok(balance_staked) => println!(
                "Account balance: {}, locked: {}.",
                (balance as f64 / DECIMAL_PRECISION as f64)
                    .to_string()
                    .yellow(),
                (balance_staked as f64 / DECIMAL_PRECISION as f64)
                    .to_string()
                    .yellow()
            ),
            Err(err) => println!("{}", err.to_string().red()),
        },
        Err(err) => println!("{}", err.to_string().red()),
    };
}
async fn height(api: &str) {
    match get::height(api).await {
        Ok(height) => println!("Latest block height is {}.", height.to_string().yellow()),
        Err(err) => println!("{}", err.to_string().red()),
    };
}
async fn transaction(api: &str, wallet: &Wallet) {
    let address = CustomType::<String>::new("Address:")
        .with_error_message("Please enter a valid address")
        .with_help_message("Type the hex encoded address with 0x as prefix")
        .with_parser(&|x| match address::public::decode(x) {
            Ok(y) => Ok(address::public::encode(&y)),
            Err(_) => Err(()),
        })
        .prompt()
        .unwrap_or_else(|err| {
            println!("{}", err.to_string().red());
            process::exit(0)
        });
    let amount = (CustomType::<f64>::new("Amount:")
        .with_formatter(&|i| format!("{:.18} pea", i)) // DECIMAL_PRECISION
        .with_error_message("Please type a valid number")
        .with_help_message("Type the amount in pea using a decimal point as a separator")
        .with_parser(&|x| match x.parse::<f64>() {
            Ok(f) => Ok(
                amount::round(&((f * DECIMAL_PRECISION as f64) as u128)) as f64
                    / DECIMAL_PRECISION as f64,
            ),
            Err(_) => Err(()),
        })
        .prompt()
        .unwrap_or_else(|err| {
            println!("{}", err.to_string().red());
            process::exit(0)
        })
        * DECIMAL_PRECISION as f64) as types::Amount;
    let fee = CustomType::<types::Amount>::new("Fee:")
        .with_formatter(&|i| format!("{} {}", i, if i == 1 { "satoshi" } else { "satoshis" }))
        .with_error_message("Please type a valid number")
        .with_help_message("Type the amount in satoshis using a decimal point as a separator")
        .prompt()
        .unwrap_or_else(|err| {
            println!("{}", err.to_string().red());
            process::exit(0)
        });
    if !match Confirm::new("Send?").prompt() {
        Ok(b) => b,
        Err(err) => {
            println!("{}", err.to_string().red());
            process::exit(0)
        }
    } {
        return;
    }
    let mut transaction = Transaction::new(address::public::decode(&address).unwrap(), amount, fee);
    transaction.sign(&wallet.keypair);
    println!("Hash: {}", hex::encode(transaction.hash()).cyan());
    match post::transaction(api, &transaction).await {
        Ok(res) => println!(
            "{}",
            if res == "success" {
                res.green()
            } else {
                res.red()
            }
        ),
        Err(err) => println!("{}", err.to_string().red()),
    };
}
async fn stake(api: &str, wallet: &Wallet) {
    let deposit = match Select::new(">>", vec!["deposit", "withdraw"])
        .prompt()
        .unwrap_or_else(|err| {
            println!("{}", err.to_string().red());
            process::exit(0)
        }) {
        "deposit" => true,
        "withdraw" => false,
        _ => false,
    };
    let amount = (CustomType::<f64>::new("Amount:")
        .with_formatter(&|i| format!("{:.18} pea", i)) // DECIMAL_PRECISION
        .with_error_message("Please type a valid number")
        .with_help_message("Type the amount in pea using a decimal point as a separator")
        .with_parser(&|x| match x.parse::<f64>() {
            Ok(f) => Ok(
                amount::round(&((f * DECIMAL_PRECISION as f64) as u128)) as f64
                    / DECIMAL_PRECISION as f64,
            ),
            Err(_) => Err(()),
        })
        .prompt()
        .unwrap_or_else(|err| {
            println!("{}", err.to_string().red());
            process::exit(0)
        })
        * DECIMAL_PRECISION as f64) as types::Amount;
    let fee = CustomType::<types::Amount>::new("Fee:")
        .with_formatter(&|i| format!("{} {}", i, if i == 1 { "satoshi" } else { "satoshis" }))
        .with_error_message("Please type a valid number")
        .with_help_message("Type the amount in satoshis using a decimal point as a separator")
        .prompt()
        .unwrap_or_else(|err| {
            println!("{}", err.to_string().red());
            process::exit(0)
        });
    if !match Confirm::new("Send?").prompt() {
        Ok(b) => b,
        Err(err) => {
            println!("{}", err.to_string().red());
            process::exit(0)
        }
    } {
        return;
    }
    let mut stake = Stake::new(deposit, amount as types::Amount, fee);
    stake.sign(&wallet.keypair);
    println!("Hash: {}", hex::encode(stake.hash()).cyan());
    match post::stake(api, &stake).await {
        Ok(res) => println!(
            "{}",
            if res == "success" {
                res.green()
            } else {
                res.red()
            }
        ),
        Err(err) => println!("{}", err.to_string().red()),
    };
}
fn address(wallet: &Wallet) {
    println!("{}", wallet.address().green());
}
async fn search(api: &str) {
    let search = CustomType::<String>::new("Search:")
        .with_error_message("Please enter a valid Address, Hash or Number.")
        .with_help_message("Search Blockchain, Transactions, Addresses, Blocks and Stakes")
        .with_parser(&|x| {
            if address::public::decode(x).is_ok() || x.len() == 64 || x.parse::<usize>().is_ok() {
                return Ok(x.to_string());
            }
            Err(())
        })
        .prompt()
        .unwrap_or_else(|err| {
            println!("{}", err.to_string().red());
            process::exit(0)
        });
    if address::public::decode(&search).is_ok() {
        balance(api, &search).await;
        return;
    } else if search.len() == 64 {
        match get::block(api, &search).await {
            Ok(block) => {
                print_block(&block, &search);
                return;
            }
            _ => {}
        };
        match get::transaction(api, &search).await {
            Ok(transaction) => {
                println!("{} {}", "Hash".cyan(), search);
                println!("{} {}", "Input PubKey".cyan(), transaction.public_key_input);
                println!(
                    "{} {}",
                    "Output PubKey".cyan(),
                    transaction.public_key_output
                );
                println!(
                    "{} {}",
                    "Amount".cyan(),
                    transaction.amount.to_string().yellow()
                );
                println!("{} {}", "Fee".cyan(), transaction.fee.to_string().yellow());
                println!(
                    "{} {}",
                    "Timestamp".cyan(),
                    Local
                        .timestamp(transaction.timestamp as i64, 0)
                        .format("%H:%M:%S")
                );
                println!("{} {}", "Signature".cyan(), transaction.signature);
                return;
            }
            _ => {}
        };
        match get::stake(api, &search).await {
            Ok(stake) => {
                println!("{} {}", "Hash".cyan(), search);
                println!("{} {}", "PubKey".cyan(), stake.public_key);
                println!("{} {}", "Amount".cyan(), stake.amount.to_string().yellow());
                println!(
                    "{}",
                    if stake.deposit {
                        "Deposit".magenta()
                    } else {
                        "Withdraw".magenta()
                    }
                );
                println!("{} {}", "Fee".cyan(), stake.fee.to_string().yellow());
                println!(
                    "{} {}",
                    "Timestamp".cyan(),
                    Local
                        .timestamp(stake.timestamp as i64, 0)
                        .format("%H:%M:%S")
                );
                println!("{} {}", "Signature".cyan(), stake.signature);
                return;
            }
            _ => {}
        };
    } else if search.parse::<usize>().is_ok() {
        match get::hash(api, &search.parse::<usize>().unwrap()).await {
            Ok(hash) => {
                match get::block(api, &hash).await {
                    Ok(block) => {
                        print_block(&block, &hash);
                        return;
                    }
                    _ => {}
                };
                return;
            }
            _ => {}
        };
    }
    println!("{}", "Nothing found".red());
    fn print_block(block: &get::Block, hash: &String) {
        println!("{} {}", "Hash".cyan(), hash);
        println!("{} {}", "PreviousHash".cyan(), block.previous_hash);
        println!(
            "{} {}",
            "Timestamp".cyan(),
            Local
                .timestamp(block.timestamp as i64, 0)
                .format("%H:%M:%S")
        );
        println!("{} {}", "Forger".cyan(), block.public_key);
        println!("{} {}", "Signature".cyan(), block.signature);
        println!(
            "{} {}",
            "Transactions".cyan(),
            block.transactions.len().to_string().yellow()
        );
        for (i, hash) in block.transactions.iter().enumerate() {
            println!("{} {}", format!("#{}", i).magenta(), hash)
        }
        println!(
            "{} {}",
            "Stakes".cyan(),
            block.stakes.len().to_string().yellow()
        );
        for (i, hash) in block.stakes.iter().enumerate() {
            println!("{} {}", format!("#{}", i).magenta(), hash)
        }
    }
}
fn key(wallet: &Wallet) {
    println!("{}", "Are you being watched?".yellow());
    println!("{}", "Never share your secret key!".yellow());
    println!(
        "{}",
        "Anyone who has it can access your funds from anywhere.".italic()
    );
    println!("{}", "View in private with no cameras around.".italic());
    if match Confirm::new("View secret key?").prompt() {
        Ok(b) => b,
        Err(err) => {
            println!("{}", err.to_string().red());
            process::exit(0)
        }
    } {
        println!("{}", wallet.key().red());
    }
}
fn data(wallet: &Wallet) {
    println!(
        "{}{}{}",
        hex::encode(&wallet.salt).red(),
        hex::encode(&wallet.nonce).red(),
        hex::encode(&wallet.ciphertext).red()
    );
}
fn exit() {
    process::exit(0);
}