diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 456277c2d..20cfe6fb2 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -20,7 +20,7 @@ jobs: - name: Get the release version from the tag # if: env.NEAR_CLI_VERSION == '' run: | - echo "NEAR_CLI_VERSION=0.1.9" >> $GITHUB_ENV + echo "NEAR_CLI_VERSION=0.1.10" >> $GITHUB_ENV echo "version is: ${{ env.NEAR_CLI_VERSION }}" - name: Create GitHub release diff --git a/src/commands/add_command/access_key/sender/mod.rs b/src/commands/add_command/access_key/sender/mod.rs index 9408dad94..a678544f4 100644 --- a/src/commands/add_command/access_key/sender/mod.rs +++ b/src/commands/add_command/access_key/sender/mod.rs @@ -25,8 +25,20 @@ impl Sender { connection_config: Option, ) -> color_eyre::eyre::Result { let sender_account_id: String = match item.sender_account_id { - Some(cli_sender_account_id) => cli_sender_account_id, - None => Sender::input_sender_account_id(), + Some(cli_sender_account_id) => match &connection_config { + Some(network_connection_config) => match crate::common::check_account_id( + network_connection_config.clone(), + cli_sender_account_id.clone(), + )? { + Some(_) => cli_sender_account_id, + None => { + println!("Account <{}> doesn't exist", cli_sender_account_id); + Sender::input_sender_account_id(connection_config.clone())? + } + }, + None => cli_sender_account_id, + }, + None => Sender::input_sender_account_id(connection_config.clone())?, }; let public_key_mode = match item.public_key_mode { Some(cli_public_key_mode) => super::public_key_mode::PublicKeyMode::from( @@ -47,12 +59,26 @@ impl Sender { } impl Sender { - pub fn input_sender_account_id() -> String { - println!(); - Input::new() - .with_prompt("What is the account ID of the sender?") - .interact_text() - .unwrap() + fn input_sender_account_id( + connection_config: Option, + ) -> color_eyre::eyre::Result { + loop { + let account_id: String = Input::new() + .with_prompt("What account ID do you need to add a key?") + .interact_text() + .unwrap(); + if let Some(connection_config) = &connection_config { + if let Some(_) = + crate::common::check_account_id(connection_config.clone(), account_id.clone())? + { + break Ok(account_id); + } else { + println!("Account <{}> doesn't exist", account_id); + } + } else { + break Ok(account_id); + } + } } pub async fn process( diff --git a/src/commands/add_command/contract_code/sender/mod.rs b/src/commands/add_command/contract_code/sender/mod.rs index 18f3468bf..d6e450716 100644 --- a/src/commands/add_command/contract_code/sender/mod.rs +++ b/src/commands/add_command/contract_code/sender/mod.rs @@ -25,8 +25,20 @@ impl Sender { connection_config: Option, ) -> color_eyre::eyre::Result { let sender_account_id: String = match item.sender_account_id { - Some(cli_sender_account_id) => cli_sender_account_id, - None => Sender::input_sender_account_id(), + Some(cli_sender_account_id) => match &connection_config { + Some(network_connection_config) => match crate::common::check_account_id( + network_connection_config.clone(), + cli_sender_account_id.clone(), + )? { + Some(_) => cli_sender_account_id, + None => { + println!("Account <{}> doesn't exist", cli_sender_account_id); + Sender::input_sender_account_id(connection_config.clone())? + } + }, + None => cli_sender_account_id, + }, + None => Sender::input_sender_account_id(connection_config.clone())?, }; let contract = match item.contract { Some(cli_contract) => super::contract::Contract::from( @@ -47,12 +59,26 @@ impl Sender { } impl Sender { - pub fn input_sender_account_id() -> String { - println!(); - Input::new() - .with_prompt("What is the account ID of the contract?") - .interact_text() - .unwrap() + fn input_sender_account_id( + connection_config: Option, + ) -> color_eyre::eyre::Result { + loop { + let account_id: String = Input::new() + .with_prompt("What is the account ID of the contract?") + .interact_text() + .unwrap(); + if let Some(connection_config) = &connection_config { + if let Some(_) = + crate::common::check_account_id(connection_config.clone(), account_id.clone())? + { + break Ok(account_id); + } else { + println!("Account <{}> doesn't exist", account_id); + } + } else { + break Ok(account_id); + } + } } pub async fn process( diff --git a/src/commands/add_command/stake_proposal/sender/mod.rs b/src/commands/add_command/stake_proposal/sender/mod.rs index 9ea808eb8..22db3b968 100644 --- a/src/commands/add_command/stake_proposal/sender/mod.rs +++ b/src/commands/add_command/stake_proposal/sender/mod.rs @@ -25,8 +25,20 @@ impl Sender { connection_config: Option, ) -> color_eyre::eyre::Result { let sender_account_id: String = match item.sender_account_id { - Some(cli_sender_account_id) => cli_sender_account_id, - None => Sender::input_sender_account_id(), + Some(cli_sender_account_id) => match &connection_config { + Some(network_connection_config) => match crate::common::check_account_id( + network_connection_config.clone(), + cli_sender_account_id.clone(), + )? { + Some(_) => cli_sender_account_id, + None => { + println!("Account <{}> doesn't exist", cli_sender_account_id); + Sender::input_sender_account_id(connection_config.clone())? + } + }, + None => cli_sender_account_id, + }, + None => Sender::input_sender_account_id(connection_config.clone())?, }; let transfer: super::transfer_near_tokens_type::Transfer = match item.transfer { Some(cli_transfer) => super::transfer_near_tokens_type::Transfer::from( @@ -47,12 +59,26 @@ impl Sender { } impl Sender { - fn input_sender_account_id() -> String { - println!(); - Input::new() - .with_prompt("What is the account ID of the validator?") - .interact_text() - .unwrap() + fn input_sender_account_id( + connection_config: Option, + ) -> color_eyre::eyre::Result { + loop { + let account_id: String = Input::new() + .with_prompt("What is the account ID of the validator?") + .interact_text() + .unwrap(); + if let Some(connection_config) = &connection_config { + if let Some(_) = + crate::common::check_account_id(connection_config.clone(), account_id.clone())? + { + break Ok(account_id); + } else { + println!("Account <{}> doesn't exist", account_id); + } + } else { + break Ok(account_id); + } + } } pub async fn process( diff --git a/src/commands/add_command/sub_account/sender/mod.rs b/src/commands/add_command/sub_account/sender/mod.rs index 981ff8185..8361816e2 100644 --- a/src/commands/add_command/sub_account/sender/mod.rs +++ b/src/commands/add_command/sub_account/sender/mod.rs @@ -25,8 +25,20 @@ impl Sender { connection_config: Option, ) -> color_eyre::eyre::Result { let owner_account_id: String = match item.owner_account_id { - Some(cli_owner_account_id) => cli_owner_account_id, - None => Sender::input_owner_account_id(), + Some(cli_owner_account_id) => match &connection_config { + Some(network_connection_config) => match crate::common::check_account_id( + network_connection_config.clone(), + cli_owner_account_id.clone(), + )? { + Some(_) => cli_owner_account_id, + None => { + println!("Account <{}> doesn't exist", cli_owner_account_id); + Sender::input_owner_account_id(connection_config.clone())? + } + }, + None => cli_owner_account_id, + }, + None => Sender::input_owner_account_id(connection_config.clone())?, }; let send_to: super::receiver::SendTo = match item.send_to { Some(cli_send_to) => super::receiver::SendTo::from( @@ -44,12 +56,26 @@ impl Sender { } impl Sender { - fn input_owner_account_id() -> String { - println!(); - Input::new() - .with_prompt("What is the owner account ID?") - .interact_text() - .unwrap() + fn input_owner_account_id( + connection_config: Option, + ) -> color_eyre::eyre::Result { + loop { + let account_id: String = Input::new() + .with_prompt("What is the owner account ID?") + .interact_text() + .unwrap(); + if let Some(connection_config) = &connection_config { + if let Some(_) = + crate::common::check_account_id(connection_config.clone(), account_id.clone())? + { + break Ok(account_id); + } else { + println!("Account <{}> doesn't exist", account_id); + } + } else { + break Ok(account_id); + } + } } pub async fn process( diff --git a/src/commands/construct_transaction_command/sender/mod.rs b/src/commands/construct_transaction_command/sender/mod.rs index d667181ef..b5ae26517 100644 --- a/src/commands/construct_transaction_command/sender/mod.rs +++ b/src/commands/construct_transaction_command/sender/mod.rs @@ -25,8 +25,20 @@ impl Sender { connection_config: Option, ) -> color_eyre::eyre::Result { let sender_account_id: String = match item.sender_account_id { - Some(cli_sender_account_id) => cli_sender_account_id, - None => Sender::input_sender_account_id(), + Some(cli_sender_account_id) => match &connection_config { + Some(network_connection_config) => match crate::common::check_account_id( + network_connection_config.clone(), + cli_sender_account_id.clone(), + )? { + Some(_) => cli_sender_account_id, + None => { + println!("Account <{}> doesn't exist", cli_sender_account_id); + Sender::input_sender_account_id(connection_config.clone())? + } + }, + None => cli_sender_account_id, + }, + None => Sender::input_sender_account_id(connection_config.clone())?, }; let send_to: super::receiver::SendTo = match item.send_to { Some(cli_send_to) => super::receiver::SendTo::from( @@ -44,12 +56,26 @@ impl Sender { } impl Sender { - pub fn input_sender_account_id() -> String { - println!(); - Input::new() - .with_prompt("What is the account ID of the sender?") - .interact_text() - .unwrap() + fn input_sender_account_id( + connection_config: Option, + ) -> color_eyre::eyre::Result { + loop { + let account_id: String = Input::new() + .with_prompt("What is the account ID of the sender?") + .interact_text() + .unwrap(); + if let Some(connection_config) = &connection_config { + if let Some(_) = + crate::common::check_account_id(connection_config.clone(), account_id.clone())? + { + break Ok(account_id); + } else { + println!("Account <{}> doesn't exist", account_id); + } + } else { + break Ok(account_id); + } + } } pub async fn process( diff --git a/src/commands/delete_command/access_key/sender/mod.rs b/src/commands/delete_command/access_key/sender/mod.rs index b5c993d80..b6b349746 100644 --- a/src/commands/delete_command/access_key/sender/mod.rs +++ b/src/commands/delete_command/access_key/sender/mod.rs @@ -25,8 +25,20 @@ impl Sender { connection_config: Option, ) -> color_eyre::eyre::Result { let sender_account_id: String = match item.sender_account_id { - Some(cli_sender_account_id) => cli_sender_account_id, - None => Sender::input_sender_account_id(), + Some(cli_sender_account_id) => match &connection_config { + Some(network_connection_config) => match crate::common::check_account_id( + network_connection_config.clone(), + cli_sender_account_id.clone(), + )? { + Some(_) => cli_sender_account_id, + None => { + println!("Account <{}> doesn't exist", cli_sender_account_id); + Sender::input_sender_account_id(connection_config.clone())? + } + }, + None => cli_sender_account_id, + }, + None => Sender::input_sender_account_id(connection_config.clone())?, }; let public_key = match item.public_key { Some(cli_delete_access_key) => super::DeleteAccessKeyAction::from( @@ -47,12 +59,26 @@ impl Sender { } impl Sender { - fn input_sender_account_id() -> String { - println!(); - Input::new() - .with_prompt("Which account ID do you need to remove the key from?") - .interact_text() - .unwrap() + fn input_sender_account_id( + connection_config: Option, + ) -> color_eyre::eyre::Result { + loop { + let account_id: String = Input::new() + .with_prompt("Which account ID do you need to remove the key from?") + .interact_text() + .unwrap(); + if let Some(connection_config) = &connection_config { + if let Some(_) = + crate::common::check_account_id(connection_config.clone(), account_id.clone())? + { + break Ok(account_id); + } else { + println!("Account <{}> doesn't exist", account_id); + } + } else { + break Ok(account_id); + } + } } pub async fn process( diff --git a/src/commands/delete_command/account/mod.rs b/src/commands/delete_command/account/mod.rs index e34aae92d..e6fc8e7d1 100644 --- a/src/commands/delete_command/account/mod.rs +++ b/src/commands/delete_command/account/mod.rs @@ -32,8 +32,20 @@ impl DeleteAccountAction { sender_account_id: String, ) -> color_eyre::eyre::Result { let beneficiary_id: near_primitives::types::AccountId = match item.beneficiary_id { - Some(cli_account_id) => cli_account_id, - None => DeleteAccountAction::input_beneficiary_id(), + Some(cli_account_id) => match &connection_config { + Some(network_connection_config) => match crate::common::check_account_id( + network_connection_config.clone(), + cli_account_id.clone(), + )? { + Some(_) => cli_account_id, + None => { + println!("Account <{}> doesn't exist", cli_account_id); + DeleteAccountAction::input_beneficiary_id(connection_config.clone())? + } + }, + None => cli_account_id, + }, + None => DeleteAccountAction::input_beneficiary_id(connection_config.clone())?, }; let sign_option = match item.sign_option { Some(cli_sign_transaction) => crate::commands::construct_transaction_command::sign_transaction::SignTransaction::from(cli_sign_transaction, connection_config, sender_account_id)?, @@ -47,12 +59,26 @@ impl DeleteAccountAction { } impl DeleteAccountAction { - pub fn input_beneficiary_id() -> near_primitives::types::AccountId { - println!(); - Input::new() - .with_prompt("Enter the beneficiary ID to delete this account ID") - .interact_text() - .unwrap() + pub fn input_beneficiary_id( + connection_config: Option, + ) -> color_eyre::eyre::Result { + loop { + let account_id: String = Input::new() + .with_prompt("Enter the beneficiary ID to delete this account ID") + .interact_text() + .unwrap(); + if let Some(connection_config) = &connection_config { + if let Some(_) = + crate::common::check_account_id(connection_config.clone(), account_id.clone())? + { + break Ok(account_id); + } else { + println!("Account <{}> doesn't exist", account_id); + } + } else { + break Ok(account_id); + } + } } pub async fn process( diff --git a/src/commands/delete_command/account/sender/mod.rs b/src/commands/delete_command/account/sender/mod.rs index 35934615f..4d0591e8d 100644 --- a/src/commands/delete_command/account/sender/mod.rs +++ b/src/commands/delete_command/account/sender/mod.rs @@ -25,8 +25,20 @@ impl Sender { connection_config: Option, ) -> color_eyre::eyre::Result { let sender_account_id: String = match item.sender_account_id { - Some(cli_sender_account_id) => cli_sender_account_id, - None => Sender::input_sender_account_id(), + Some(cli_sender_account_id) => match &connection_config { + Some(network_connection_config) => match crate::common::check_account_id( + network_connection_config.clone(), + cli_sender_account_id.clone(), + )? { + Some(_) => cli_sender_account_id, + None => { + println!("Account <{}> doesn't exist", cli_sender_account_id); + Sender::input_sender_account_id(connection_config.clone())? + } + }, + None => cli_sender_account_id, + }, + None => Sender::input_sender_account_id(connection_config.clone())?, }; let send_to: SendTo = match item.send_to { Some(cli_send_to) => { @@ -42,12 +54,26 @@ impl Sender { } impl Sender { - fn input_sender_account_id() -> String { - println!(); - Input::new() - .with_prompt("Which account ID do you need to remove?") - .interact_text() - .unwrap() + fn input_sender_account_id( + connection_config: Option, + ) -> color_eyre::eyre::Result { + loop { + let account_id: String = Input::new() + .with_prompt("Which account ID do you need to remove?") + .interact_text() + .unwrap(); + if let Some(connection_config) = &connection_config { + if let Some(_) = + crate::common::check_account_id(connection_config.clone(), account_id.clone())? + { + break Ok(account_id); + } else { + println!("Account <{}> doesn't exist", account_id); + } + } else { + break Ok(account_id); + } + } } pub async fn process( diff --git a/src/commands/execute_command/change_method/call_function_type/mod.rs b/src/commands/execute_command/change_method/call_function_type/mod.rs index eaa5a4a4c..c305f8db2 100644 --- a/src/commands/execute_command/change_method/call_function_type/mod.rs +++ b/src/commands/execute_command/change_method/call_function_type/mod.rs @@ -15,7 +15,7 @@ pub struct CliCallFunctionAction { #[clap(long = "prepaid-gas")] gas: Option, #[clap(subcommand)] - send_from: Option, + send_from: Option, } #[derive(Debug)] @@ -24,7 +24,7 @@ pub struct CallFunctionAction { args: Vec, gas: near_primitives::types::Gas, deposit: near_primitives::types::Balance, - send_from: super::sender::SendFrom, + send_from: super::signer::SendFrom, } impl CallFunctionAction { @@ -51,8 +51,8 @@ impl CallFunctionAction { None => CallFunctionAction::input_deposit(), }; let send_from = match item.send_from { - Some(cli_send_from) => super::sender::SendFrom::from(cli_send_from, connection_config)?, - None => super::sender::SendFrom::choose_send_from(connection_config)?, + Some(cli_send_from) => super::signer::SendFrom::from(cli_send_from, connection_config)?, + None => super::signer::SendFrom::choose_send_from(connection_config)?, }; Ok(Self { method_name, diff --git a/src/commands/execute_command/change_method/receiver/mod.rs b/src/commands/execute_command/change_method/contract/mod.rs similarity index 50% rename from src/commands/execute_command/change_method/receiver/mod.rs rename to src/commands/execute_command/change_method/contract/mod.rs index 46c90ccab..8ea1b10db 100644 --- a/src/commands/execute_command/change_method/receiver/mod.rs +++ b/src/commands/execute_command/change_method/contract/mod.rs @@ -2,13 +2,13 @@ use dialoguer::Input; #[derive(Debug, clap::Clap)] pub enum CliSendTo { - /// Specify a receiver - Contract(CliReceiver), + /// Specify a contract ID + Contract(CliContract), } #[derive(Debug)] pub enum SendTo { - Contract(Receiver), + Contract(Contract), } impl SendTo { @@ -17,9 +17,9 @@ impl SendTo { connection_config: Option, ) -> color_eyre::eyre::Result { match item { - CliSendTo::Contract(cli_receiver) => { - let receiver = Receiver::from(cli_receiver, connection_config)?; - Ok(Self::Contract(receiver)) + CliSendTo::Contract(cli_contract) => { + let contract = Contract::from(cli_contract, connection_config)?; + Ok(Self::Contract(contract)) } } } @@ -29,10 +29,7 @@ impl SendTo { pub fn send_to( connection_config: Option, ) -> color_eyre::eyre::Result { - Ok(Self::from( - CliSendTo::Contract(Default::default()), - connection_config, - )?) + Self::from(CliSendTo::Contract(Default::default()), connection_config) } pub async fn process( @@ -57,44 +54,71 @@ impl SendTo { setting(clap::AppSettings::DisableHelpSubcommand), setting(clap::AppSettings::VersionlessSubcommands) )] -pub struct CliReceiver { - receiver_account_id: Option, +pub struct CliContract { + contract_account_id: Option, #[clap(subcommand)] call: Option, } #[derive(Debug)] -pub struct Receiver { - pub receiver_account_id: String, +pub struct Contract { + pub contract_account_id: String, pub call: super::CallFunction, } -impl Receiver { +impl Contract { fn from( - item: CliReceiver, + item: CliContract, connection_config: Option, ) -> color_eyre::eyre::Result { - let receiver_account_id: String = match item.receiver_account_id { - Some(cli_receiver_account_id) => cli_receiver_account_id, - None => Receiver::input_receiver_account_id(), + let contract_account_id: String = match item.contract_account_id { + Some(cli_contract_account_id) => match &connection_config { + Some(network_connection_config) => match crate::common::check_account_id( + network_connection_config.clone(), + cli_contract_account_id.clone(), + )? { + Some(_) => cli_contract_account_id, + None => { + println!("Account <{}> doesn't exist", cli_contract_account_id); + Contract::input_receiver_account_id(connection_config.clone())? + } + }, + None => cli_contract_account_id, + }, + None => Contract::input_receiver_account_id(connection_config.clone())?, }; let call = match item.call { Some(cli_call) => super::CallFunction::from(cli_call, connection_config)?, None => super::CallFunction::choose_call_function(connection_config)?, }; Ok(Self { - receiver_account_id, + contract_account_id, call, }) } } -impl Receiver { - fn input_receiver_account_id() -> String { - Input::new() - .with_prompt("What is the account ID of the contract?") - .interact_text() - .unwrap() +impl Contract { + fn input_receiver_account_id( + connection_config: Option, + ) -> color_eyre::eyre::Result { + loop { + let account_id: String = Input::new() + .with_prompt("What is the account ID of the contract?") + .interact_text() + .unwrap(); + if let Some(connection_config) = &connection_config { + if let Some(_) = + crate::common::check_account_id(connection_config.clone(), account_id.clone())? + { + break Ok(account_id); + } else { + println!("Account <{}> doesn't exist", account_id); + } + } else { + break Ok(account_id); + } + } } pub async fn process( @@ -103,7 +127,7 @@ impl Receiver { network_connection_config: Option, ) -> crate::CliResult { let unsigned_transaction = near_primitives::transaction::Transaction { - receiver_id: self.receiver_account_id.clone(), + receiver_id: self.contract_account_id.clone(), ..prepopulated_unsigned_transaction }; self.call diff --git a/src/commands/execute_command/change_method/mod.rs b/src/commands/execute_command/change_method/mod.rs index 09bfc01f2..a610e31f0 100644 --- a/src/commands/execute_command/change_method/mod.rs +++ b/src/commands/execute_command/change_method/mod.rs @@ -2,9 +2,9 @@ use dialoguer::{theme::ColorfulTheme, Select}; use strum::{EnumDiscriminants, EnumIter, EnumMessage, IntoEnumIterator}; mod call_function_type; +mod contract; pub mod operation_mode; -mod receiver; -mod sender; +mod signer; #[derive(Debug, clap::Clap)] pub enum CliCallFunction { diff --git a/src/commands/execute_command/change_method/operation_mode/offline_mode/mod.rs b/src/commands/execute_command/change_method/operation_mode/offline_mode/mod.rs index cbef6247e..2d4085759 100644 --- a/src/commands/execute_command/change_method/operation_mode/offline_mode/mod.rs +++ b/src/commands/execute_command/change_method/operation_mode/offline_mode/mod.rs @@ -7,19 +7,19 @@ )] pub struct CliOfflineArgs { #[clap(subcommand)] - pub send_to: Option, + pub send_to: Option, } #[derive(Debug)] pub struct OfflineArgs { - send_to: super::super::receiver::SendTo, + send_to: super::super::contract::SendTo, } impl OfflineArgs { pub fn from(item: CliOfflineArgs) -> color_eyre::eyre::Result { let send_to = match item.send_to { - Some(cli_send_to) => super::super::receiver::SendTo::from(cli_send_to, None)?, - None => super::super::receiver::SendTo::send_to(None)?, + Some(cli_send_to) => super::super::contract::SendTo::from(cli_send_to, None)?, + None => super::super::contract::SendTo::send_to(None)?, }; Ok(Self { send_to }) } diff --git a/src/commands/execute_command/change_method/operation_mode/online_mode/select_server/server/mod.rs b/src/commands/execute_command/change_method/operation_mode/online_mode/select_server/server/mod.rs index 14a747e6f..50832e021 100644 --- a/src/commands/execute_command/change_method/operation_mode/online_mode/select_server/server/mod.rs +++ b/src/commands/execute_command/change_method/operation_mode/online_mode/select_server/server/mod.rs @@ -9,7 +9,7 @@ use dialoguer::Input; )] pub struct CliServer { #[clap(subcommand)] - pub send_to: Option, + pub send_to: Option, } /// данные для custom server @@ -23,13 +23,13 @@ pub struct CliCustomServer { #[clap(long)] pub url: Option, #[clap(subcommand)] - send_to: Option, + send_to: Option, } #[derive(Debug)] pub struct Server { pub connection_config: Option, - pub send_to: super::super::super::super::receiver::SendTo, + pub send_to: super::super::super::super::contract::SendTo, } impl CliServer { @@ -38,11 +38,11 @@ impl CliServer { connection_config: crate::common::ConnectionConfig, ) -> color_eyre::eyre::Result { let send_to = match self.send_to { - Some(cli_send_to) => super::super::super::super::receiver::SendTo::from( + Some(cli_send_to) => super::super::super::super::contract::SendTo::from( cli_send_to, Some(connection_config.clone()), )?, - None => super::super::super::super::receiver::SendTo::send_to(Some( + None => super::super::super::super::contract::SendTo::send_to(Some( connection_config.clone(), ))?, }; @@ -66,12 +66,12 @@ impl CliCustomServer { url: url.inner.clone(), }); let send_to = match self.send_to { - Some(cli_send_to) => super::super::super::super::receiver::SendTo::from( + Some(cli_send_to) => super::super::super::super::contract::SendTo::from( cli_send_to, connection_config.clone(), )?, None => { - super::super::super::super::receiver::SendTo::send_to(connection_config.clone())? + super::super::super::super::contract::SendTo::send_to(connection_config.clone())? } }; Ok(Server { diff --git a/src/commands/execute_command/change_method/sender/mod.rs b/src/commands/execute_command/change_method/signer/mod.rs similarity index 68% rename from src/commands/execute_command/change_method/sender/mod.rs rename to src/commands/execute_command/change_method/signer/mod.rs index 92cac8bb8..f6949728f 100644 --- a/src/commands/execute_command/change_method/sender/mod.rs +++ b/src/commands/execute_command/change_method/signer/mod.rs @@ -28,10 +28,7 @@ impl SendFrom { pub fn choose_send_from( connection_config: Option, ) -> color_eyre::eyre::Result { - Ok(Self::from( - CliSendFrom::Signer(Default::default()), - connection_config, - )?) + Self::from(CliSendFrom::Signer(Default::default()), connection_config) } pub async fn process( @@ -77,8 +74,20 @@ impl Sender { connection_config: Option, ) -> color_eyre::eyre::Result { let sender_account_id: String = match item.sender_account_id { - Some(cli_sender_account_id) => cli_sender_account_id, - None => Sender::input_sender_account_id(), + Some(cli_sender_account_id) => match &connection_config { + Some(network_connection_config) => match crate::common::check_account_id( + network_connection_config.clone(), + cli_sender_account_id.clone(), + )? { + Some(_) => cli_sender_account_id, + None => { + println!("Account <{}> doesn't exist", cli_sender_account_id); + Sender::input_sender_account_id(connection_config.clone())? + } + }, + None => cli_sender_account_id, + }, + None => Sender::input_sender_account_id(connection_config.clone())?, }; let sign_option = match item.sign_option { Some(cli_sign_transaction) => crate::commands::construct_transaction_command::sign_transaction::SignTransaction::from(cli_sign_transaction, connection_config, sender_account_id.clone())?, @@ -92,12 +101,26 @@ impl Sender { } impl Sender { - pub fn input_sender_account_id() -> String { - println!(); - Input::new() - .with_prompt("What is the account ID of the signer?") - .interact_text() - .unwrap() + fn input_sender_account_id( + connection_config: Option, + ) -> color_eyre::eyre::Result { + loop { + let account_id: String = Input::new() + .with_prompt("What is the account ID of the signer?") + .interact_text() + .unwrap(); + if let Some(connection_config) = &connection_config { + if let Some(_) = + crate::common::check_account_id(connection_config.clone(), account_id.clone())? + { + break Ok(account_id); + } else { + println!("Account <{}> doesn't exist", account_id); + } + } else { + break Ok(account_id); + } + } } pub async fn process( diff --git a/src/commands/execute_command/mod.rs b/src/commands/execute_command/mod.rs index ebf78770c..a839748e1 100644 --- a/src/commands/execute_command/mod.rs +++ b/src/commands/execute_command/mod.rs @@ -21,13 +21,13 @@ pub struct OptionMethod { method: Method, } -impl From for OptionMethod { - fn from(item: CliOptionMethod) -> Self { +impl OptionMethod { + pub fn from(item: CliOptionMethod) -> color_eyre::eyre::Result { let method = match item.method { - Some(cli_method) => Method::from(cli_method), - None => Method::choose_method(), + Some(cli_method) => Method::from(cli_method)?, + None => Method::choose_method()?, }; - Self { method } + Ok(Self { method }) } } @@ -57,22 +57,21 @@ enum Method { ViewMethod(self::view_method::operation_mode::OperationMode), } -impl From for Method { - fn from(item: CliMethod) -> Self { +impl Method { + fn from(item: CliMethod) -> color_eyre::eyre::Result { match item { - CliMethod::ChangeMethod(cli_operation_mode) => Method::ChangeMethod( - self::change_method::operation_mode::OperationMode::from(cli_operation_mode) - .unwrap(), - ), - CliMethod::ViewMethod(cli_operation_mode) => { - Method::ViewMethod(cli_operation_mode.into()) - } + CliMethod::ChangeMethod(cli_operation_mode) => Ok(Method::ChangeMethod( + self::change_method::operation_mode::OperationMode::from(cli_operation_mode)?, + )), + CliMethod::ViewMethod(cli_operation_mode) => Ok(Method::ViewMethod( + self::view_method::operation_mode::OperationMode::from(cli_operation_mode)?, + )), } } } impl Method { - fn choose_method() -> Self { + fn choose_method() -> color_eyre::eyre::Result { println!(); let variants = MethodDiscriminants::iter().collect::>(); let methods = variants diff --git a/src/commands/execute_command/view_method/operation_mode/mod.rs b/src/commands/execute_command/view_method/operation_mode/mod.rs index 8f2b45c85..5c9322b88 100644 --- a/src/commands/execute_command/view_method/operation_mode/mod.rs +++ b/src/commands/execute_command/view_method/operation_mode/mod.rs @@ -19,13 +19,13 @@ pub struct OperationMode { pub mode: Mode, } -impl From for OperationMode { - fn from(item: CliOperationMode) -> Self { +impl OperationMode { + pub fn from(item: CliOperationMode) -> color_eyre::eyre::Result { let mode = match item.mode { - Some(cli_mode) => Mode::from(cli_mode), - None => Mode::choose_mode(), + Some(cli_mode) => Mode::from(cli_mode)?, + None => Mode::choose_mode()?, }; - Self { mode } + Ok(Self { mode }) } } @@ -48,16 +48,18 @@ pub enum Mode { Network(self::online_mode::NetworkArgs), } -impl From for Mode { - fn from(item: CliMode) -> Self { +impl Mode { + fn from(item: CliMode) -> color_eyre::eyre::Result { match item { - CliMode::Network(cli_network_args) => Self::Network(cli_network_args.into()), + CliMode::Network(cli_network_args) => Ok(Self::Network( + self::online_mode::NetworkArgs::from(cli_network_args)?, + )), } } } impl Mode { - pub fn choose_mode() -> Self { + fn choose_mode() -> color_eyre::eyre::Result { Self::from(CliMode::Network(Default::default())) } diff --git a/src/commands/execute_command/view_method/operation_mode/online_mode/mod.rs b/src/commands/execute_command/view_method/operation_mode/online_mode/mod.rs index a10f12ab9..d83c8d896 100644 --- a/src/commands/execute_command/view_method/operation_mode/online_mode/mod.rs +++ b/src/commands/execute_command/view_method/operation_mode/online_mode/mod.rs @@ -17,15 +17,15 @@ pub struct NetworkArgs { selected_server: self::select_server::SelectServer, } -impl From for NetworkArgs { - fn from(item: CliNetworkArgs) -> Self { +impl NetworkArgs { + pub fn from(item: CliNetworkArgs) -> color_eyre::eyre::Result { let selected_server = match item.selected_server { Some(cli_selected_server) => { - self::select_server::SelectServer::from(cli_selected_server) + self::select_server::SelectServer::from(cli_selected_server)? } - None => self::select_server::SelectServer::choose_server(), + None => self::select_server::SelectServer::choose_server()?, }; - Self { selected_server } + Ok(Self { selected_server }) } } diff --git a/src/commands/execute_command/view_method/operation_mode/online_mode/select_server/mod.rs b/src/commands/execute_command/view_method/operation_mode/online_mode/select_server/mod.rs index 1ffe72c3f..6bdf14a7f 100644 --- a/src/commands/execute_command/view_method/operation_mode/online_mode/select_server/mod.rs +++ b/src/commands/execute_command/view_method/operation_mode/online_mode/select_server/mod.rs @@ -28,27 +28,27 @@ pub enum SelectServer { Custom(self::server::Server), } -impl From for SelectServer { - fn from(item: CliSelectServer) -> Self { +impl SelectServer { + pub fn from(item: CliSelectServer) -> color_eyre::eyre::Result { match item { - CliSelectServer::Testnet(cli_server) => { - Self::Testnet(cli_server.into_server(crate::common::ConnectionConfig::Testnet)) - } - CliSelectServer::Mainnet(cli_server) => { - Self::Mainnet(cli_server.into_server(crate::common::ConnectionConfig::Mainnet)) - } - CliSelectServer::Betanet(cli_server) => { - Self::Betanet(cli_server.into_server(crate::common::ConnectionConfig::Betanet)) - } + CliSelectServer::Testnet(cli_server) => Ok(Self::Testnet( + cli_server.into_server(crate::common::ConnectionConfig::Testnet)?, + )), + CliSelectServer::Mainnet(cli_server) => Ok(Self::Mainnet( + cli_server.into_server(crate::common::ConnectionConfig::Mainnet)?, + )), + CliSelectServer::Betanet(cli_server) => Ok(Self::Betanet( + cli_server.into_server(crate::common::ConnectionConfig::Betanet)?, + )), CliSelectServer::Custom(cli_custom_server) => { - Self::Custom(cli_custom_server.into_server()) + Ok(Self::Custom(cli_custom_server.into_server()?)) } } } } impl SelectServer { - pub fn choose_server() -> Self { + pub fn choose_server() -> color_eyre::eyre::Result { println!(); let variants = SelectServerDiscriminants::iter().collect::>(); let servers = variants diff --git a/src/commands/execute_command/view_method/operation_mode/online_mode/select_server/server/mod.rs b/src/commands/execute_command/view_method/operation_mode/online_mode/select_server/server/mod.rs index 13ec6e1b2..d0a78acad 100644 --- a/src/commands/execute_command/view_method/operation_mode/online_mode/select_server/server/mod.rs +++ b/src/commands/execute_command/view_method/operation_mode/online_mode/select_server/server/mod.rs @@ -33,20 +33,28 @@ pub struct Server { } impl CliServer { - pub fn into_server(self, network_connection_config: crate::common::ConnectionConfig) -> Server { + pub fn into_server( + self, + network_connection_config: crate::common::ConnectionConfig, + ) -> color_eyre::eyre::Result { let send_to = match self.send_to { - Some(cli_send_to) => super::super::super::super::receiver::SendTo::from(cli_send_to), - None => super::super::super::super::receiver::SendTo::send_to(), + Some(cli_send_to) => super::super::super::super::receiver::SendTo::from( + cli_send_to, + network_connection_config.clone(), + )?, + None => super::super::super::super::receiver::SendTo::send_to( + network_connection_config.clone(), + )?, }; - Server { + Ok(Server { network_connection_config, send_to, - } + }) } } impl CliCustomServer { - pub fn into_server(self) -> Server { + pub fn into_server(self) -> color_eyre::eyre::Result { let url: crate::common::AvailableRpcServerUrl = match self.url { Some(url) => url, None => Input::new() @@ -54,14 +62,20 @@ impl CliCustomServer { .interact_text() .unwrap(), }; + let connection_config = crate::common::ConnectionConfig::Custom { + url: url.inner.clone(), + }; let send_to = match self.send_to { - Some(cli_send_to) => super::super::super::super::receiver::SendTo::from(cli_send_to), - None => super::super::super::super::receiver::SendTo::send_to(), + Some(cli_send_to) => super::super::super::super::receiver::SendTo::from( + cli_send_to, + connection_config.clone(), + )?, + None => super::super::super::super::receiver::SendTo::send_to(connection_config)?, }; - Server { + Ok(Server { network_connection_config: crate::common::ConnectionConfig::Custom { url: url.inner }, send_to, - } + }) } } diff --git a/src/commands/execute_command/view_method/receiver/mod.rs b/src/commands/execute_command/view_method/receiver/mod.rs index 1cb1969f5..b6361e0c3 100644 --- a/src/commands/execute_command/view_method/receiver/mod.rs +++ b/src/commands/execute_command/view_method/receiver/mod.rs @@ -11,20 +11,25 @@ pub enum SendTo { Contract(Receiver), } -impl From for SendTo { - fn from(item: CliSendTo) -> Self { +impl SendTo { + pub fn from( + item: CliSendTo, + connection_config: crate::common::ConnectionConfig, + ) -> color_eyre::eyre::Result { match item { CliSendTo::Contract(cli_receiver) => { - let receiver = Receiver::from(cli_receiver); - Self::Contract(receiver) + let receiver = Receiver::from(cli_receiver, connection_config)?; + Ok(Self::Contract(receiver)) } } } } impl SendTo { - pub fn send_to() -> Self { - Self::from(CliSendTo::Contract(Default::default())) + pub fn send_to( + connection_config: crate::common::ConnectionConfig, + ) -> color_eyre::eyre::Result { + Self::from(CliSendTo::Contract(Default::default()), connection_config) } pub async fn process( @@ -56,29 +61,70 @@ pub struct Receiver { pub call: super::CallFunction, } -impl From for Receiver { - fn from(item: CliReceiver) -> Self { +impl Receiver { + fn from( + item: CliReceiver, + connection_config: crate::common::ConnectionConfig, + ) -> color_eyre::eyre::Result { let contract_account_id: String = match item.contract_account_id { - Some(cli_contract_account_id) => cli_contract_account_id, - None => Receiver::input_contract_account_id(), + Some(cli_contract_account_id) => { + let contract_code_hash: near_primitives::hash::CryptoHash = + match crate::common::check_account_id( + connection_config.clone(), + cli_contract_account_id.clone(), + )? { + Some(account_view) => account_view.code_hash, + None => near_primitives::hash::CryptoHash::default(), + }; + if contract_code_hash == near_primitives::hash::CryptoHash::default() { + println!( + "Contract code is not deployed to this account <{}>.", + cli_contract_account_id + ); + Receiver::input_contract_account_id(connection_config)? + } else { + cli_contract_account_id + } + } + None => Receiver::input_contract_account_id(connection_config)?, }; let call = match item.call { Some(cli_call) => cli_call.into(), None => super::CallFunction::choose_call_function(), }; - Self { + Ok(Self { contract_account_id, call, - } + }) } } impl Receiver { - pub fn input_contract_account_id() -> String { - Input::new() - .with_prompt("What is the account ID of the contract?") - .interact_text() - .unwrap() + fn input_contract_account_id( + connection_config: crate::common::ConnectionConfig, + ) -> color_eyre::eyre::Result { + loop { + let contract_account_id: String = Input::new() + .with_prompt("What is the account ID of the contract?") + .interact_text() + .unwrap(); + let contract_code_hash: near_primitives::hash::CryptoHash = + match crate::common::check_account_id( + connection_config.clone(), + contract_account_id.clone(), + )? { + Some(account_view) => account_view.code_hash, + None => near_primitives::hash::CryptoHash::default(), + }; + if contract_code_hash == near_primitives::hash::CryptoHash::default() { + println!( + "Contract code is not deployed to this account <{}>.", + contract_account_id + ) + } else { + break Ok(contract_account_id); + } + } } pub async fn process( diff --git a/src/commands/mod.rs b/src/commands/mod.rs index 74a107b06..4f9bdae5b 100644 --- a/src/commands/mod.rs +++ b/src/commands/mod.rs @@ -75,9 +75,9 @@ impl From for TopLevelCommand { CliTopLevelCommand::Delete(cli_delete_action) => TopLevelCommand::Delete( self::delete_command::DeleteAction::from(cli_delete_action).unwrap(), ), - CliTopLevelCommand::Execute(cli_option_method) => { - TopLevelCommand::Execute(cli_option_method.into()) - } + CliTopLevelCommand::Execute(cli_option_method) => TopLevelCommand::Execute( + self::execute_command::OptionMethod::from(cli_option_method).unwrap(), + ), CliTopLevelCommand::GenerateShellCompletions(_) => { unreachable!("This variant is handled in the main function") } diff --git a/src/commands/transfer_command/receiver/mod.rs b/src/commands/transfer_command/receiver/mod.rs index e2a36ca30..3ec0f96bd 100644 --- a/src/commands/transfer_command/receiver/mod.rs +++ b/src/commands/transfer_command/receiver/mod.rs @@ -79,8 +79,24 @@ impl Receiver { sender_account_id: String, ) -> color_eyre::eyre::Result { let receiver_account_id: String = match item.receiver_account_id { - Some(cli_receiver_account_id) => cli_receiver_account_id, - None => Receiver::input_receiver_account_id(), + Some(cli_receiver_account_id) => match &connection_config { + Some(network_connection_config) => match crate::common::check_account_id( + network_connection_config.clone(), + cli_receiver_account_id.clone(), + )? { + Some(_) => cli_receiver_account_id, + None => { + if !crate::common::is_64_len_hex(&cli_receiver_account_id) { + println!("Account <{}> doesn't exist", cli_receiver_account_id); + Receiver::input_receiver_account_id(connection_config.clone())? + } else { + cli_receiver_account_id + } + } + }, + None => cli_receiver_account_id, + }, + None => Receiver::input_receiver_account_id(connection_config.clone())?, }; let transfer: super::transfer_near_tokens_type::Transfer = match item.transfer { Some(cli_transfer) => super::transfer_near_tokens_type::Transfer::from( @@ -101,11 +117,30 @@ impl Receiver { } impl Receiver { - pub fn input_receiver_account_id() -> String { - Input::new() - .with_prompt("What is the account ID of the receiver?") - .interact_text() - .unwrap() + fn input_receiver_account_id( + connection_config: Option, + ) -> color_eyre::eyre::Result { + loop { + let account_id: String = Input::new() + .with_prompt("What is the account ID of the receiver?") + .interact_text() + .unwrap(); + if let Some(connection_config) = &connection_config { + if let Some(_) = + crate::common::check_account_id(connection_config.clone(), account_id.clone())? + { + break Ok(account_id); + } else { + if !crate::common::is_64_len_hex(&account_id) { + println!("Account <{}> doesn't exist", account_id); + } else { + break Ok(account_id); + } + } + } else { + break Ok(account_id); + } + } } pub async fn process( diff --git a/src/commands/transfer_command/sender/mod.rs b/src/commands/transfer_command/sender/mod.rs index d667181ef..b5ae26517 100644 --- a/src/commands/transfer_command/sender/mod.rs +++ b/src/commands/transfer_command/sender/mod.rs @@ -25,8 +25,20 @@ impl Sender { connection_config: Option, ) -> color_eyre::eyre::Result { let sender_account_id: String = match item.sender_account_id { - Some(cli_sender_account_id) => cli_sender_account_id, - None => Sender::input_sender_account_id(), + Some(cli_sender_account_id) => match &connection_config { + Some(network_connection_config) => match crate::common::check_account_id( + network_connection_config.clone(), + cli_sender_account_id.clone(), + )? { + Some(_) => cli_sender_account_id, + None => { + println!("Account <{}> doesn't exist", cli_sender_account_id); + Sender::input_sender_account_id(connection_config.clone())? + } + }, + None => cli_sender_account_id, + }, + None => Sender::input_sender_account_id(connection_config.clone())?, }; let send_to: super::receiver::SendTo = match item.send_to { Some(cli_send_to) => super::receiver::SendTo::from( @@ -44,12 +56,26 @@ impl Sender { } impl Sender { - pub fn input_sender_account_id() -> String { - println!(); - Input::new() - .with_prompt("What is the account ID of the sender?") - .interact_text() - .unwrap() + fn input_sender_account_id( + connection_config: Option, + ) -> color_eyre::eyre::Result { + loop { + let account_id: String = Input::new() + .with_prompt("What is the account ID of the sender?") + .interact_text() + .unwrap(); + if let Some(connection_config) = &connection_config { + if let Some(_) = + crate::common::check_account_id(connection_config.clone(), account_id.clone())? + { + break Ok(account_id); + } else { + println!("Account <{}> doesn't exist", account_id); + } + } else { + break Ok(account_id); + } + } } pub async fn process( diff --git a/src/commands/transfer_command/transfer_near_tokens_type/mod.rs b/src/commands/transfer_command/transfer_near_tokens_type/mod.rs index 9a3073d1a..87cff65a0 100644 --- a/src/commands/transfer_command/transfer_near_tokens_type/mod.rs +++ b/src/commands/transfer_command/transfer_near_tokens_type/mod.rs @@ -84,9 +84,37 @@ impl TransferNEARTokensAction { connection_config: Option, sender_account_id: String, ) -> color_eyre::eyre::Result { - let amount: crate::common::NearBalance = match item.amount { - Some(cli_amount) => cli_amount, - None => TransferNEARTokensAction::input_amount(), + let amount: crate::common::NearBalance = match &connection_config { + Some(network_connection_config) => { + let account_balance: crate::common::NearBalance = + match crate::common::check_account_id( + network_connection_config.clone(), + sender_account_id.clone(), + )? { + Some(account_view) => { + crate::common::NearBalance::from_yoctonear(account_view.amount) + } + None => crate::common::NearBalance::from_yoctonear(0), + }; + match item.amount { + Some(cli_amount) => { + if cli_amount <= account_balance { + cli_amount + } else { + println!( + "You need to enter a value of no more than {}", + account_balance + ); + TransferNEARTokensAction::input_amount(Some(account_balance)) + } + } + None => TransferNEARTokensAction::input_amount(Some(account_balance)), + } + } + None => match item.amount { + Some(cli_amount) => cli_amount, + None => TransferNEARTokensAction::input_amount(None), + }, }; let sign_option = match item.sign_option { Some(cli_sign_transaction) => crate::commands::construct_transaction_command::sign_transaction::SignTransaction::from(cli_sign_transaction, connection_config, sender_account_id)?, @@ -100,11 +128,30 @@ impl TransferNEARTokensAction { } impl TransferNEARTokensAction { - pub fn input_amount() -> crate::common::NearBalance { - Input::new() - .with_prompt("How many NEAR Tokens do you want to transfer? (example: 10NEAR or 0.5near or 10000yoctonear)") - .interact_text() - .unwrap() + fn input_amount( + account_balance: Option, + ) -> crate::common::NearBalance { + match account_balance { + Some(account_balance) => loop { + let input_amount: crate::common::NearBalance = Input::new() + .with_prompt("How many NEAR Tokens do you want to transfer? (example: 10NEAR or 0.5near or 10000yoctonear)") + .with_initial_text(format!("{}", account_balance)) + .interact_text() + .unwrap(); + if input_amount <= account_balance { + break input_amount; + } else { + println!( + "You need to enter a value of no more than {}", + account_balance + ) + } + } + None => Input::new() + .with_prompt("How many NEAR Tokens do you want to transfer? (example: 10NEAR or 0.5near or 10000yoctonear)") + .interact_text() + .unwrap() + } } pub async fn process( diff --git a/src/common.rs b/src/common.rs index 4e7feca7c..5c46a1c69 100644 --- a/src/common.rs +++ b/src/common.rs @@ -95,9 +95,9 @@ impl std::fmt::Display for AvailableRpcServerUrl { const ONE_NEAR: u128 = 10u128.pow(24); -#[derive(Debug, Clone, Default, PartialEq)] +#[derive(Debug, Clone, Default, PartialEq, PartialOrd)] pub struct NearBalance { - yoctonear_amount: u128, + pub yoctonear_amount: u128, } impl NearBalance { @@ -318,6 +318,45 @@ impl ConnectionConfig { } } +pub fn check_account_id( + connection_config: ConnectionConfig, + account_id: String, +) -> color_eyre::eyre::Result> { + let query_view_method_response = actix::System::new().block_on(async { + near_jsonrpc_client::new_client(connection_config.rpc_url().as_str()) + .query(near_jsonrpc_primitives::types::query::RpcQueryRequest { + block_reference: near_primitives::types::Finality::Final.into(), + request: near_primitives::views::QueryRequest::ViewAccount { account_id }, + }) + .await + }); + match query_view_method_response { + Ok(rpc_query_response) => { + let account_view = + if let near_jsonrpc_primitives::types::query::QueryResponseKind::ViewAccount( + result, + ) = rpc_query_response.kind + { + result + } else { + return Err(color_eyre::Report::msg(format!("Error call result"))); + }; + Ok(Some(account_view)) + } + Err(_) => return Ok(None), + } +} + +/// Returns true if the account ID length is 64 characters and it's a hex representation. This is used to check the implicit account. +pub fn is_64_len_hex(account_id: impl AsRef) -> bool { + let account_id = account_id.as_ref(); + account_id.len() == 64 + && account_id + .as_bytes() + .iter() + .all(|b| matches!(b, b'a'..=b'f' | b'0'..=b'9')) +} + #[derive(Debug, Clone)] pub struct KeyPairProperties { pub seed_phrase_hd_path: slip10::BIP32Path,