From cb2d6215d51bf3237d710420abb25ac8702b7c56 Mon Sep 17 00:00:00 2001 From: alexandre Date: Sat, 1 Feb 2025 15:12:15 +0100 Subject: [PATCH 1/4] [ADD] wallet commands --- src/args.rs | 4 +- src/infos.rs | 2 + src/main.rs | 2 +- src/wallet.rs | 275 ++++++++++++++++++++++++++++++++++++++++++++++---- 4 files changed, 258 insertions(+), 25 deletions(-) diff --git a/src/args.rs b/src/args.rs index 081cc33..d2b832a 100644 --- a/src/args.rs +++ b/src/args.rs @@ -1,6 +1,6 @@ use clap::{Parser, Subcommand, ValueHint}; -use crate::wallet::WalletSubcommands; +use crate::wallet::WalletsSubcommands; use crate::infos::InfosSubcommands; #[derive(Parser)] @@ -20,7 +20,7 @@ pub enum KermitSubcommand { #[command(visible_alias = "w")] Wallet { #[command(subcommand)] - command: WalletSubcommands, + command: WalletsSubcommands, }, #[command(visible_alias = "i")] Infos { diff --git a/src/infos.rs b/src/infos.rs index 1f4a7d5..027c5b6 100644 --- a/src/infos.rs +++ b/src/infos.rs @@ -3,6 +3,8 @@ use anyhow::{Ok, Result}; use clap::Parser; use serde_json::{json, Value}; +/// port : 22973 + /// CLI arguments for `kermit infos`. #[derive(Parser)] pub enum InfosSubcommands { diff --git a/src/main.rs b/src/main.rs index eadc371..8ea1dee 100644 --- a/src/main.rs +++ b/src/main.rs @@ -19,7 +19,7 @@ async fn run() -> Result<()> { let kermit = Kermit::parse(); match kermit.cmd { - KermitSubcommand::Wallet { command } => command.run().await?, + KermitSubcommand::Wallet { command } => command.run(&kermit.url).await?, KermitSubcommand::Infos { command } => command.run(&kermit.url).await?, } diff --git a/src/wallet.rs b/src/wallet.rs index 1dc81e9..d323d3d 100644 --- a/src/wallet.rs +++ b/src/wallet.rs @@ -1,32 +1,263 @@ -use anyhow::Result; +use crate::utils::{delete, get, post, put}; +use anyhow::{anyhow, Result}; + use clap::Parser; +use serde_json::{json, Value}; -/// CLI arguments for `kermit wallet`. +/// CLI arguments for `kermit wallets`. #[derive(Parser)] -pub enum WalletSubcommands { - /// Create a new random keypair. - #[command(visible_alias = "n")] - New { - /// Number of wallets to generate. - #[arg(long, short, default_value = "1")] - number: u32, +pub enum WalletsSubcommands { + /// List available wallets. + #[command(visible_alias = "lw")] + List, + + /// Restore a wallet from your mnemonic. + #[command(visible_alias = "rw")] + Restore { mnemonic: String }, + + /// Create a new wallet. + #[command(visible_alias = "cw")] + Create { + password: String, + wallet_name: String, + }, + + /// Get wallet's status. + #[command(visible_alias = "ws")] + Status { wallet_name: String }, + + /// Delete your wallet file (can be recovered with your mnemonic). + #[command(visible_alias = "dw")] + Delete { + wallet_name: String, + password: String, + }, + + /// Lock your wallet. + #[command(visible_alias = "wl")] + Lock { wallet_name: String }, + + /// Unlock your wallet. + #[command(visible_alias = "wu")] + Unlock { + wallet_name: String, + password: String, + }, + + /// Get your total balance. + #[command(visible_alias = "wb")] + Balances { wallet_name: String }, + + /// Reveal your mnemonic. !!! Use it with caution !!! + #[command(visible_alias = "rm")] + RevealMnemonic { + wallet_name: String, + password: String, + }, + + /// Transfer ALPH from the active address. + #[command(visible_alias = "wt")] + Transfer { + wallet_name: String, + to_address: String, + amount: String, + }, + + /// Transfer all unlocked ALPH from the active address to another address. + #[command(visible_alias = "wa")] + SweepActiveAddress { + wallet_name: String, + to_address: String, + }, + + /// Transfer unlocked ALPH from all addresses (including mining addresses) to another address. + #[command(visible_alias = "waa")] + SweepAllAddresses { + wallet_name: String, + to_address: String, + }, + + /// Sign the given data and return back the signature. + #[command(visible_alias = "wsign")] + Sign { wallet_name: String, data: String }, + + /// List all your wallet's addresses. + #[command(visible_alias = "wal")] + Addresses { wallet_name: String }, + + /// Get address' info. + #[command(visible_alias = "wai")] + AddressInfo { + wallet_name: String, + address: String, + }, + + /// Derive your next address. + #[command(visible_alias = "wna")] + DeriveNextAddress { + wallet_name: String, + group: Option, + }, + + /// Choose the active address. + #[command(visible_alias = "wca")] + ChangeActiveAddress { + wallet_name: String, + address: String, }, } -impl WalletSubcommands { - pub async fn run(self) -> Result<()> { - match self { - Self::New { number } => { - for _ in 0..number { - println!("Successfully created new keypair."); - println!("Address: {}", "0x1234"); - println!( - "Private key: 0x{}", - "1234567890123456789012345678901234567890123456789012345678901234" - ); - } - } +impl WalletsSubcommands { + pub async fn run(self, url: &str) -> Result<()> { + if url == "https://node.mainnet.alephium.org" { + eprintln!("You need to use a devnet adress (-u [ADDRESS]) to use Wallet commands"); + return Err(anyhow!( + "Invalid URL: Mainnet node is not allowed for wallet operations." + )); } + let value: Value = match self { + Self::List => get(url, "/wallets").await?, + Self::Restore { mnemonic } => { + put(url, "/wallets", json!({ "mnemonic": mnemonic })).await? + }, + Self::Create { + password, + wallet_name, + } => { + post( + url, + "/wallets", + json!({ + "password": password, + "walletName": wallet_name + }), + ) + .await? + }, + Self::Status { wallet_name } => get(url, &format!("/wallets/{}", wallet_name)).await?, + Self::Delete { + wallet_name, + password, + } => { + delete( + url, + &format!("/wallets/{}?password={}", wallet_name, password), + ) + .await?; + json!("Remove") + }, + Self::Lock { wallet_name } => { + post(url, &format!("/wallets/{}/lock", wallet_name), json!({})).await?; + json!("Remove") + }, + Self::Unlock { + wallet_name, + password, + } => { + post( + url, + &format!("/wallets/{}/unlock", wallet_name), + json!({ "password": password }), + ) + .await? + }, + Self::Balances { wallet_name } => { + get(url, &format!("/wallets/{}/balances", wallet_name)).await? + }, + Self::RevealMnemonic { + wallet_name, + password, + } => { + post( + url, + &format!("/wallets/{}/reveal-mnemonic", wallet_name), + json!({ "password": password }), + ) + .await? + }, + Self::Transfer { + wallet_name, + to_address, + amount, + } => { + post( + url, + &format!("/wallets/{}/transfer", wallet_name), + json!({ + "destinations": [ + { + "address": to_address, + "attoAlphAmount": amount + } + ] + + }), + ) + .await? + }, + Self::SweepActiveAddress { + wallet_name, + to_address, + } => { + post( + url, + &format!("/wallets/{}/sweep-active-address", wallet_name), + json!({ "toAddress": to_address }), + ) + .await? + }, + Self::SweepAllAddresses { + wallet_name, + to_address, + } => { + post( + url, + &format!("/wallets/{}/sweep-all-addresses", wallet_name), + json!({ "toAddress": to_address }), + ) + .await? + }, + Self::Sign { wallet_name, data } => { + post( + url, + &format!("/wallets/{}/sign", wallet_name), + json!({ "data": data }), + ) + .await? + }, + Self::Addresses { wallet_name } => { + get(url, &format!("/wallets/{}/addresses", wallet_name)).await? + }, + Self::AddressInfo { + wallet_name, + address, + } => { + get( + url, + &format!("/wallets/{}/addresses/{}", wallet_name, address), + ) + .await? + }, + Self::DeriveNextAddress { wallet_name, group } => { + let mut endpoint = format!("/wallets/{}/derive-next-address", wallet_name); + if let Some(group) = group { + endpoint.push_str(&format!("&toTs={}", group)); + } + post(url, &endpoint, json!({})).await? + }, + Self::ChangeActiveAddress { + wallet_name, + address, + } => { + post( + url, + &format!("/wallets/{}/change-active-address", wallet_name), + json!({ "address": address }), + ) + .await? + }, + }; + serde_json::to_writer_pretty(std::io::stdout(), &value)?; Ok(()) } From 8611a9916f205c6f4ec8147592fb6a6c55d350d4 Mon Sep 17 00:00:00 2001 From: alexandre Date: Sat, 1 Feb 2025 16:41:33 +0100 Subject: [PATCH 2/4] [FIX] update of all functions --- src/args.rs | 8 +++++ src/events.rs | 76 ++++++++++++++++++++++++++++++++++++++++++++ src/main.rs | 2 ++ src/utils/reqwest.rs | 6 ++-- src/wallet.rs | 11 ++++--- 5 files changed, 94 insertions(+), 9 deletions(-) create mode 100644 src/events.rs diff --git a/src/args.rs b/src/args.rs index d2b832a..7fb00d2 100644 --- a/src/args.rs +++ b/src/args.rs @@ -2,6 +2,7 @@ use clap::{Parser, Subcommand, ValueHint}; use crate::wallet::WalletsSubcommands; use crate::infos::InfosSubcommands; +use crate::events::EventsSubcommands; #[derive(Parser)] #[command(version)] @@ -22,9 +23,16 @@ pub enum KermitSubcommand { #[command(subcommand)] command: WalletsSubcommands, }, + /// Infos about node and hashrate. #[command(visible_alias = "i")] Infos { #[command(subcommand)] command: InfosSubcommands, + }, + /// Event for contract, block and hash. + #[command(visible_alias = "e")] + Events { + #[command(subcommand)] + command: EventsSubcommands, } } diff --git a/src/events.rs b/src/events.rs new file mode 100644 index 0000000..bfe7dfd --- /dev/null +++ b/src/events.rs @@ -0,0 +1,76 @@ +use crate::utils::get; +use anyhow::Result; +use clap::Parser; +use serde_json::Value; + +/// CLI arguments for `kermit events`. +#[derive(Parser)] +pub enum EventsSubcommands { + /// Get events for a contract within a counter range. + #[command(visible_alias = "ec")] + ContractEvents { + contract_address: String, + start: i32, + limit: Option, + group: Option, + }, + + /// Get the current value of the events counter for a contract. + #[command(visible_alias = "cc")] + ContractCurrentCount { contract_address: String }, + + /// Get contract events for a transaction. + #[command(visible_alias = "etc")] + TxContractEvents { tx_id: String, group: Option }, + + /// Get contract events for a block. + #[command(visible_alias = "ebh")] + BlockContractEvents { + block_hash: String, + group: Option, + }, +} + +impl EventsSubcommands { + pub async fn run(self, url: &str) -> Result<()> { + let value: Value = match self { + Self::ContractEvents { + contract_address, + start, + limit, + group, + } => { + let mut endpoint = format!("/events/contract/{}?start={}", contract_address, start); + if let Some(limit) = limit { + endpoint.push_str(&format!("&limit={}", limit)); + } + if let Some(group) = group { + endpoint.push_str(&format!("&limit={}", group)); + } + get(url, &endpoint).await? + }, + Self::ContractCurrentCount { contract_address } => { + let endpoint = format!("/events/contract/{}/current-count", contract_address); + get(url, &endpoint).await? + }, + Self::TxContractEvents { tx_id, group } => { + let mut endpoint = format!("/events/tx-id/{}", tx_id); + if let Some(group) = group { + endpoint.push_str(&format!("&limit={}", group)); + } + get(url, &endpoint).await? + }, + Self::BlockContractEvents { block_hash, group } => { + let mut endpoint = format!("/events/block-hash/{}", block_hash); + if let Some(group) = group { + endpoint.push_str(&format!("&limit={}", group)); + } + get(url, &endpoint).await? + }, + }; + + serde_json::to_writer_pretty(std::io::stdout(), &value)?; + + Ok(()) + } +} diff --git a/src/main.rs b/src/main.rs index 8ea1dee..7e21c56 100644 --- a/src/main.rs +++ b/src/main.rs @@ -2,6 +2,7 @@ mod args; mod utils; mod wallet; mod infos; +mod events; use anyhow::Result; use args::{Kermit, KermitSubcommand}; @@ -21,6 +22,7 @@ async fn run() -> Result<()> { match kermit.cmd { KermitSubcommand::Wallet { command } => command.run(&kermit.url).await?, KermitSubcommand::Infos { command } => command.run(&kermit.url).await?, + KermitSubcommand::Events { command } => command.run(&kermit.url).await?, } Ok(()) diff --git a/src/utils/reqwest.rs b/src/utils/reqwest.rs index a00c2dc..f9f59f5 100644 --- a/src/utils/reqwest.rs +++ b/src/utils/reqwest.rs @@ -84,7 +84,7 @@ pub async fn put( } /// Perform a DELETE request to the given URL -pub async fn delete(url: &str, endpoint: &str) -> Result { +pub async fn delete(url: &str, endpoint: &str) -> Result<()> { let client = Client::new(); let url = format!("{}{}", url, endpoint); @@ -100,7 +100,5 @@ pub async fn delete(url: &str, endpoint: &str) -> Result bail!(err.detail); } - let data = res.json().await?; - - Ok(data) + Ok(()) } diff --git a/src/wallet.rs b/src/wallet.rs index d323d3d..25b838b 100644 --- a/src/wallet.rs +++ b/src/wallet.rs @@ -18,8 +18,8 @@ pub enum WalletsSubcommands { /// Create a new wallet. #[command(visible_alias = "cw")] Create { - password: String, wallet_name: String, + password: String, }, /// Get wallet's status. @@ -121,8 +121,8 @@ impl WalletsSubcommands { put(url, "/wallets", json!({ "mnemonic": mnemonic })).await? }, Self::Create { - password, wallet_name, + password, } => { post( url, @@ -144,11 +144,11 @@ impl WalletsSubcommands { &format!("/wallets/{}?password={}", wallet_name, password), ) .await?; - json!("Remove") + json!("Remove") }, Self::Lock { wallet_name } => { - post(url, &format!("/wallets/{}/lock", wallet_name), json!({})).await?; - json!("Remove") + post::<(), Value>(url, &format!("/wallets/{}/lock", wallet_name), Value::Null).await?; + json!("Ok") }, Self::Unlock { wallet_name, @@ -257,6 +257,7 @@ impl WalletsSubcommands { .await? }, }; + serde_json::to_writer_pretty(std::io::stdout(), &value)?; Ok(()) From 831b07f4805b3106448ee2623f4573ff0ed05b33 Mon Sep 17 00:00:00 2001 From: alexandre Date: Sat, 1 Feb 2025 16:43:06 +0100 Subject: [PATCH 3/4] [FIX] wallets and comment --- src/args.rs | 2 +- src/infos.rs | 2 -- src/main.rs | 2 +- 3 files changed, 2 insertions(+), 4 deletions(-) diff --git a/src/args.rs b/src/args.rs index 7fb00d2..52a4791 100644 --- a/src/args.rs +++ b/src/args.rs @@ -19,7 +19,7 @@ pub struct Kermit { pub enum KermitSubcommand { /// Wallet management utilities. #[command(visible_alias = "w")] - Wallet { + Wallets { #[command(subcommand)] command: WalletsSubcommands, }, diff --git a/src/infos.rs b/src/infos.rs index 027c5b6..1f4a7d5 100644 --- a/src/infos.rs +++ b/src/infos.rs @@ -3,8 +3,6 @@ use anyhow::{Ok, Result}; use clap::Parser; use serde_json::{json, Value}; -/// port : 22973 - /// CLI arguments for `kermit infos`. #[derive(Parser)] pub enum InfosSubcommands { diff --git a/src/main.rs b/src/main.rs index 7e21c56..ee6d206 100644 --- a/src/main.rs +++ b/src/main.rs @@ -20,7 +20,7 @@ async fn run() -> Result<()> { let kermit = Kermit::parse(); match kermit.cmd { - KermitSubcommand::Wallet { command } => command.run(&kermit.url).await?, + KermitSubcommand::Wallets { command } => command.run(&kermit.url).await?, KermitSubcommand::Infos { command } => command.run(&kermit.url).await?, KermitSubcommand::Events { command } => command.run(&kermit.url).await?, } From 228ac1fb79af93c65db9bd0128e66e5803d63d29 Mon Sep 17 00:00:00 2001 From: alexandre Date: Sat, 1 Feb 2025 16:45:11 +0100 Subject: [PATCH 4/4] [FIX] comment --- src/wallet.rs | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/wallet.rs b/src/wallet.rs index 25b838b..8f60bce 100644 --- a/src/wallet.rs +++ b/src/wallet.rs @@ -144,11 +144,12 @@ impl WalletsSubcommands { &format!("/wallets/{}?password={}", wallet_name, password), ) .await?; - json!("Remove") + json!("wallet remove") }, Self::Lock { wallet_name } => { - post::<(), Value>(url, &format!("/wallets/{}/lock", wallet_name), Value::Null).await?; - json!("Ok") + post::<(), Value>(url, &format!("/wallets/{}/lock", wallet_name), Value::Null) + .await?; + json!("wallet lock") }, Self::Unlock { wallet_name,