From 0250e779ad11b2480f195b7f9466924ab83bfe92 Mon Sep 17 00:00:00 2001 From: kbizikav <132550763+kbizikav@users.noreply.github.com> Date: Wed, 21 Jan 2026 23:08:19 +0700 Subject: [PATCH] feat: view key option --- cli/src/args.rs | 84 ++++++++++++++++++++++++++-------------- cli/src/format.rs | 26 ++++++++++++- cli/src/main.rs | 98 ++++++++++++++++++++++++++++++----------------- 3 files changed, 144 insertions(+), 64 deletions(-) diff --git a/cli/src/args.rs b/cli/src/args.rs index 5faf0823..b7286e82 100644 --- a/cli/src/args.rs +++ b/cli/src/args.rs @@ -78,58 +78,78 @@ pub enum Commands { mining: bool, }, SyncWithdrawals { - #[clap(long)] - private_key: Bytes32, + #[clap(long, required_unless_present = "view_key")] + private_key: Option, + #[clap(long, required_unless_present = "private_key")] + view_key: Option, #[clap(long)] fee_token_index: Option, }, SyncClaims { - #[clap(long)] - private_key: Bytes32, + #[clap(long, required_unless_present = "view_key")] + private_key: Option, + #[clap(long, required_unless_present = "private_key")] + view_key: Option, #[clap(long)] recipient: Address, #[clap(long)] fee_token_index: Option, }, Balance { - #[clap(long)] - private_key: Bytes32, + #[clap(long, required_unless_present = "view_key")] + private_key: Option, + #[clap(long, required_unless_present = "private_key")] + view_key: Option, #[clap(long, default_value_t = false)] without_sync: bool, }, UserData { - #[clap(long)] - private_key: Bytes32, + #[clap(long, required_unless_present = "view_key")] + private_key: Option, + #[clap(long, required_unless_present = "private_key")] + view_key: Option, }, History { - #[clap(long)] - private_key: Bytes32, + #[clap(long, required_unless_present = "view_key")] + private_key: Option, + #[clap(long, required_unless_present = "private_key")] + view_key: Option, #[clap(long)] order: Option, // asc or desc #[clap(long)] from: Option, }, WithdrawalStatus { - #[clap(long)] - private_key: Bytes32, + #[clap(long, required_unless_present = "view_key")] + private_key: Option, + #[clap(long, required_unless_present = "private_key")] + view_key: Option, }, MiningList { - #[clap(long)] - private_key: Bytes32, + #[clap(long, required_unless_present = "view_key")] + private_key: Option, + #[clap(long, required_unless_present = "private_key")] + view_key: Option, }, ClaimStatus { - #[clap(long)] - private_key: Bytes32, + #[clap(long, required_unless_present = "view_key")] + private_key: Option, + #[clap(long, required_unless_present = "private_key")] + view_key: Option, }, ClaimWithdrawals { - #[clap(long)] - private_key: Bytes32, + #[clap(long, required_unless_present = "view_key")] + private_key: Option, + #[clap(long, required_unless_present = "private_key")] + view_key: Option, #[clap(long)] eth_private_key: Bytes32, }, PaymentMemos { - #[clap(long)] - private_key: Bytes32, + #[clap(long, required_unless_present = "view_key")] + private_key: Option, + #[clap(long, required_unless_present = "private_key")] + view_key: Option, #[clap(long)] name: String, }, @@ -138,14 +158,18 @@ pub enum Commands { eth_private_key: Bytes32, }, Resync { - #[clap(long)] - private_key: Bytes32, + #[clap(long, required_unless_present = "view_key")] + private_key: Option, + #[clap(long, required_unless_present = "private_key")] + view_key: Option, #[clap(long, default_value_t = false)] deep: bool, }, MakeBackup { - #[clap(long)] - private_key: Bytes32, + #[clap(long, required_unless_present = "view_key")] + private_key: Option, + #[clap(long, required_unless_present = "private_key")] + view_key: Option, #[clap(long)] dir: Option, #[clap(long)] @@ -156,16 +180,20 @@ pub enum Commands { path: PathBuf, }, GenerateReceipt { - #[clap(long)] - private_key: Bytes32, + #[clap(long, required_unless_present = "view_key")] + private_key: Option, + #[clap(long, required_unless_present = "private_key")] + view_key: Option, #[clap(long)] tx_digest: Bytes32, #[clap(long)] transfer_index: u32, }, VerifyReceipt { - #[clap(long)] - private_key: Bytes32, + #[clap(long, required_unless_present = "view_key")] + private_key: Option, + #[clap(long, required_unless_present = "private_key")] + view_key: Option, #[clap(long)] receipt: String, }, diff --git a/cli/src/format.rs b/cli/src/format.rs index 0263d7d3..df068c95 100644 --- a/cli/src/format.rs +++ b/cli/src/format.rs @@ -1,13 +1,15 @@ +use crate::cli::error::CliError; use intmax2_interfaces::{ data::deposit_data::TokenType, utils::{ - key::{KeyPair, PrivateKey}, + key::{KeyPair, PrivateKey, ViewPair}, key_derivation::derive_keypair_from_spend_key, }, }; use intmax2_zkp::ethereum_types::{ address::Address, bytes32::Bytes32, u256::U256, u32limb_trait::U32LimbTrait as _, }; +use std::str::FromStr; #[derive(Debug, thiserror::Error)] pub enum FormatTokenInfoError { @@ -70,3 +72,25 @@ pub fn privkey_to_keypair(privkey: Bytes32) -> KeyPair { .unwrap_or(false); derive_keypair_from_spend_key(PrivateKey(privkey.into()), is_legacy) } + +pub fn viewkey_to_viewpair( + viewkey: &str, +) -> Result { + ViewPair::from_str(viewkey) +} + +/// Resolves ViewPair from either private_key or view_key option +pub fn resolve_view_pair( + private_key: Option, + view_key: Option, +) -> Result { + match (private_key, view_key) { + (Some(pk), _) => Ok(privkey_to_keypair(pk).into()), + (None, Some(vk)) => { + viewkey_to_viewpair(&vk).map_err(|e| CliError::ParseError(e.to_string())) + } + (None, None) => Err(CliError::ParseError( + "Either --private-key or --view-key must be provided".to_string(), + )), + } +} diff --git a/cli/src/main.rs b/cli/src/main.rs index 8cc38f87..0f153e7c 100644 --- a/cli/src/main.rs +++ b/cli/src/main.rs @@ -21,7 +21,7 @@ use intmax2_cli::{ sync::{resync, sync_claims, sync_withdrawals}, withdrawal::send_withdrawal, }, - format::{format_token_info, privkey_to_keypair, TokenInput}, + format::{format_token_info, privkey_to_keypair, resolve_view_pair, TokenInput}, }; use intmax2_client_sdk::client::{ config::network_from_env, @@ -175,78 +175,104 @@ async fn main_process(command: Commands) -> Result<(), CliError> { } Commands::SyncWithdrawals { private_key, + view_key, fee_token_index, } => { - let key_pair = privkey_to_keypair(private_key); - sync_withdrawals(key_pair.into(), fee_token_index).await?; + let view_pair = resolve_view_pair(private_key, view_key)?; + sync_withdrawals(view_pair, fee_token_index).await?; } Commands::SyncClaims { private_key, + view_key, recipient, fee_token_index, } => { - let key_pair = privkey_to_keypair(private_key); - sync_claims(key_pair.into(), recipient, fee_token_index).await?; + let view_pair = resolve_view_pair(private_key, view_key)?; + sync_claims(view_pair, recipient, fee_token_index).await?; } Commands::ClaimBuilderReward { eth_private_key } => { claim_builder_reward(eth_private_key).await?; } Commands::Balance { private_key, + view_key, without_sync, } => { - let key_pair = privkey_to_keypair(private_key); - balance(key_pair.into(), !without_sync).await?; + let view_pair = resolve_view_pair(private_key, view_key)?; + balance(view_pair, !without_sync).await?; } - Commands::UserData { private_key } => { - let key_pair = privkey_to_keypair(private_key); - get_user_data(key_pair.into()).await?; + Commands::UserData { + private_key, + view_key, + } => { + let view_pair = resolve_view_pair(private_key, view_key)?; + get_user_data(view_pair).await?; } Commands::History { private_key, + view_key, order, from, } => { - let key_pair = privkey_to_keypair(private_key); + let view_pair = resolve_view_pair(private_key, view_key)?; let order = order.unwrap_or_default(); - history(key_pair.into(), order, from).await?; + history(view_pair, order, from).await?; } - Commands::WithdrawalStatus { private_key } => { - let key_pair = privkey_to_keypair(private_key); - withdrawal_status(key_pair.into()).await?; + Commands::WithdrawalStatus { + private_key, + view_key, + } => { + let view_pair = resolve_view_pair(private_key, view_key)?; + withdrawal_status(view_pair).await?; } - Commands::MiningList { private_key } => { - let key_pair = privkey_to_keypair(private_key); - mining_list(key_pair.into()).await?; + Commands::MiningList { + private_key, + view_key, + } => { + let view_pair = resolve_view_pair(private_key, view_key)?; + mining_list(view_pair).await?; } - Commands::ClaimStatus { private_key } => { - let key_pair = privkey_to_keypair(private_key); - claim_status(key_pair.into()).await?; + Commands::ClaimStatus { + private_key, + view_key, + } => { + let view_pair = resolve_view_pair(private_key, view_key)?; + claim_status(view_pair).await?; } - Commands::PaymentMemos { private_key, name } => { - let key_pair = privkey_to_keypair(private_key); - get_payment_memos(key_pair.into(), &name).await?; + Commands::PaymentMemos { + private_key, + view_key, + name, + } => { + let view_pair = resolve_view_pair(private_key, view_key)?; + get_payment_memos(view_pair, &name).await?; } Commands::ClaimWithdrawals { private_key, + view_key, eth_private_key, } => { - let key_pair = privkey_to_keypair(private_key); - claim_withdrawals(key_pair.into(), eth_private_key).await?; + let view_pair = resolve_view_pair(private_key, view_key)?; + claim_withdrawals(view_pair, eth_private_key).await?; } - Commands::Resync { private_key, deep } => { - let key_pair = privkey_to_keypair(private_key); - resync(key_pair.into(), deep).await?; + Commands::Resync { + private_key, + view_key, + deep, + } => { + let view_pair = resolve_view_pair(private_key, view_key)?; + resync(view_pair, deep).await?; } Commands::MakeBackup { private_key, + view_key, dir, from, } => { - let key_pair = privkey_to_keypair(private_key); + let view_pair = resolve_view_pair(private_key, view_key)?; let from = from.unwrap_or_default(); let dir = dir.unwrap_or_default(); - make_history_backup(key_pair.into(), &dir, from).await?; + make_history_backup(view_pair, &dir, from).await?; } Commands::IncorporateBackup { path } => { incorporate_backup(&path)?; @@ -256,18 +282,20 @@ async fn main_process(command: Commands) -> Result<(), CliError> { } Commands::GenerateReceipt { private_key, + view_key, tx_digest, transfer_index, } => { - let key_pair = privkey_to_keypair(private_key); - generate_receipt(key_pair.into(), tx_digest, transfer_index).await?; + let view_pair = resolve_view_pair(private_key, view_key)?; + generate_receipt(view_pair, tx_digest, transfer_index).await?; } Commands::VerifyReceipt { private_key, + view_key, receipt, } => { - let key_pair = privkey_to_keypair(private_key); - verify_receipt(key_pair.into(), &receipt).await?; + let view_pair = resolve_view_pair(private_key, view_key)?; + verify_receipt(view_pair, &receipt).await?; } Commands::GenerateKey => { let mut rng = default_rng();