From d614e38dfbb2ac5e870654058c984814519c6d0e Mon Sep 17 00:00:00 2001 From: FroVolod Date: Tue, 21 May 2024 21:46:03 +0300 Subject: [PATCH] fix: Wrong console command for adding Function-Call key with unlimited allowance (#342) Resolves #341 _near-cli_ (JS) command with "allowance": ``` near add-key example-acct.testnet GkMNfc92fwM1AmwH1MTjF4b7UZuceamsq96XPkHsQ9vi --allowance 30000000000 --contract-id example-contract.testnet --method-names example_method ``` _near-cli-rs_ command: ``` near account add-key example-acct.testnet grant-function-call-access --allowance '30000000000 NEAR' --receiver-account-id example-contract.testnet --method-names example_method use-manually-provided-public-key GkMNfc92fwM1AmwH1MTjF4b7UZuceamsq96XPkHsQ9vi network-config testnet sign-with-keychain send ``` _near-cli_ (JS) command without "allowance": ``` near add-key example-acct.testnet GkMNfc92fwM1AmwH1MTjF4b7UZuceamsq96XPkHsQ9vi --contract-id example-contract.testnet --method-names example_method ``` _near-cli-rs_ command: ``` near account add-key example-acct.testnet grant-function-call-access --allowance unlimited --receiver-account-id example-contract.testnet --method-names example_method use-manually-provided-public-key ed25519:GkMNfc92fwM1AmwH1MTjF4b7UZuceamsq96XPkHsQ9vi network-config testnet ``` --- .../account/add_key/access_key_type/mod.rs | 10 +- src/js_command_match/add_key.rs | 11 ++- src/types/mod.rs | 1 + src/types/near_allowance.rs | 91 +++++++++++++++++++ src/types/near_token.rs | 4 + 5 files changed, 109 insertions(+), 8 deletions(-) create mode 100644 src/types/near_allowance.rs diff --git a/src/commands/account/add_key/access_key_type/mod.rs b/src/commands/account/add_key/access_key_type/mod.rs index 6a6c4c437..c86504d26 100644 --- a/src/commands/account/add_key/access_key_type/mod.rs +++ b/src/commands/account/add_key/access_key_type/mod.rs @@ -53,7 +53,7 @@ impl From for AccessTypeContext { pub struct FunctionCallType { #[interactive_clap(long)] #[interactive_clap(skip_default_input_arg)] - allowance: crate::types::near_token::NearToken, + allowance: crate::types::near_allowance::NearAllowance, #[interactive_clap(long)] /// Enter a receiver to use by this access key to pay for function call gas and transaction fees: receiver_account_id: crate::types::account_id::AccountId, @@ -81,7 +81,7 @@ impl FunctionCallTypeContext { Ok(Self { global_context: previous_context.global_context, signer_account_id: previous_context.owner_account_id.into(), - allowance: Some(scope.allowance), + allowance: scope.allowance.optional_near_token(), receiver_account_id: scope.receiver_account_id.clone(), method_names: scope.method_names.clone(), }) @@ -144,10 +144,10 @@ impl FunctionCallType { pub fn input_allowance( _context: &super::AddKeyCommandContext, - ) -> color_eyre::eyre::Result> { - let allowance_near_balance: crate::types::near_token::NearToken = + ) -> color_eyre::eyre::Result> { + let allowance_near_balance: crate::types::near_allowance::NearAllowance = CustomType::new("Enter the allowance, a budget this access key can use to pay for transaction fees (example: 10NEAR or 0.5near or 10000yoctonear):") - .with_starting_input("0.25 NEAR") + .with_starting_input("unlimited") .prompt()?; Ok(Some(allowance_near_balance)) } diff --git a/src/js_command_match/add_key.rs b/src/js_command_match/add_key.rs index 878ad5e6b..49139f3d6 100644 --- a/src/js_command_match/add_key.rs +++ b/src/js_command_match/add_key.rs @@ -7,8 +7,8 @@ pub struct AddKeyArgs { contract_id: Option, #[clap(long, aliases = ["method_names", "methodNames"], requires = "contract_id", value_delimiter = ',', num_args = 1..)] method_names: Vec, - #[clap(long, default_value = "0")] - allowance: String, + #[clap(long, default_value = None)] + allowance: Option, #[clap(allow_hyphen_values = true, num_args = 0..)] _unknown_args: Vec, } @@ -16,13 +16,18 @@ pub struct AddKeyArgs { impl AddKeyArgs { pub fn to_cli_args(&self, network_config: String) -> Vec { if let Some(contract_id) = self.contract_id.as_deref() { + let allowance = if let Some(allowance) = &self.allowance { + format!("{} NEAR", allowance) + } else { + "unlimited".to_string() + }; return vec![ "account".to_owned(), "add-key".to_owned(), self.account_id.to_owned(), "grant-function-call-access".to_owned(), "--allowance".to_owned(), - format!("{} NEAR", self.allowance), + allowance, "--receiver-account-id".to_owned(), contract_id.to_owned(), "--method-names".to_owned(), diff --git a/src/types/mod.rs b/src/types/mod.rs index bff6b93d9..99e9ab80f 100644 --- a/src/types/mod.rs +++ b/src/types/mod.rs @@ -5,6 +5,7 @@ pub mod crypto_hash; pub mod file_bytes; pub mod ft_properties; pub mod json; +pub mod near_allowance; pub mod near_token; pub mod path_buf; pub mod public_key; diff --git a/src/types/near_allowance.rs b/src/types/near_allowance.rs new file mode 100644 index 000000000..c48af3d65 --- /dev/null +++ b/src/types/near_allowance.rs @@ -0,0 +1,91 @@ +const UNLIMITED: &str = "unlimited"; + +#[derive( + Debug, + Clone, + Copy, + serde::Serialize, + serde::Deserialize, + derive_more::AsRef, + derive_more::From, + derive_more::Into, +)] +#[as_ref(forward)] +pub struct NearAllowance(Option); + +impl std::fmt::Display for NearAllowance { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + if let Some(amount) = self.0 { + amount.fmt(f) + } else { + write!(f, "{UNLIMITED}") + } + } +} + +impl std::str::FromStr for NearAllowance { + type Err = near_token::NearTokenError; + + fn from_str(s: &str) -> Result { + if s == UNLIMITED { + return Ok(Self(None)); + } + Ok(Self(Some(crate::types::near_token::NearToken::from_str( + s, + )?))) + } +} + +impl NearAllowance { + pub fn optional_near_token(&self) -> Option { + self.0 + } +} + +impl interactive_clap::ToCli for NearAllowance { + type CliVariant = NearAllowance; +} + +#[cfg(test)] +mod tests { + use super::*; + use std::str::FromStr; + + #[test] + fn near_allowance_to_string_0_near() { + assert_eq!( + NearAllowance(Some(near_token::NearToken::from_near(0).into())).to_string(), + "0 NEAR".to_string() + ) + } + #[test] + fn near_allowance_to_string_0_millinear() { + assert_eq!( + NearAllowance(Some(near_token::NearToken::from_millinear(0).into())).to_string(), + "0 NEAR".to_string() + ) + } + #[test] + fn near_allowance_to_string_none() { + assert_eq!(NearAllowance(None).to_string(), "unlimited".to_string()) + } + #[test] + fn near_allowance_to_string_0dot02_near() { + assert_eq!( + NearAllowance(Some( + near_token::NearToken::from_yoctonear(20_000_000_000_000_000_000_000).into() + )) + .to_string(), + "0.02 NEAR".to_string() + ) + } + #[test] + fn near_allowance_from_str_unlimited() { + assert_eq!( + NearAllowance::from_str("unlimited") + .unwrap() + .optional_near_token(), + None + ) + } +} diff --git a/src/types/near_token.rs b/src/types/near_token.rs index 416300d8b..a268996b9 100644 --- a/src/types/near_token.rs +++ b/src/types/near_token.rs @@ -5,6 +5,10 @@ const ONE_NEAR: u128 = 10u128.pow(24); Default, Clone, Copy, + Eq, + PartialEq, + Ord, + PartialOrd, serde::Serialize, serde::Deserialize, derive_more::AsRef,