diff --git a/src/js_command_match/account/create.rs b/src/js_command_match/account/create.rs new file mode 100644 index 000000000..2d372cbbc --- /dev/null +++ b/src/js_command_match/account/create.rs @@ -0,0 +1,227 @@ +use crate::js_command_match::constants::{ + DEFAULT_SEED_PHRASE_PATH, INITIAL_BALANCE_ALIASES, LEDGER_PATH_ALIASES, NETWORK_ID_ALIASES, + PK_LEDGER_PATH_ALIASES, PUBLIC_KEY_ALIASES, SEED_PHRASE_ALIASES, SIGN_WITH_LEDGER_ALIASES, + USE_ACCOUNT_ALIASES, USE_FAUCET_ALIASES, USE_LEDGER_PK_ALIASES, +}; + +#[derive(Debug, Clone, clap::Parser)] +pub struct CreateAccountArgs { + new_account_id: String, + #[clap(long, aliases = USE_FAUCET_ALIASES, default_value_t = false)] + use_faucet: bool, + #[clap(long, aliases = USE_ACCOUNT_ALIASES, conflicts_with = "use_faucet")] + use_account: Option, + #[clap(long, aliases = INITIAL_BALANCE_ALIASES, default_value = "1")] + initial_balance: String, + #[clap(long, aliases = PUBLIC_KEY_ALIASES)] + public_key: Option, + #[clap(long, aliases = SEED_PHRASE_ALIASES, conflicts_with = "public_key")] + seed_phrase: Option, + #[clap(long, aliases = SIGN_WITH_LEDGER_ALIASES, default_value_t = false, conflicts_with="use_faucet")] + sign_with_ledger: bool, + #[clap(long, aliases = LEDGER_PATH_ALIASES, default_value = DEFAULT_SEED_PHRASE_PATH)] + ledger_path: String, + #[clap(long, aliases = USE_LEDGER_PK_ALIASES, default_value_t = false, conflicts_with = "public_key")] + use_ledger_pk: bool, + #[clap(long, aliases = PK_LEDGER_PATH_ALIASES, default_value = DEFAULT_SEED_PHRASE_PATH)] + pk_ledger_path: String, + #[clap(long, aliases = NETWORK_ID_ALIASES)] + network_id: Option, +} + +impl CreateAccountArgs { + pub fn to_cli_args(&self, network_config: String) -> Vec { + let network_id = self.network_id.clone().unwrap_or(network_config); + + let mut command = vec!["account".to_string(), "create-account".to_string()]; + + if self.use_faucet { + command.push("sponsor-by-faucet-service".to_string()); + command.push(self.new_account_id.to_owned()); + } else { + command.push("fund-myself".to_string()); + command.push(self.new_account_id.to_owned()); + command.push(format!("{} NEAR", self.initial_balance)); + } + + if self.use_ledger_pk { + command.push("use-ledger".to_string()); + command.push("--seed-phrase-hd-path".to_string()); + command.push(self.pk_ledger_path.to_owned()); + } else if let Some(seed_phrase) = &self.seed_phrase { + command.push("use-manually-provided-seed-phrase".to_string()); + command.push(seed_phrase.to_string()); + } else if let Some(public_key) = &self.public_key { + command.push("use-manually-provided-public-key".to_string()); + command.push(public_key.to_string()); + } else { + command.push("autogenerate-new-keypair".to_string()); + command.push("save-to-keychain".to_string()); + } + + if !self.use_faucet { + command.push("sign-as".to_string()); + command.push( + self.use_account + .to_owned() + .expect("Valid master account must be provided"), + ); + }; + + command.push("network-config".to_string()); + command.push(network_id); + + if self.use_faucet { + command.push("create".to_string()); + } else { + if self.sign_with_ledger { + command.push("sign-with-ledger".to_string()); + command.push("--seed-phrase-hd-path".to_string()); + command.push(self.ledger_path.to_owned()); + } else { + command.push("sign-with-keychain".to_string()); + } + command.push("send".to_string()); + } + + command + } +} + +#[cfg(test)] +mod tests { + use super::super::super::JsCmd; + use super::*; + use clap::Parser; + + #[test] + fn create_account() { + for (input, expected_output) in [ + ( + format!("near create-account bob.testnet --{}", USE_FAUCET_ALIASES[0]), + "account create-account sponsor-by-faucet-service bob.testnet autogenerate-new-keypair save-to-keychain network-config testnet create" + ), + ( + format!("near create bob.testnet --{}", USE_FAUCET_ALIASES[0]), + "account create-account sponsor-by-faucet-service bob.testnet autogenerate-new-keypair save-to-keychain network-config testnet create" + ), + ( + format!("near create bob.testnet --{}", USE_FAUCET_ALIASES[1]), + "account create-account sponsor-by-faucet-service bob.testnet autogenerate-new-keypair save-to-keychain network-config testnet create" + ), + ( + format!("near create bob.testnet --{} alice.testnet", USE_ACCOUNT_ALIASES[0]), + "account create-account fund-myself bob.testnet '1 NEAR' autogenerate-new-keypair save-to-keychain sign-as alice.testnet network-config testnet sign-with-keychain send" + ), + ( + format!("near create bob.testnet --{} alice.testnet", USE_ACCOUNT_ALIASES[1]), + "account create-account fund-myself bob.testnet '1 NEAR' autogenerate-new-keypair save-to-keychain sign-as alice.testnet network-config testnet sign-with-keychain send" + ), + ( + format!("near create bob.testnet --{} alice.testnet", USE_ACCOUNT_ALIASES[2]), + "account create-account fund-myself bob.testnet '1 NEAR' autogenerate-new-keypair save-to-keychain sign-as alice.testnet network-config testnet sign-with-keychain send" + ), + ( + format!("near create bob.testnet --{} alice.testnet", USE_ACCOUNT_ALIASES[3]), + "account create-account fund-myself bob.testnet '1 NEAR' autogenerate-new-keypair save-to-keychain sign-as alice.testnet network-config testnet sign-with-keychain send" + ), + ( + format!("near create bob.testnet --{} alice.testnet", USE_ACCOUNT_ALIASES[4]), + "account create-account fund-myself bob.testnet '1 NEAR' autogenerate-new-keypair save-to-keychain sign-as alice.testnet network-config testnet sign-with-keychain send" + ), + ( + format!("near create bob.testnet --{} alice.testnet", USE_ACCOUNT_ALIASES[5]), + "account create-account fund-myself bob.testnet '1 NEAR' autogenerate-new-keypair save-to-keychain sign-as alice.testnet network-config testnet sign-with-keychain send" + ), + ( + format!("near create bob.testnet --useAccount alice.testnet --{} 0.1", INITIAL_BALANCE_ALIASES[0]), + "account create-account fund-myself bob.testnet '0.1 NEAR' autogenerate-new-keypair save-to-keychain sign-as alice.testnet network-config testnet sign-with-keychain send" + ), + ( + format!("near create bob.testnet --useAccount alice.testnet --{} 0.1", INITIAL_BALANCE_ALIASES[1]), + "account create-account fund-myself bob.testnet '0.1 NEAR' autogenerate-new-keypair save-to-keychain sign-as alice.testnet network-config testnet sign-with-keychain send" + ), + ( + format!("near create bob.testnet --useAccount alice.testnet --{} 78MziB9aTNsu19MHHVrfWy762S5mAqXgCB6Vgvrv9uGV --initialBalance 0.1", PUBLIC_KEY_ALIASES[0]), + "account create-account fund-myself bob.testnet '0.1 NEAR' use-manually-provided-public-key 78MziB9aTNsu19MHHVrfWy762S5mAqXgCB6Vgvrv9uGV sign-as alice.testnet network-config testnet sign-with-keychain send" + ), + ( + format!("near create bob.testnet --useAccount alice.testnet --{} 78MziB9aTNsu19MHHVrfWy762S5mAqXgCB6Vgvrv9uGV --initialBalance 0.1", PUBLIC_KEY_ALIASES[1]), + "account create-account fund-myself bob.testnet '0.1 NEAR' use-manually-provided-public-key 78MziB9aTNsu19MHHVrfWy762S5mAqXgCB6Vgvrv9uGV sign-as alice.testnet network-config testnet sign-with-keychain send" + ), + ( + format!("near create bob.testnet --{} 'crisp clump stay mean dynamic become fashion mail bike disorder chronic sight' --useFaucet", SEED_PHRASE_ALIASES[0]), + "account create-account sponsor-by-faucet-service bob.testnet use-manually-provided-seed-phrase 'crisp clump stay mean dynamic become fashion mail bike disorder chronic sight' network-config testnet create" + ), + ( + format!("near create bob.testnet --{} 'crisp clump stay mean dynamic become fashion mail bike disorder chronic sight' --useFaucet", SEED_PHRASE_ALIASES[1]), + "account create-account sponsor-by-faucet-service bob.testnet use-manually-provided-seed-phrase 'crisp clump stay mean dynamic become fashion mail bike disorder chronic sight' network-config testnet create" + ), + ( + format!("near create bob.testnet --useAccount alice.testnet --{} --networkId testnet", SIGN_WITH_LEDGER_ALIASES[0]), + "account create-account fund-myself bob.testnet '1 NEAR' autogenerate-new-keypair save-to-keychain sign-as alice.testnet network-config testnet sign-with-ledger --seed-phrase-hd-path '44'\\''/397'\\''/0'\\''/0'\\''/1'\\''' send" + ), + ( + format!("near create bob.testnet --useAccount alice.testnet --{} --networkId testnet", SIGN_WITH_LEDGER_ALIASES[1]), + "account create-account fund-myself bob.testnet '1 NEAR' autogenerate-new-keypair save-to-keychain sign-as alice.testnet network-config testnet sign-with-ledger --seed-phrase-hd-path '44'\\''/397'\\''/0'\\''/0'\\''/1'\\''' send" + ), + ( + format!("near create bob.testnet --useAccount alice.testnet --{} --networkId testnet", SIGN_WITH_LEDGER_ALIASES[2]), + "account create-account fund-myself bob.testnet '1 NEAR' autogenerate-new-keypair save-to-keychain sign-as alice.testnet network-config testnet sign-with-ledger --seed-phrase-hd-path '44'\\''/397'\\''/0'\\''/0'\\''/1'\\''' send" + ), + ( + format!("near create bob.testnet --useAccount alice.testnet --{} --networkId testnet", SIGN_WITH_LEDGER_ALIASES[3]), + "account create-account fund-myself bob.testnet '1 NEAR' autogenerate-new-keypair save-to-keychain sign-as alice.testnet network-config testnet sign-with-ledger --seed-phrase-hd-path '44'\\''/397'\\''/0'\\''/0'\\''/1'\\''' send" + ), + ( + format!("near create bob.testnet --useAccount alice.testnet --signWithLedger --{} \"44'/397'/0'/0'/2'\"", LEDGER_PATH_ALIASES[0]), + "account create-account fund-myself bob.testnet '1 NEAR' autogenerate-new-keypair save-to-keychain sign-as alice.testnet network-config testnet sign-with-ledger --seed-phrase-hd-path '44'\\''/397'\\''/0'\\''/0'\\''/2'\\''' send" + ), + ( + format!("near create bob.testnet --useAccount alice.testnet --signWithLedger --{} \"44'/397'/0'/0'/2'\"", LEDGER_PATH_ALIASES[1]), + "account create-account fund-myself bob.testnet '1 NEAR' autogenerate-new-keypair save-to-keychain sign-as alice.testnet network-config testnet sign-with-ledger --seed-phrase-hd-path '44'\\''/397'\\''/0'\\''/0'\\''/2'\\''' send" + ), + ( + format!("near create bob.testnet --{} --useFaucet", USE_LEDGER_PK_ALIASES[0]), + "account create-account sponsor-by-faucet-service bob.testnet use-ledger --seed-phrase-hd-path '44'\\''/397'\\''/0'\\''/0'\\''/1'\\''' network-config testnet create" + ), + ( + format!("near create bob.testnet --{} --useFaucet", USE_LEDGER_PK_ALIASES[1]), + "account create-account sponsor-by-faucet-service bob.testnet use-ledger --seed-phrase-hd-path '44'\\''/397'\\''/0'\\''/0'\\''/1'\\''' network-config testnet create" + ), + ( + format!("near create bob.testnet --{} --useFaucet", USE_LEDGER_PK_ALIASES[2]), + "account create-account sponsor-by-faucet-service bob.testnet use-ledger --seed-phrase-hd-path '44'\\''/397'\\''/0'\\''/0'\\''/1'\\''' network-config testnet create" + ), + ( + format!("near create bob.testnet --{} --useFaucet", USE_LEDGER_PK_ALIASES[3]), + "account create-account sponsor-by-faucet-service bob.testnet use-ledger --seed-phrase-hd-path '44'\\''/397'\\''/0'\\''/0'\\''/1'\\''' network-config testnet create" + ), + ( + format!("near create bob.testnet --useLedgerPK --{} \"44'/397'/0'/0'/2'\" --useFaucet", PK_LEDGER_PATH_ALIASES[0]), + "account create-account sponsor-by-faucet-service bob.testnet use-ledger --seed-phrase-hd-path '44'\\''/397'\\''/0'\\''/0'\\''/2'\\''' network-config testnet create" + ), + ( + format!("near create bob.testnet --useLedgerPK --{} \"44'/397'/0'/0'/2'\" --useFaucet", PK_LEDGER_PATH_ALIASES[1]), + "account create-account sponsor-by-faucet-service bob.testnet use-ledger --seed-phrase-hd-path '44'\\''/397'\\''/0'\\''/0'\\''/2'\\''' network-config testnet create" + ), + ( + format!("near create bob.near --useAccount alice.near --signWithLedger --ledgerPath \"44'/397'/0'/0'/2'\" --{} mainnet", NETWORK_ID_ALIASES[0]), + "account create-account fund-myself bob.near '1 NEAR' autogenerate-new-keypair save-to-keychain sign-as alice.near network-config mainnet sign-with-ledger --seed-phrase-hd-path '44'\\''/397'\\''/0'\\''/0'\\''/2'\\''' send" + ), + ( + format!("near create bob.near --useAccount alice.near --signWithLedger --ledgerPath \"44'/397'/0'/0'/2'\" --{} mainnet", NETWORK_ID_ALIASES[1]), + "account create-account fund-myself bob.near '1 NEAR' autogenerate-new-keypair save-to-keychain sign-as alice.near network-config mainnet sign-with-ledger --seed-phrase-hd-path '44'\\''/397'\\''/0'\\''/0'\\''/2'\\''' send" + ) + ] { + let input_cmd = shell_words::split(&input).expect("Input command must be a valid shell command"); + let JsCmd::CreateAccount(create_account_args) = JsCmd::parse_from(&input_cmd) else { + panic!("CreateAccount command was expected, but something else was parsed out from {input}"); + }; + assert_eq!( + shell_words::join(CreateAccountArgs::to_cli_args(&create_account_args, "testnet".to_string())), + expected_output + ); + } + } +} diff --git a/src/js_command_match/account/delete.rs b/src/js_command_match/account/delete.rs new file mode 100644 index 000000000..5ca4e2dd0 --- /dev/null +++ b/src/js_command_match/account/delete.rs @@ -0,0 +1,110 @@ +use crate::js_command_match::constants::{ + DEFAULT_SEED_PHRASE_PATH, LEDGER_PATH_ALIASES, NETWORK_ID_ALIASES, SIGN_WITH_LEDGER_ALIASES, +}; + +#[derive(Debug, Clone, clap::Parser)] +pub struct DeleteAccountArgs { + account_id: String, + beneficiary_id: String, + #[clap(long, aliases = SIGN_WITH_LEDGER_ALIASES, default_value_t = false)] + sign_with_ledger: bool, + #[clap(long, aliases = LEDGER_PATH_ALIASES, default_value = DEFAULT_SEED_PHRASE_PATH)] + ledger_path: String, + #[clap(long, aliases = NETWORK_ID_ALIASES)] + network_id: Option, + #[clap(long, default_value_t = false)] + force: bool, +} + +impl DeleteAccountArgs { + pub fn to_cli_args(&self, network_config: String) -> Vec { + let network_id = self.network_id.clone().unwrap_or(network_config); + + let mut command = vec![ + "account".to_string(), + "delete-account".to_string(), + self.account_id.to_owned(), + "beneficiary".to_string(), + self.beneficiary_id.to_owned(), + ]; + + command.push("network-config".to_string()); + command.push(network_id); + + if self.sign_with_ledger { + command.push("sign-with-ledger".to_string()); + command.push("--seed-phrase-hd-path".to_string()); + command.push(self.ledger_path.to_owned()); + } else { + command.push("sign-with-keychain".to_string()); + } + + if self.force { + command.push("send".to_string()); + } + + command + } +} + +#[cfg(test)] +mod tests { + use super::super::super::JsCmd; + use super::*; + use clap::Parser; + + #[test] + fn delete_account() { + for (input, expected_output) in [ + ( + format!("near delete bob.testnet alice.testnet --force"), + format!("account delete-account bob.testnet beneficiary alice.testnet network-config testnet sign-with-keychain send") + ), + ( + format!("near delete-account bob.testnet alice.testnet --force"), + format!("account delete-account bob.testnet beneficiary alice.testnet network-config testnet sign-with-keychain send") + ), + ( + format!("near delete-account bob.testnet alice.testnet --{} --force", SIGN_WITH_LEDGER_ALIASES[0]), + format!("account delete-account bob.testnet beneficiary alice.testnet network-config testnet sign-with-ledger --seed-phrase-hd-path '44'\\''/397'\\''/0'\\''/0'\\''/1'\\''' send") + ), + ( + format!("near delete-account bob.testnet alice.testnet --{} --force", SIGN_WITH_LEDGER_ALIASES[1]), + format!("account delete-account bob.testnet beneficiary alice.testnet network-config testnet sign-with-ledger --seed-phrase-hd-path '44'\\''/397'\\''/0'\\''/0'\\''/1'\\''' send") + ), + ( + format!("near delete-account bob.testnet alice.testnet --{} --force", SIGN_WITH_LEDGER_ALIASES[2]), + format!("account delete-account bob.testnet beneficiary alice.testnet network-config testnet sign-with-ledger --seed-phrase-hd-path '44'\\''/397'\\''/0'\\''/0'\\''/1'\\''' send") + ), + ( + format!("near delete-account bob.testnet alice.testnet --{} --force", SIGN_WITH_LEDGER_ALIASES[3]), + format!("account delete-account bob.testnet beneficiary alice.testnet network-config testnet sign-with-ledger --seed-phrase-hd-path '44'\\''/397'\\''/0'\\''/0'\\''/1'\\''' send") + ), + ( + format!("near delete-account bob.testnet alice.testnet --signWithLedger --{} \"44'/397'/0'/0'/2'\" --force", LEDGER_PATH_ALIASES[0]), + format!("account delete-account bob.testnet beneficiary alice.testnet network-config testnet sign-with-ledger --seed-phrase-hd-path '44'\\''/397'\\''/0'\\''/0'\\''/2'\\''' send") + ), + ( + format!("near delete-account bob.testnet alice.testnet --signWithLedger --{} \"44'/397'/0'/0'/2'\" --force", LEDGER_PATH_ALIASES[1]), + format!("account delete-account bob.testnet beneficiary alice.testnet network-config testnet sign-with-ledger --seed-phrase-hd-path '44'\\''/397'\\''/0'\\''/0'\\''/2'\\''' send") + ), + ( + format!("near delete-account bob.testnet alice.testnet --signWithLedger --{} mainnet --force", NETWORK_ID_ALIASES[0]), + format!("account delete-account bob.testnet beneficiary alice.testnet network-config mainnet sign-with-ledger --seed-phrase-hd-path '44'\\''/397'\\''/0'\\''/0'\\''/1'\\''' send") + ), + ( + format!("near delete-account bob.testnet alice.testnet --signWithLedger --{} mainnet --force", NETWORK_ID_ALIASES[1]), + format!("account delete-account bob.testnet beneficiary alice.testnet network-config mainnet sign-with-ledger --seed-phrase-hd-path '44'\\''/397'\\''/0'\\''/0'\\''/1'\\''' send") + ) + ] { + let input_cmd = shell_words::split(&input).expect("Input command must be a valid shell command"); + let JsCmd::DeleteAccount(delete_account_args) = JsCmd::parse_from(&input_cmd) else { + panic!("DeleteAccount command was expected, but something else was parsed out from {input}"); + }; + assert_eq!( + shell_words::join(DeleteAccountArgs::to_cli_args(&delete_account_args, "testnet".to_string())), + expected_output + ); + } + } +} diff --git a/src/js_command_match/account/login.rs b/src/js_command_match/account/login.rs new file mode 100644 index 000000000..d94941cf6 --- /dev/null +++ b/src/js_command_match/account/login.rs @@ -0,0 +1,65 @@ +use crate::js_command_match::constants::NETWORK_ID_ALIASES; + +#[derive(Debug, Clone, clap::Parser)] +/// This is a legacy `legacy` command. Once you run it with the specified arguments, new syntax command will be suggested. +pub struct LoginArgs { + #[clap(long, aliases = NETWORK_ID_ALIASES)] + network_id: Option, +} + +impl LoginArgs { + pub fn to_cli_args(&self, network_config: String) -> Vec { + let network_id = self.network_id.clone().unwrap_or(network_config); + + let command = vec![ + "account".to_string(), + "import-account".to_string(), + "using-web-wallet".to_string(), + "network-config".to_string(), + network_id, + ]; + + command + } +} + +#[cfg(test)] +mod tests { + use super::super::super::JsCmd; + use super::*; + use clap::Parser; + + #[test] + fn login() { + for (input, expected_output) in [ + ( + "near import-account".to_string(), + "account import-account using-web-wallet network-config testnet".to_string(), + ), + ( + "near login".to_string(), + "account import-account using-web-wallet network-config testnet".to_string(), + ), + ( + format!("near login --{} testnet", NETWORK_ID_ALIASES[0]), + "account import-account using-web-wallet network-config testnet".to_string(), + ), + ( + format!("near login --{} mainnet", NETWORK_ID_ALIASES[1]), + "account import-account using-web-wallet network-config mainnet".to_string(), + ), + ] { + let input_cmd = + shell_words::split(&input).expect("Input command must be a valid shell command"); + let JsCmd::Login(login_args) = JsCmd::parse_from(&input_cmd) else { + panic!( + "Login command was expected, but something else was parsed out from {input}" + ); + }; + assert_eq!( + shell_words::join(LoginArgs::to_cli_args(&login_args, "testnet".to_string())), + expected_output + ); + } + } +} diff --git a/src/js_command_match/account/mod.rs b/src/js_command_match/account/mod.rs new file mode 100644 index 000000000..fe5a39a56 --- /dev/null +++ b/src/js_command_match/account/mod.rs @@ -0,0 +1,4 @@ +pub mod create; +pub mod delete; +pub mod login; +pub mod state; diff --git a/src/js_command_match/account/state.rs b/src/js_command_match/account/state.rs new file mode 100644 index 000000000..2fcdaa8b1 --- /dev/null +++ b/src/js_command_match/account/state.rs @@ -0,0 +1,71 @@ +use crate::js_command_match::constants::NETWORK_ID_ALIASES; + +#[derive(Debug, Clone, clap::Parser)] +pub struct StateArgs { + account_id: String, + #[clap(long, aliases = NETWORK_ID_ALIASES)] + network_id: Option, +} + +impl StateArgs { + pub fn to_cli_args(&self, network_config: String) -> Vec { + let network_id = self.network_id.clone().unwrap_or(network_config); + + let command = vec![ + "account".to_string(), + "view-account-summary".to_string(), + self.account_id.to_owned(), + "network-config".to_string(), + network_id, + "now".to_string(), + ]; + + command + } +} + +#[cfg(test)] +mod tests { + use super::super::super::JsCmd; + use super::*; + use clap::Parser; + + #[test] + fn state() { + for (input, expected_output) in [ + ( + "near state contract.testnet".to_string(), + "account view-account-summary contract.testnet network-config testnet now" + .to_string(), + ), + ( + format!( + "near state contract.testnet --{} testnet", + NETWORK_ID_ALIASES[0] + ), + "account view-account-summary contract.testnet network-config testnet now" + .to_string(), + ), + ( + format!( + "near state contract.testnet --{} mainnet", + NETWORK_ID_ALIASES[1] + ), + "account view-account-summary contract.testnet network-config mainnet now" + .to_string(), + ), + ] { + let input_cmd = + shell_words::split(&input).expect("Input command must be a valid shell command"); + let JsCmd::State(state_args) = JsCmd::parse_from(&input_cmd) else { + panic!( + "State command was expected, but something else was parsed out from {input}" + ); + }; + assert_eq!( + shell_words::join(StateArgs::to_cli_args(&state_args, "testnet".to_string())), + expected_output + ); + } + } +} diff --git a/src/js_command_match/add_key.rs b/src/js_command_match/add_key.rs deleted file mode 100644 index 297527d05..000000000 --- a/src/js_command_match/add_key.rs +++ /dev/null @@ -1,56 +0,0 @@ -#[derive(Debug, Clone, clap::Parser)] -/// This is a legacy `add-key` command. Once you run it with the specified arguments, new syntax command will be suggested. -pub struct AddKeyArgs { - account_id: String, - access_key: String, - #[clap(long, aliases = ["contract_id", "contractId"], default_value = None)] - contract_id: Option, - #[clap(long, aliases = ["method_names", "methodNames"], requires = "contract_id", value_delimiter = ',', num_args = 1..)] - method_names: Vec, - #[clap(long, default_value = None)] - allowance: Option, - #[clap(allow_hyphen_values = true, num_args = 0..)] - _unknown_args: Vec, -} - -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(), - allowance, - "--contract-account-id".to_owned(), - contract_id.to_owned(), - "--function-names".to_owned(), - self.method_names.join(","), - "use-manually-provided-public-key".to_owned(), - self.access_key.to_owned(), - "network-config".to_owned(), - network_config, - "sign-with-keychain".to_owned(), - "send".to_owned(), - ]; - } - vec![ - "account".to_owned(), - "add-key".to_owned(), - self.account_id.to_owned(), - "grant-full-access".to_owned(), - "use-manually-provided-public-key".to_owned(), - self.access_key.to_owned(), - "network-config".to_owned(), - network_config, - "sign-with-keychain".to_owned(), - "send".to_owned(), - ] - } -} diff --git a/src/js_command_match/call.rs b/src/js_command_match/call.rs deleted file mode 100644 index 371718131..000000000 --- a/src/js_command_match/call.rs +++ /dev/null @@ -1,46 +0,0 @@ -#[derive(Debug, Clone, clap::Parser)] -/// This is a legacy `call` command. Once you run it with the specified arguments, new syntax command will be suggested. -pub struct CallArgs { - contract_account_id: String, - method_name: String, - #[clap(long, default_value_t = false)] - base64: bool, - args: String, - #[clap(long, aliases = ["account_id", "accountId"])] - account_id: String, - #[clap(long, default_value_t = 30_000_000_000_000)] - gas: u64, - #[clap(long, default_value = "0")] - deposit: String, - #[clap(allow_hyphen_values = true, num_args = 0..)] - _unknown_args: Vec, -} - -impl CallArgs { - pub fn to_cli_args(&self, network_config: String) -> Vec { - let format_args = if self.base64 { - "base64-args" - } else { - "json-args" - }; - vec![ - "contract".to_owned(), - "call-function".to_owned(), - "as-transaction".to_owned(), - self.contract_account_id.to_owned(), - self.method_name.to_owned(), - format_args.to_owned(), - self.args.to_owned(), - "prepaid-gas".to_owned(), - format!("{} TeraGas", self.gas / 1_000_000_000_000), - "attached-deposit".to_owned(), - format!("{} NEAR", self.deposit), - "sign-as".to_owned(), - self.account_id.to_owned(), - "network-config".to_owned(), - network_config, - "sign-with-keychain".to_owned(), - "send".to_owned(), - ] - } -} diff --git a/src/js_command_match/clean.rs b/src/js_command_match/clean.rs deleted file mode 100644 index 3feea8415..000000000 --- a/src/js_command_match/clean.rs +++ /dev/null @@ -1,8 +0,0 @@ -#[derive(Debug, Clone, clap::Parser)] -/// This is a legacy `clean` command. Once you run it with the specified arguments, new syntax command will be suggested. -pub struct CleanArgs { - #[clap(long, aliases = ["out_dir", "outDir"], default_value = "./out")] - out_dir: String, - #[clap(allow_hyphen_values = true, num_args = 0..)] - _unknown_args: Vec, -} diff --git a/src/js_command_match/constants.rs b/src/js_command_match/constants.rs new file mode 100644 index 000000000..ad955f174 --- /dev/null +++ b/src/js_command_match/constants.rs @@ -0,0 +1,49 @@ +// Accounts +pub const USE_ACCOUNT_ALIASES: [&str; 6] = [ + "masterAccount", + "master-account", + "useAccount", + "use-account", + "accountId", + "account-id", +]; + +// Contracts +pub const CONTRACT_ID_ALIASES: [&str; 2] = ["contractId", "contract-id"]; +pub const METHOD_NAMES_ALIASES: [&str; 2] = ["methodNames", "method-names"]; + +// Keys +pub const PUBLIC_KEY_ALIASES: [&str; 2] = ["publicKey", "public-key"]; +pub const SEED_PHRASE_ALIASES: [&str; 2] = ["seedPhrase", "seed-phrase"]; + +// Ledger +pub const LEDGER_PATH_ALIASES: [&str; 2] = ["ledgerPath", "ledger-path"]; +pub const DEFAULT_SEED_PHRASE_PATH: &str = "44'/397'/0'/0'/1'"; +pub const SIGN_WITH_LEDGER_ALIASES: [&str; 4] = [ + "signWithLedger", + "sign-with-ledger", + "useLedgerKey", + "use-ledger-key", +]; +pub const USE_LEDGER_PK_ALIASES: [&str; 4] = [ + "useLedgerPK", + "use-ledger-pk", + "newLedgerKey", + "new-ledger-key", +]; +pub const PK_LEDGER_PATH_ALIASES: [&str; 2] = ["pkLedgerPath", "pk-ledger-path"]; + +// Balance and faucet +pub const INITIAL_BALANCE_ALIASES: [&str; 2] = ["initialBalance", "initial-balance"]; +pub const USE_FAUCET_ALIASES: [&str; 2] = ["useFaucet", "use-faucet"]; + +// SETTINGS +pub const NETWORK_ID_ALIASES: [&str; 2] = ["networkId", "network-id"]; +pub const BLOCK_ID_ALIASES: [&str; 2] = ["blockId", "block-id"]; + +// Deploy +pub const WASM_FILE_ALIASES: [&str; 2] = ["wasmFile", "wasm-file"]; +pub const INIT_FUNCTION_ALIASES: [&str; 2] = ["initFunction", "init-function"]; +pub const INIT_ARGS_ALIASES: [&str; 2] = ["initArgs", "init-args"]; +pub const INIT_GAS_ALIASES: [&str; 2] = ["initGas", "init-gas"]; +pub const INIT_DEPOSIT_ALIASES: [&str; 2] = ["initDeposit", "init-deposit"]; diff --git a/src/js_command_match/contract/call.rs b/src/js_command_match/contract/call.rs new file mode 100644 index 000000000..24598f972 --- /dev/null +++ b/src/js_command_match/contract/call.rs @@ -0,0 +1,168 @@ +use near_gas::NearGas; + +use crate::js_command_match::constants::{ + DEFAULT_SEED_PHRASE_PATH, LEDGER_PATH_ALIASES, NETWORK_ID_ALIASES, SIGN_WITH_LEDGER_ALIASES, + USE_ACCOUNT_ALIASES, +}; + +#[derive(Debug, Clone, clap::Parser)] +pub struct CallArgs { + contract_name: String, + method_name: String, + #[clap(default_value = "")] + args: String, + #[clap(long, aliases = USE_ACCOUNT_ALIASES)] + use_account: String, + #[clap(long, aliases = SIGN_WITH_LEDGER_ALIASES, default_value_t = false)] + sign_with_ledger: bool, + #[clap(long, aliases = LEDGER_PATH_ALIASES, default_value = DEFAULT_SEED_PHRASE_PATH)] + ledger_path: String, + #[clap(long, default_value_t = 30_000_000_000_000)] + gas: u64, + #[clap(long, default_value = "0")] + deposit: String, + #[clap(long, default_value = "0", conflicts_with = "deposit", aliases = ["depositYocto"])] + deposit_yocto: String, + #[clap(long, default_value_t = false)] + base64: bool, + #[clap(long, aliases = ["privateKey"])] + private_key: Option, + #[clap(long, aliases = NETWORK_ID_ALIASES)] + network_id: Option, +} + +impl CallArgs { + pub fn to_cli_args(&self, network_config: String) -> Vec { + let network_id = self.network_id.clone().unwrap_or(network_config); + + let mut command = vec![ + "contract".to_string(), + "call-function".to_string(), + "as-transaction".to_string(), + self.contract_name.to_owned(), + self.method_name.to_owned(), + ]; + + if self.base64 { + command.push("base64-args".to_string()); + } else { + command.push("json-args".to_string()); + }; + + command.push(self.args.to_owned()); + command.push("prepaid-gas".to_string()); + command.push(format!("{} Tgas", NearGas::from_gas(self.gas).as_tgas())); + command.push("attached-deposit".to_string()); + + if self.deposit_yocto != "0" { + command.push(format!("{} yoctonear", self.deposit_yocto)); + } else { + command.push(format!("{} NEAR", self.deposit)); + } + + command.push("sign-as".to_string()); + command.push(self.use_account.to_owned()); + command.push("network-config".to_string()); + command.push(network_id); + + if self.sign_with_ledger { + command.push("sign-with-ledger".to_string()); + command.push("--seed-phrase-hd-path".to_string()); + command.push(self.ledger_path.to_owned()); + } else if let Some(private_key) = &self.private_key { + command.push("sign-with-plaintext-private-key".to_string()); + command.push(private_key.to_string()); + } else { + command.push("sign-with-keychain".to_string()); + } + + command.push("send".to_string()); + + command + } +} + +#[cfg(test)] +mod tests { + use super::super::super::JsCmd; + use super::*; + use clap::Parser; + + #[test] + fn call() { + let json_args = "{\"player_guess\": \"tail\"}"; + let base64_args = "eyJwbGF5ZXJfZ3Vlc3MiOiAidGFpbCJ9"; + + for (input, expected_output) in [ + ( + format!("near call contract.testnet flip_coin '{json_args}' --{} bob.testnet", USE_ACCOUNT_ALIASES[0]), + format!("contract call-function as-transaction contract.testnet flip_coin json-args '{json_args}' prepaid-gas '30 Tgas' attached-deposit '0 NEAR' sign-as bob.testnet network-config testnet sign-with-keychain send") + ), + ( + format!("near call contract.testnet flip_coin '{json_args}' --{} bob.testnet", USE_ACCOUNT_ALIASES[1]), + format!("contract call-function as-transaction contract.testnet flip_coin json-args '{json_args}' prepaid-gas '30 Tgas' attached-deposit '0 NEAR' sign-as bob.testnet network-config testnet sign-with-keychain send") + ), + ( + format!("near call contract.testnet flip_coin '{json_args}' --{} bob.testnet", USE_ACCOUNT_ALIASES[2]), + format!("contract call-function as-transaction contract.testnet flip_coin json-args '{json_args}' prepaid-gas '30 Tgas' attached-deposit '0 NEAR' sign-as bob.testnet network-config testnet sign-with-keychain send") + ), + ( + format!("near call contract.testnet flip_coin '{json_args}' --{} bob.testnet", USE_ACCOUNT_ALIASES[3]), + format!("contract call-function as-transaction contract.testnet flip_coin json-args '{json_args}' prepaid-gas '30 Tgas' attached-deposit '0 NEAR' sign-as bob.testnet network-config testnet sign-with-keychain send") + ), + ( + format!("near call contract.testnet flip_coin '{json_args}' --{} bob.testnet", USE_ACCOUNT_ALIASES[4]), + format!("contract call-function as-transaction contract.testnet flip_coin json-args '{json_args}' prepaid-gas '30 Tgas' attached-deposit '0 NEAR' sign-as bob.testnet network-config testnet sign-with-keychain send") + ), + ( + format!("near call contract.testnet flip_coin '{json_args}' --{} bob.testnet", USE_ACCOUNT_ALIASES[5]), + format!("contract call-function as-transaction contract.testnet flip_coin json-args '{json_args}' prepaid-gas '30 Tgas' attached-deposit '0 NEAR' sign-as bob.testnet network-config testnet sign-with-keychain send") + ), + ( + format!("near call contract.testnet flip_coin {base64_args} --useAccount bob.testnet --base64"), + format!("contract call-function as-transaction contract.testnet flip_coin base64-args {base64_args} prepaid-gas '30 Tgas' attached-deposit '0 NEAR' sign-as bob.testnet network-config testnet sign-with-keychain send") + ), + ( + format!("near call contract.testnet flip_coin '{json_args}' --useAccount bob.testnet --{}", SIGN_WITH_LEDGER_ALIASES[0]), + format!("contract call-function as-transaction contract.testnet flip_coin json-args '{json_args}' prepaid-gas '30 Tgas' attached-deposit '0 NEAR' sign-as bob.testnet network-config testnet sign-with-ledger --seed-phrase-hd-path '44'\\''/397'\\''/0'\\''/0'\\''/1'\\''' send") + ), + ( + format!("near call contract.testnet flip_coin '{json_args}' --useAccount bob.testnet --{}", SIGN_WITH_LEDGER_ALIASES[1]), + format!("contract call-function as-transaction contract.testnet flip_coin json-args '{json_args}' prepaid-gas '30 Tgas' attached-deposit '0 NEAR' sign-as bob.testnet network-config testnet sign-with-ledger --seed-phrase-hd-path '44'\\''/397'\\''/0'\\''/0'\\''/1'\\''' send") + ), + ( + format!("near call contract.testnet flip_coin '{json_args}' --useAccount bob.testnet --{}", SIGN_WITH_LEDGER_ALIASES[2]), + format!("contract call-function as-transaction contract.testnet flip_coin json-args '{json_args}' prepaid-gas '30 Tgas' attached-deposit '0 NEAR' sign-as bob.testnet network-config testnet sign-with-ledger --seed-phrase-hd-path '44'\\''/397'\\''/0'\\''/0'\\''/1'\\''' send") + ), + ( + format!("near call contract.testnet flip_coin '{json_args}' --useAccount bob.testnet --{}", SIGN_WITH_LEDGER_ALIASES[3]), + format!("contract call-function as-transaction contract.testnet flip_coin json-args '{json_args}' prepaid-gas '30 Tgas' attached-deposit '0 NEAR' sign-as bob.testnet network-config testnet sign-with-ledger --seed-phrase-hd-path '44'\\''/397'\\''/0'\\''/0'\\''/1'\\''' send") + ), + ( + format!("near call contract.testnet flip_coin '{json_args}' --useAccount bob.testnet --signWithLedger --{} \"44'/397'/0'/0'/2'\"", LEDGER_PATH_ALIASES[0]), + format!("contract call-function as-transaction contract.testnet flip_coin json-args '{json_args}' prepaid-gas '30 Tgas' attached-deposit '0 NEAR' sign-as bob.testnet network-config testnet sign-with-ledger --seed-phrase-hd-path '44'\\''/397'\\''/0'\\''/0'\\''/2'\\''' send") + ), + ( + format!("near call contract.testnet flip_coin '{json_args}' --useAccount bob.testnet --signWithLedger --{} \"44'/397'/0'/0'/2'\"", LEDGER_PATH_ALIASES[1]), + format!("contract call-function as-transaction contract.testnet flip_coin json-args '{json_args}' prepaid-gas '30 Tgas' attached-deposit '0 NEAR' sign-as bob.testnet network-config testnet sign-with-ledger --seed-phrase-hd-path '44'\\''/397'\\''/0'\\''/0'\\''/2'\\''' send") + ), + ( + format!("near call contract.testnet flip_coin '{json_args}' --useAccount bob.testnet --{} mainnet", NETWORK_ID_ALIASES[0]), + format!("contract call-function as-transaction contract.testnet flip_coin json-args '{json_args}' prepaid-gas '30 Tgas' attached-deposit '0 NEAR' sign-as bob.testnet network-config mainnet sign-with-keychain send") + ), + ( + format!("near call contract.testnet flip_coin '{json_args}' --useAccount bob.testnet --{} mainnet", NETWORK_ID_ALIASES[1]), + format!("contract call-function as-transaction contract.testnet flip_coin json-args '{json_args}' prepaid-gas '30 Tgas' attached-deposit '0 NEAR' sign-as bob.testnet network-config mainnet sign-with-keychain send") + ), + ] { + let input_cmd = shell_words::split(&input).expect("Input command must be a valid shell command"); + let JsCmd::Call(call_args) = JsCmd::parse_from(&input_cmd) else { + panic!("Call command was expected, but something else was parsed out from {input}"); + }; + assert_eq!( + shell_words::join(CallArgs::to_cli_args(&call_args, "testnet".to_string())), + expected_output + ); + } + } +} diff --git a/src/js_command_match/contract/deploy.rs b/src/js_command_match/contract/deploy.rs new file mode 100644 index 000000000..bdaef16a7 --- /dev/null +++ b/src/js_command_match/contract/deploy.rs @@ -0,0 +1,142 @@ +use near_gas::NearGas; + +use crate::js_command_match::constants::{ + INIT_ARGS_ALIASES, INIT_DEPOSIT_ALIASES, INIT_FUNCTION_ALIASES, INIT_GAS_ALIASES, + NETWORK_ID_ALIASES, WASM_FILE_ALIASES, +}; + +#[derive(Debug, Clone, clap::Parser)] +pub struct DeployArgs { + account_id: String, + #[clap(required_unless_present = "wasm_file")] + wasm_file_path: Option, + #[clap(long, aliases = WASM_FILE_ALIASES )] + wasm_file: Option, + #[clap(long, aliases = INIT_FUNCTION_ALIASES)] + init_function: Option, + #[clap(long, aliases = INIT_ARGS_ALIASES, default_value = "{}")] + init_args: String, + #[clap(long, aliases = INIT_GAS_ALIASES, default_value_t = 30_000_000_000_000)] + init_gas: u64, + #[clap(long, aliases = INIT_DEPOSIT_ALIASES, default_value = "0")] + init_deposit: String, + #[clap(long, aliases = NETWORK_ID_ALIASES)] + network_id: Option, +} + +impl DeployArgs { + pub fn to_cli_args(&self, network_config: String) -> Vec { + let network_id = self.network_id.clone().unwrap_or(network_config); + let mut command = vec!["contract".to_string(), "deploy".to_string()]; + + command.push(self.account_id.to_owned()); + + let wasm_file = self + .wasm_file_path + .to_owned() + .or(self.wasm_file.to_owned()) + .unwrap(); + + command.push("use-file".to_string()); + command.push(wasm_file.to_owned()); + + if let Some(init_function) = &self.init_function { + command.push("with-init-call".to_string()); + command.push(init_function.to_string()); + command.push("json-args".to_string()); + command.push(self.init_args.to_owned()); + command.push("prepaid-gas".to_string()); + command.push(format!( + "{} Tgas", + NearGas::from_gas(self.init_gas).as_tgas() + )); + command.push("attached-deposit".to_string()); + command.push(format!("{} NEAR", self.init_deposit)); + } else { + command.push("without-init-call".to_string()); + } + + command.push("network-config".to_string()); + command.push(network_id); + command.push("sign-with-keychain".to_string()); + command.push("send".to_owned()); + + command + } +} + +#[cfg(test)] +mod tests { + use super::super::super::JsCmd; + use super::*; + use clap::Parser; + + #[test] + fn deploy() { + let args = "{\"owner_id\":\"contract.testnet\",\"total_supply\":\"1000000\"}"; + + for (input, expected_output) in [ + ( + "near deploy contract.testnet build/hello_near.wasm".to_string(), + "contract deploy contract.testnet use-file build/hello_near.wasm without-init-call network-config testnet sign-with-keychain send".to_string(), + ), + ( + format!("near deploy contract.testnet --{} build/hello_near.wasm", WASM_FILE_ALIASES[0]), + "contract deploy contract.testnet use-file build/hello_near.wasm without-init-call network-config testnet sign-with-keychain send".to_string(), + ), + ( + format!("near deploy contract.testnet --{} build/hello_near.wasm", WASM_FILE_ALIASES[1]), + "contract deploy contract.testnet use-file build/hello_near.wasm without-init-call network-config testnet sign-with-keychain send".to_string(), + ), + ( + format!("near deploy contract.testnet build/hello_near.wasm --{} new --initArgs '{args}'", INIT_FUNCTION_ALIASES[0]), + format!("contract deploy contract.testnet use-file build/hello_near.wasm with-init-call new json-args '{}' prepaid-gas '30 Tgas' attached-deposit '0 NEAR' network-config testnet sign-with-keychain send", args) + ), + ( + format!("near deploy contract.testnet build/hello_near.wasm --{} new --initArgs '{args}'", INIT_FUNCTION_ALIASES[1]), + format!("contract deploy contract.testnet use-file build/hello_near.wasm with-init-call new json-args '{}' prepaid-gas '30 Tgas' attached-deposit '0 NEAR' network-config testnet sign-with-keychain send", args) + ), + ( + format!("near deploy contract.testnet build/hello_near.wasm --initFunction new --{} '{args}'", INIT_ARGS_ALIASES[0]), + format!("contract deploy contract.testnet use-file build/hello_near.wasm with-init-call new json-args '{}' prepaid-gas '30 Tgas' attached-deposit '0 NEAR' network-config testnet sign-with-keychain send", args) + ), + ( + format!("near deploy contract.testnet build/hello_near.wasm --initFunction new --{} '{args}'", INIT_ARGS_ALIASES[1]), + format!("contract deploy contract.testnet use-file build/hello_near.wasm with-init-call new json-args '{}' prepaid-gas '30 Tgas' attached-deposit '0 NEAR' network-config testnet sign-with-keychain send", args) + ), + ( + format!("near deploy contract.testnet build/hello_near.wasm --initFunction new --initArgs '{args}' --{} 60000000000000", INIT_GAS_ALIASES[0]), + format!("contract deploy contract.testnet use-file build/hello_near.wasm with-init-call new json-args '{}' prepaid-gas '60 Tgas' attached-deposit '0 NEAR' network-config testnet sign-with-keychain send", args) + ), + ( + format!("near deploy contract.testnet build/hello_near.wasm --initFunction new --initArgs '{args}' --{} 60000000000000", INIT_GAS_ALIASES[1]), + format!("contract deploy contract.testnet use-file build/hello_near.wasm with-init-call new json-args '{}' prepaid-gas '60 Tgas' attached-deposit '0 NEAR' network-config testnet sign-with-keychain send", args) + ), + ( + format!("near deploy contract.testnet build/hello_near.wasm --initFunction new --initArgs '{args}' --initGas 60000000000000 --{} 1", INIT_DEPOSIT_ALIASES[0]), + format!("contract deploy contract.testnet use-file build/hello_near.wasm with-init-call new json-args '{}' prepaid-gas '60 Tgas' attached-deposit '1 NEAR' network-config testnet sign-with-keychain send", args) + ), + ( + format!("near deploy contract.testnet build/hello_near.wasm --initFunction new --initArgs '{args}' --initGas 60000000000000 --{} 1", INIT_DEPOSIT_ALIASES[1]), + format!("contract deploy contract.testnet use-file build/hello_near.wasm with-init-call new json-args '{}' prepaid-gas '60 Tgas' attached-deposit '1 NEAR' network-config testnet sign-with-keychain send", args) + ), + ( + format!("near deploy contract.testnet build/hello_near.wasm --{} testnet", NETWORK_ID_ALIASES[0]), + "contract deploy contract.testnet use-file build/hello_near.wasm without-init-call network-config testnet sign-with-keychain send".to_string(), + ), + ( + format!("near deploy contract.testnet build/hello_near.wasm --{} mainnet", NETWORK_ID_ALIASES[1]), + "contract deploy contract.testnet use-file build/hello_near.wasm without-init-call network-config mainnet sign-with-keychain send".to_string(), + ), + ] { + let input_cmd = shell_words::split(&input).expect("Input command must be a valid shell command"); + let JsCmd::Deploy(deploy_args) = JsCmd::parse_from(&input_cmd) else { + panic!("Deploy command was expected, but something else was parsed out from {input}"); + }; + assert_eq!( + shell_words::join(DeployArgs::to_cli_args(&deploy_args, "testnet".to_string())), + expected_output + ); + } + } +} diff --git a/src/js_command_match/contract/mod.rs b/src/js_command_match/contract/mod.rs new file mode 100644 index 000000000..684c9fa38 --- /dev/null +++ b/src/js_command_match/contract/mod.rs @@ -0,0 +1,4 @@ +pub mod call; +pub mod deploy; +pub mod storage; +pub mod view; diff --git a/src/js_command_match/contract/storage.rs b/src/js_command_match/contract/storage.rs new file mode 100644 index 000000000..97c4cfeb9 --- /dev/null +++ b/src/js_command_match/contract/storage.rs @@ -0,0 +1,104 @@ +use crate::js_command_match::constants::{BLOCK_ID_ALIASES, NETWORK_ID_ALIASES}; + +#[derive(Debug, Clone, clap::Parser)] +pub struct ViewStateArgs { + account_id: String, + #[clap(long)] + prefix: Option, + #[clap(long, default_value_t = false)] + utf8: bool, + #[clap(long, aliases = BLOCK_ID_ALIASES)] + block_id: Option, + #[clap(long, conflicts_with = "block_id")] + finality: Option, + #[clap(long, aliases = NETWORK_ID_ALIASES)] + network_id: Option, +} + +impl ViewStateArgs { + pub fn to_cli_args(&self, network_config: String) -> Vec { + let network_id = self.network_id.clone().unwrap_or(network_config); + + let mut command = vec![ + "contract".to_string(), + "view-storage".to_string(), + self.account_id.to_owned(), + ]; + + let output_format = if self.utf8 { "as-text" } else { "as-json" }; + + if let Some(prefix) = &self.prefix { + let prefix_type = match near_primitives::serialize::from_base64(&prefix[..]) { + Ok(_) => "keys-start-with-bytes-as-base64".to_string(), + Err(_) => "keys-start-with-string".to_string(), + }; + + command.push(prefix_type); + command.push(prefix.to_string()); + } else { + command.push("all".to_string()); + } + + command.push(output_format.to_owned()); + command.push("network-config".to_string()); + command.push(network_id); + + if self.finality.is_some() { + command.push("now".to_string()); + } else if let Some(block_id) = &self.block_id { + match block_id.parse::() { + Ok(_) => { + command.push("at-block-height".to_string()); + } + Err(_) => { + command.push("at-block-hash".to_string()); + } + } + command.push(block_id.to_string()); + } + + command + } +} + +#[cfg(test)] +mod tests { + use super::super::super::JsCmd; + use super::*; + use clap::Parser; + + #[test] + fn view_state() { + for (input, expected_output) in [ + ( + format!("near storage counter.near-examples.testnet --prefix U1RBVEU= --{} 167860267", BLOCK_ID_ALIASES[0]), + "contract view-storage counter.near-examples.testnet keys-start-with-bytes-as-base64 'U1RBVEU=' as-json network-config testnet at-block-height 167860267" + ), + ( + format!("near view-state counter.near-examples.testnet --prefix U1RBVEU= --{} 167860267", BLOCK_ID_ALIASES[0]), + "contract view-storage counter.near-examples.testnet keys-start-with-bytes-as-base64 'U1RBVEU=' as-json network-config testnet at-block-height 167860267" + ), + ( + format!("near view-state counter.near-examples.testnet --prefix U1RBVEU= --{} 167860267", BLOCK_ID_ALIASES[1]), + "contract view-storage counter.near-examples.testnet keys-start-with-bytes-as-base64 'U1RBVEU=' as-json network-config testnet at-block-height 167860267" + ), + ( + format!("near view-state counter.near-examples.testnet --prefix STATE --utf8 --finality final --{} mainnet", NETWORK_ID_ALIASES[0]), + "contract view-storage counter.near-examples.testnet keys-start-with-string STATE as-text network-config mainnet now" + ), + ( + format!("near view-state counter.near-examples.testnet --prefix STATE --utf8 --finality final --{} mainnet", NETWORK_ID_ALIASES[1]), + "contract view-storage counter.near-examples.testnet keys-start-with-string STATE as-text network-config mainnet now" + ), + ] { + let input_cmd = shell_words::split(&input).expect("Input command must be a valid shell command"); + let JsCmd::ViewState(view_state_args) = JsCmd::parse_from(&input_cmd) else { + panic!("ViewState command was expected, but something else was parsed out from {input}"); + }; + assert_eq!( + shell_words::join(ViewStateArgs::to_cli_args(&view_state_args, "testnet".to_string())), + expected_output + ); + } + } +} diff --git a/src/js_command_match/contract/view.rs b/src/js_command_match/contract/view.rs new file mode 100644 index 000000000..a9733db85 --- /dev/null +++ b/src/js_command_match/contract/view.rs @@ -0,0 +1,69 @@ +use crate::js_command_match::constants::NETWORK_ID_ALIASES; + +#[derive(Debug, Clone, clap::Parser)] +/// This is a legacy `view` command. Once you run it with the specified arguments, new syntax command will be suggested. +pub struct ViewArgs { + contract_name: String, + method_name: String, + #[clap(default_value = "")] + args: String, + #[clap(long, aliases = NETWORK_ID_ALIASES)] + network_id: Option, +} + +impl ViewArgs { + pub fn to_cli_args(&self, network_config: String) -> Vec { + let network_id = self.network_id.clone().unwrap_or(network_config); + + let command = vec![ + "contract".to_string(), + "call-function".to_string(), + "as-read-only".to_string(), + self.contract_name.to_owned(), + self.method_name.to_owned(), + "text-args".to_string(), + self.args.to_owned(), + "network-config".to_string(), + network_id, + "now".to_string(), + ]; + + command + } +} + +#[cfg(test)] +mod tests { + use super::super::super::JsCmd; + use super::*; + use clap::Parser; + + #[test] + fn view() { + let args = "{\"account_id\": \"bob.testnet\"}"; + + for (input, expected_output) in [ + ( + format!("near view counter.near-examples.testnet get '{args}'"), + format!("contract call-function as-read-only counter.near-examples.testnet get text-args '{args}' network-config testnet now") + ), + ( + format!("near view counter.near-examples.testnet get '{args}' --{} testnet", NETWORK_ID_ALIASES[0]), + format!("contract call-function as-read-only counter.near-examples.testnet get text-args '{args}' network-config testnet now") + ), + ( + format!("near view counter.near-examples.testnet get '{args}' --{} mainnet", NETWORK_ID_ALIASES[1]), + format!("contract call-function as-read-only counter.near-examples.testnet get text-args '{args}' network-config mainnet now") + ), + ] { + let input_cmd = shell_words::split(&input).expect("Input command must be a valid shell command"); + let JsCmd::View(view_args) = JsCmd::parse_from(&input_cmd) else { + panic!("View command was expected, but something else was parsed out from {input}"); + }; + assert_eq!( + shell_words::join(ViewArgs::to_cli_args(&view_args, "testnet".to_string())), + expected_output + ); + } + } +} diff --git a/src/js_command_match/create_account.rs b/src/js_command_match/create_account.rs deleted file mode 100644 index b23ab84ba..000000000 --- a/src/js_command_match/create_account.rs +++ /dev/null @@ -1,69 +0,0 @@ -#[derive(Debug, Clone, clap::Parser)] -/// This is a legacy `create-account` command. Once you run it with the specified arguments, new syntax command will be suggested. -pub struct CreateAccountArgs { - account_id: String, - #[clap(long, aliases = ["master_account", "masterAccount"])] - master_account: String, - #[clap(long, aliases = ["public_key", "publicKey"], default_value = None, conflicts_with = "new_ledger_key")] - public_key: Option, - #[clap(long, aliases = ["new_ledger_key", "newLedgerKey"], default_missing_value = Some("44'/397'/0'/0'/1'"), num_args=0..=1)] - new_ledger_key: Option, - #[clap(long, aliases = ["initial_balance", "initialBalance"], default_value = "100")] - initial_balance: String, - #[clap(allow_hyphen_values = true, num_args = 0..)] - _unknown_args: Vec, -} - -impl CreateAccountArgs { - pub fn to_cli_args(&self, network_config: String) -> Vec { - if self.new_ledger_key.is_some() { - return vec![ - "account".to_owned(), - "create-account".to_owned(), - "fund-myself".to_owned(), - self.account_id.to_owned(), - format!("{} NEAR", self.initial_balance), - "use-ledger".to_owned(), - "sign-as".to_owned(), - self.master_account.to_owned(), - "network-config".to_owned(), - network_config, - "sign-with-keychain".to_owned(), - "send".to_owned(), - ]; - } - if let Some(public_key) = self.public_key.as_deref() { - vec![ - "account".to_owned(), - "create-account".to_owned(), - "fund-myself".to_owned(), - self.account_id.to_owned(), - format!("{} NEAR", self.initial_balance), - "use-manually-provided-public-key".to_owned(), - public_key.to_owned(), - "sign-as".to_owned(), - self.master_account.to_owned(), - "network-config".to_owned(), - network_config, - "sign-with-keychain".to_owned(), - "send".to_owned(), - ] - } else { - vec![ - "account".to_owned(), - "create-account".to_owned(), - "fund-myself".to_owned(), - self.account_id.to_owned(), - format!("{} NEAR", self.initial_balance), - "autogenerate-new-keypair".to_owned(), - "save-to-keychain".to_owned(), - "sign-as".to_owned(), - self.master_account.to_owned(), - "network-config".to_owned(), - network_config, - "sign-with-keychain".to_owned(), - "send".to_owned(), - ] - } - } -} diff --git a/src/js_command_match/delete.rs b/src/js_command_match/delete.rs deleted file mode 100644 index 2e4d2d121..000000000 --- a/src/js_command_match/delete.rs +++ /dev/null @@ -1,24 +0,0 @@ -#[derive(Debug, Clone, clap::Parser)] -/// This is a legacy `delete` command. Once you run it with the specified arguments, new syntax command will be suggested. -pub struct DeleteArgs { - account_id: String, - beneficiary_id: String, - #[clap(allow_hyphen_values = true, num_args = 0..)] - _unknown_args: Vec, -} - -impl DeleteArgs { - pub fn to_cli_args(&self, network_config: String) -> Vec { - vec![ - "account".to_owned(), - "delete-account".to_owned(), - self.account_id.to_owned(), - "beneficiary".to_owned(), - self.beneficiary_id.to_owned(), - "network-config".to_owned(), - network_config, - "sign-with-keychain".to_owned(), - "send".to_owned(), - ] - } -} diff --git a/src/js_command_match/delete_key.rs b/src/js_command_match/delete_key.rs deleted file mode 100644 index b14132eea..000000000 --- a/src/js_command_match/delete_key.rs +++ /dev/null @@ -1,23 +0,0 @@ -#[derive(Debug, Clone, clap::Parser)] -/// This is a legacy `delete-key` command. Once you run it with the specified arguments, new syntax command will be suggested. -pub struct DeleteKeyArgs { - account_id: String, - access_key: String, - #[clap(allow_hyphen_values = true, num_args = 0..)] - _unknown_args: Vec, -} - -impl DeleteKeyArgs { - pub fn to_cli_args(&self, network_config: String) -> Vec { - vec![ - "account".to_owned(), - "delete-key".to_owned(), - self.account_id.to_owned(), - self.access_key.to_owned(), - "network-config".to_owned(), - network_config, - "sign-with-keychain".to_owned(), - "send".to_owned(), - ] - } -} diff --git a/src/js_command_match/deploy.rs b/src/js_command_match/deploy.rs deleted file mode 100644 index 30b7769b4..000000000 --- a/src/js_command_match/deploy.rs +++ /dev/null @@ -1,78 +0,0 @@ -#[derive(Debug, Clone, clap::Parser)] -/// This is a legacy `deploy` command. Once you run it with the specified arguments, new syntax command will be suggested. -pub struct DeployArgs { - contract_account_id: Option, - #[clap(long, aliases = ["account_id", "accountId", "contract_name", "contractName"])] - account_id: Option, - wasm_file_path: Option, - #[clap(long, aliases = ["wasm_file", "wasmFile"])] - wasm_file: Option, - #[clap(long, aliases = ["init_function", "initFunction"])] - init_function: Option, - #[clap(long, aliases = ["init_args", "initArgs"])] - init_args: Option, - #[clap(long, aliases = ["init_gas", "initGas"], default_value_t = 30_000_000_000_000)] - init_gas: u64, - #[clap(long, aliases = ["init_deposit", "initDeposit"], default_value = "0")] - init_deposit: String, - #[clap(allow_hyphen_values = true, num_args = 0..)] - _unknown_args: Vec, -} - -impl DeployArgs { - pub fn to_cli_args(&self, network_config: String) -> Vec { - let contract_account_id = if let Some(account_id) = &self.contract_account_id { - account_id - } else if let Some(account_id) = &self.account_id { - account_id - } else { - return vec!["contract".to_owned(), "deploy".to_owned()]; - }; - let wasm_file = if let Some(file_path) = &self.wasm_file_path { - file_path - } else if let Some(wasm_file) = &self.wasm_file { - wasm_file - } else { - return vec![ - "contract".to_owned(), - "deploy".to_owned(), - contract_account_id.to_owned(), - ]; - }; - - if self.init_function.is_some() { - vec![ - "contract".to_owned(), - "deploy".to_owned(), - contract_account_id.to_owned(), - "use-file".to_owned(), - wasm_file.to_owned(), - "with-init-call".to_owned(), - self.init_function.as_deref().unwrap_or("new").to_owned(), - "json-args".to_owned(), - self.init_args.as_deref().unwrap_or("{}").to_owned(), - "prepaid-gas".to_owned(), - format!("{} TeraGas", self.init_gas / 1_000_000_000_000), - "attached-deposit".to_owned(), - format!("{} NEAR", self.init_deposit), - "network-config".to_owned(), - network_config, - "sign-with-keychain".to_owned(), - "send".to_owned(), - ] - } else { - vec![ - "contract".to_owned(), - "deploy".to_owned(), - contract_account_id.to_owned(), - "use-file".to_owned(), - wasm_file.to_owned(), - "without-init-call".to_owned(), - "network-config".to_owned(), - network_config, - "sign-with-keychain".to_owned(), - "send".to_owned(), - ] - } - } -} diff --git a/src/js_command_match/dev_deploy.rs b/src/js_command_match/dev_deploy.rs deleted file mode 100644 index 1448e3b65..000000000 --- a/src/js_command_match/dev_deploy.rs +++ /dev/null @@ -1,72 +0,0 @@ -#[derive(Debug, Clone, clap::Parser)] -/// This is a legacy `dev-deploy` command. Once you run it with the specified arguments, new syntax command will be suggested. -pub struct DevDeployArgs { - wasm_file_path: Option, - #[clap(long, aliases = ["wasm_file", "wasmFile"])] - wasm_file: Option, - #[clap(long, aliases = ["init_function", "initFunction"])] - init_function: Option, - #[clap(long, aliases = ["init_args", "initArgs"])] - init_args: Option, - #[clap(long, aliases = ["init_gas", "initGas"], default_value_t = 30_000_000_000_000)] - init_gas: u64, - #[clap(long, aliases = ["init_deposit", "initDeposit"], default_value = "0")] - init_deposit: String, - #[clap(long, aliases = ["initial_balance", "initialBalance"], default_value = "100")] - initial_balance: String, - #[clap(long, default_value_t = false)] - force: bool, - #[clap(allow_hyphen_values = true, num_args = 0..)] - _unknown_args: Vec, -} - -impl DevDeployArgs { - pub fn to_cli_args(&self, network_config: String) { - eprintln!("The command you tried to run is deprecated in the new NEAR CLI, but we tried our best to match the old command with the new syntax, try it instead:\n"); - eprintln!("Make sure you have the cargo-near app installed on your computer (https://github.com/near/cargo-near/blob/main/README.md)"); - eprintln!("In your project folder (cargo.toml) do the following:"); - eprintln!("1. Create a dev-account using the command:\n cargo near create-dev-account\n"); - eprintln!( - "2. Run the following command, after inserting the name of the created dev-account:" - ); - - if self.init_function.is_some() { - eprintln!( - " {}", - shell_words::join(vec![ - "cargo".to_owned(), - "near".to_owned(), - "deploy".to_owned(), - "".to_owned(), - "with-init-call".to_owned(), - self.init_function.as_deref().unwrap_or("new").to_owned(), - "json-args".to_owned(), - self.init_args.as_deref().unwrap_or("{}").to_owned(), - "prepaid-gas".to_owned(), - format!("{} TeraGas", self.init_gas / 1_000_000_000_000), - "attached-deposit".to_owned(), - format!("{} NEAR", self.init_deposit), - "network-config".to_owned(), - network_config, - "sign-with-keychain".to_owned(), - "send".to_owned(), - ]) - ); - } else { - eprintln!( - " {}", - shell_words::join(vec![ - "cargo".to_owned(), - "near".to_owned(), - "deploy".to_owned(), - "".to_owned(), - "without-init-call".to_owned(), - "network-config".to_owned(), - network_config, - "sign-with-keychain".to_owned(), - "send".to_owned(), - ]) - ); - } - } -} diff --git a/src/js_command_match/evm_call.rs b/src/js_command_match/evm_call.rs deleted file mode 100644 index 2f9f5e2e1..000000000 --- a/src/js_command_match/evm_call.rs +++ /dev/null @@ -1,6 +0,0 @@ -#[derive(Debug, Clone, clap::Parser)] -/// This is a legacy `evm-call` command. Once you run it with the specified arguments, new syntax command will be suggested. -pub struct EvmCallArgs { - #[clap(allow_hyphen_values = true, num_args = 0..)] - evm_call_args: Vec, -} diff --git a/src/js_command_match/evm_dev_init.rs b/src/js_command_match/evm_dev_init.rs deleted file mode 100644 index ab2e9617c..000000000 --- a/src/js_command_match/evm_dev_init.rs +++ /dev/null @@ -1,6 +0,0 @@ -#[derive(Debug, Clone, clap::Parser)] -/// This is a legacy `evm-dev-init` command. Once you run it with the specified arguments, new syntax command will be suggested. -pub struct EvmDevInitArgs { - #[clap(allow_hyphen_values = true, num_args = 0..)] - evm_dev_init_args: Vec, -} diff --git a/src/js_command_match/evm_view.rs b/src/js_command_match/evm_view.rs deleted file mode 100644 index f33a8f956..000000000 --- a/src/js_command_match/evm_view.rs +++ /dev/null @@ -1,6 +0,0 @@ -#[derive(Debug, Clone, clap::Parser)] -/// This is a legacy `evm-view` command. Once you run it with the specified arguments, new syntax command will be suggested. -pub struct EvmViewArgs { - #[clap(allow_hyphen_values = true, num_args = 0..)] - evm_view_args: Vec, -} diff --git a/src/js_command_match/generate_key.rs b/src/js_command_match/generate_key.rs deleted file mode 100644 index 8740ddd4e..000000000 --- a/src/js_command_match/generate_key.rs +++ /dev/null @@ -1,81 +0,0 @@ -#[derive(Debug, Clone, clap::Parser)] -/// This is a legacy `generate-key` command. Once you run it with the specified arguments, new syntax command will be suggested. -pub struct GenerateKeyArgs { - account_id: Option, - #[clap(long, aliases = ["seed_phrase", "seedPhrase"], default_value = None, conflicts_with = "use_ledger_key")] - seed_phrase: Option, - #[clap(long, aliases = ["use_ledger_key", "useLedgerKey"], default_missing_value = Some("44'/397'/0'/0'/1'"), num_args=0..=1)] - use_ledger_key: Option, - #[clap(allow_hyphen_values = true, num_args = 0..)] - _unknown_args: Vec, -} - -impl GenerateKeyArgs { - pub fn to_cli_args(&self, network_config: String) -> color_eyre::eyre::Result> { - let config = crate::config::Config::get_config_toml()?; - let mut generation_method = "use-auto-generation".to_string(); - if self.use_ledger_key.is_some() { - generation_method = "use-ledger".to_string(); - } - if let Some(account_id) = self.account_id.as_deref() { - let folder_path = shellexpand::tilde( - format!( - "{}/{}/{}", - config.credentials_home_dir.to_string_lossy(), - network_config, - account_id - ) - .as_str(), - ) - .as_ref() - .parse()?; - if let Some(seed_phrase) = self.seed_phrase.as_deref() { - return Ok(vec![ - "account".to_owned(), - "create-account".to_owned(), - "fund-later".to_owned(), - "use-seed-phrase".to_owned(), - seed_phrase.to_owned(), - "--seed-phrase-hd-path".to_owned(), - "m/44'/397'/0'".to_owned(), - "save-to-folder".to_owned(), - folder_path, - ]); - } - return Ok(vec![ - "account".to_owned(), - "create-account".to_owned(), - "fund-later".to_owned(), - generation_method, - "save-to-folder".to_owned(), - folder_path, - ]); - } - let folder_path = shellexpand::tilde( - format!("{}/implicit", config.credentials_home_dir.to_string_lossy()).as_str(), - ) - .as_ref() - .parse()?; - if let Some(seed_phrase) = self.seed_phrase.as_deref() { - return Ok(vec![ - "account".to_owned(), - "create-account".to_owned(), - "fund-later".to_owned(), - "use-seed-phrase".to_owned(), - seed_phrase.to_owned(), - "--seed-phrase-hd-path".to_owned(), - "m/44'/397'/0'".to_owned(), - "save-to-folder".to_owned(), - folder_path, - ]); - } - Ok(vec![ - "account".to_owned(), - "create-account".to_owned(), - "fund-later".to_owned(), - generation_method, - "save-to-folder".to_owned(), - folder_path, - ]) - } -} diff --git a/src/js_command_match/js.rs b/src/js_command_match/js.rs deleted file mode 100644 index 00d2d3e2a..000000000 --- a/src/js_command_match/js.rs +++ /dev/null @@ -1,5 +0,0 @@ -#[derive(Debug, Clone, clap::Parser)] -pub struct JsArgs { - #[clap(allow_hyphen_values = true, num_args = 0..)] - js_args: Vec, -} diff --git a/src/js_command_match/keys.rs b/src/js_command_match/keys.rs deleted file mode 100644 index d339dbd5c..000000000 --- a/src/js_command_match/keys.rs +++ /dev/null @@ -1,20 +0,0 @@ -#[derive(Debug, Clone, clap::Parser)] -/// This is a legacy `keys` command. Once you run it with the specified arguments, new syntax command will be suggested. -pub struct KeysArgs { - account_id: String, - #[clap(allow_hyphen_values = true, num_args = 0..)] - _unknown_args: Vec, -} - -impl KeysArgs { - pub fn to_cli_args(&self, network_config: String) -> Vec { - vec![ - "account".to_owned(), - "list-keys".to_owned(), - self.account_id.to_owned(), - "network-config".to_owned(), - network_config, - "now".to_owned(), - ] - } -} diff --git a/src/js_command_match/keys/add.rs b/src/js_command_match/keys/add.rs new file mode 100644 index 000000000..ba617d747 --- /dev/null +++ b/src/js_command_match/keys/add.rs @@ -0,0 +1,138 @@ +use crate::js_command_match::constants::{ + CONTRACT_ID_ALIASES, DEFAULT_SEED_PHRASE_PATH, LEDGER_PATH_ALIASES, METHOD_NAMES_ALIASES, + NETWORK_ID_ALIASES, SIGN_WITH_LEDGER_ALIASES, +}; + +#[derive(Debug, Clone, clap::Parser)] +pub struct AddKeyArgs { + account_id: String, + public_key: String, + #[clap(long, aliases = CONTRACT_ID_ALIASES)] + contract_id: Option, + #[clap(long, aliases = METHOD_NAMES_ALIASES, requires = "contract_id", default_value="", value_delimiter = ',', num_args = 0..)] + method_names: Vec, + #[clap(long, default_value = "0")] + allowance: String, + #[clap(long, aliases = SIGN_WITH_LEDGER_ALIASES, default_value_t = false)] + sign_with_ledger: bool, + #[clap(long, aliases = LEDGER_PATH_ALIASES, default_value = DEFAULT_SEED_PHRASE_PATH)] + ledger_path: String, + #[clap(long, aliases = NETWORK_ID_ALIASES)] + network_id: Option, +} + +impl AddKeyArgs { + pub fn to_cli_args(&self, network_config: String) -> Vec { + let network_id = self.network_id.clone().unwrap_or(network_config); + + let mut command = vec![ + "account".to_string(), + "add-key".to_string(), + self.account_id.to_owned(), + ]; + + if let Some(contract_id) = &self.contract_id { + let allowance = if self.allowance != "0" { + format!("{} NEAR", self.allowance) + } else { + "unlimited".to_string() + }; + + command.push("grant-function-call-access".to_string()); + command.push("--allowance".to_string()); + command.push(allowance); + command.push("--contract-account-id".to_string()); + command.push(contract_id.to_string()); + command.push("--function-names".to_string()); + command.push(self.method_names.join(",")); + } else { + command.push("grant-full-access".to_string()); + } + + command.push("use-manually-provided-public-key".to_string()); + command.push(self.public_key.to_owned()); + command.push("network-config".to_string()); + command.push(network_id); + + if self.sign_with_ledger { + command.push("sign-with-ledger".to_string()); + command.push("--seed-phrase-hd-path".to_string()); + command.push(self.ledger_path.to_owned()); + } else { + command.push("sign-with-keychain".to_string()); + } + command.push("send".to_string()); + + command + } +} + +#[cfg(test)] +mod tests { + use super::super::super::JsCmd; + use super::*; + use clap::Parser; + + #[test] + fn add_key() { + for (input, expected_output) in [ + ( + format!("near add-key bob.testnet ed25519:DReZmNmnGhpsYcCFFeYgPsJ9YCm9xH16GGujCPe3KQEq --{} contract.testnet", CONTRACT_ID_ALIASES[0]), + "account add-key bob.testnet grant-function-call-access --allowance unlimited --contract-account-id contract.testnet --function-names '' use-manually-provided-public-key ed25519:DReZmNmnGhpsYcCFFeYgPsJ9YCm9xH16GGujCPe3KQEq network-config testnet sign-with-keychain send" + ), + ( + format!("near add-key bob.testnet ed25519:DReZmNmnGhpsYcCFFeYgPsJ9YCm9xH16GGujCPe3KQEq --{} contract.testnet", CONTRACT_ID_ALIASES[1]), + "account add-key bob.testnet grant-function-call-access --allowance unlimited --contract-account-id contract.testnet --function-names '' use-manually-provided-public-key ed25519:DReZmNmnGhpsYcCFFeYgPsJ9YCm9xH16GGujCPe3KQEq network-config testnet sign-with-keychain send" + ), + ( + format!("near add-key bob.testnet ed25519:DReZmNmnGhpsYcCFFeYgPsJ9YCm9xH16GGujCPe3KQEq --contractId contract.testnet --{} get,set", METHOD_NAMES_ALIASES[0]), + "account add-key bob.testnet grant-function-call-access --allowance unlimited --contract-account-id contract.testnet --function-names get,set use-manually-provided-public-key ed25519:DReZmNmnGhpsYcCFFeYgPsJ9YCm9xH16GGujCPe3KQEq network-config testnet sign-with-keychain send" + ), + ( + format!("near add-key bob.testnet ed25519:DReZmNmnGhpsYcCFFeYgPsJ9YCm9xH16GGujCPe3KQEq --contractId contract.testnet --{} get,set", METHOD_NAMES_ALIASES[1]), + "account add-key bob.testnet grant-function-call-access --allowance unlimited --contract-account-id contract.testnet --function-names get,set use-manually-provided-public-key ed25519:DReZmNmnGhpsYcCFFeYgPsJ9YCm9xH16GGujCPe3KQEq network-config testnet sign-with-keychain send" + ), + ( + format!("near add-key bob.testnet ed25519:DReZmNmnGhpsYcCFFeYgPsJ9YCm9xH16GGujCPe3KQEq --contractId contract.testnet --{}", SIGN_WITH_LEDGER_ALIASES[0]), + "account add-key bob.testnet grant-function-call-access --allowance unlimited --contract-account-id contract.testnet --function-names '' use-manually-provided-public-key ed25519:DReZmNmnGhpsYcCFFeYgPsJ9YCm9xH16GGujCPe3KQEq network-config testnet sign-with-ledger --seed-phrase-hd-path '44'\\''/397'\\''/0'\\''/0'\\''/1'\\''' send" + ), + ( + format!("near add-key bob.testnet ed25519:DReZmNmnGhpsYcCFFeYgPsJ9YCm9xH16GGujCPe3KQEq --contractId contract.testnet --{}", SIGN_WITH_LEDGER_ALIASES[1]), + "account add-key bob.testnet grant-function-call-access --allowance unlimited --contract-account-id contract.testnet --function-names '' use-manually-provided-public-key ed25519:DReZmNmnGhpsYcCFFeYgPsJ9YCm9xH16GGujCPe3KQEq network-config testnet sign-with-ledger --seed-phrase-hd-path '44'\\''/397'\\''/0'\\''/0'\\''/1'\\''' send" + ), + ( + format!("near add-key bob.testnet ed25519:DReZmNmnGhpsYcCFFeYgPsJ9YCm9xH16GGujCPe3KQEq --contractId contract.testnet --{}", SIGN_WITH_LEDGER_ALIASES[2]), + "account add-key bob.testnet grant-function-call-access --allowance unlimited --contract-account-id contract.testnet --function-names '' use-manually-provided-public-key ed25519:DReZmNmnGhpsYcCFFeYgPsJ9YCm9xH16GGujCPe3KQEq network-config testnet sign-with-ledger --seed-phrase-hd-path '44'\\''/397'\\''/0'\\''/0'\\''/1'\\''' send" + ), + ( + format!("near add-key bob.testnet ed25519:DReZmNmnGhpsYcCFFeYgPsJ9YCm9xH16GGujCPe3KQEq --contractId contract.testnet --{}", SIGN_WITH_LEDGER_ALIASES[3]), + "account add-key bob.testnet grant-function-call-access --allowance unlimited --contract-account-id contract.testnet --function-names '' use-manually-provided-public-key ed25519:DReZmNmnGhpsYcCFFeYgPsJ9YCm9xH16GGujCPe3KQEq network-config testnet sign-with-ledger --seed-phrase-hd-path '44'\\''/397'\\''/0'\\''/0'\\''/1'\\''' send" + ), + ( + format!("near add-key bob.testnet ed25519:DReZmNmnGhpsYcCFFeYgPsJ9YCm9xH16GGujCPe3KQEq --contractId contract.testnet --signWithLedger --{} \"44'/397'/0'/0'/2'\"", LEDGER_PATH_ALIASES[0]), + "account add-key bob.testnet grant-function-call-access --allowance unlimited --contract-account-id contract.testnet --function-names '' use-manually-provided-public-key ed25519:DReZmNmnGhpsYcCFFeYgPsJ9YCm9xH16GGujCPe3KQEq network-config testnet sign-with-ledger --seed-phrase-hd-path '44'\\''/397'\\''/0'\\''/0'\\''/2'\\''' send" + ), + ( + format!("near add-key bob.testnet ed25519:DReZmNmnGhpsYcCFFeYgPsJ9YCm9xH16GGujCPe3KQEq --contractId contract.testnet --signWithLedger --{} \"44'/397'/0'/0'/2'\"", LEDGER_PATH_ALIASES[1]), + "account add-key bob.testnet grant-function-call-access --allowance unlimited --contract-account-id contract.testnet --function-names '' use-manually-provided-public-key ed25519:DReZmNmnGhpsYcCFFeYgPsJ9YCm9xH16GGujCPe3KQEq network-config testnet sign-with-ledger --seed-phrase-hd-path '44'\\''/397'\\''/0'\\''/0'\\''/2'\\''' send" + ), + ( + format!("near add-key bob.testnet ed25519:DReZmNmnGhpsYcCFFeYgPsJ9YCm9xH16GGujCPe3KQEq --{} testnet", NETWORK_ID_ALIASES[0]), + "account add-key bob.testnet grant-full-access use-manually-provided-public-key ed25519:DReZmNmnGhpsYcCFFeYgPsJ9YCm9xH16GGujCPe3KQEq network-config testnet sign-with-keychain send" + ), + ( + format!("near add-key bob.testnet ed25519:DReZmNmnGhpsYcCFFeYgPsJ9YCm9xH16GGujCPe3KQEq --{} mainnet", NETWORK_ID_ALIASES[1]), + "account add-key bob.testnet grant-full-access use-manually-provided-public-key ed25519:DReZmNmnGhpsYcCFFeYgPsJ9YCm9xH16GGujCPe3KQEq network-config mainnet sign-with-keychain send" + ), + ] { + let input_cmd = shell_words::split(&input).expect("Input command must be a valid shell command"); + let JsCmd::AddKey(add_key_args) = JsCmd::parse_from(&input_cmd) else { + panic!("AddKey command was expected, but something else was parsed out from {input}"); + }; + assert_eq!( + shell_words::join(AddKeyArgs::to_cli_args(&add_key_args, "testnet".to_string())), + expected_output + ); + } + } +} diff --git a/src/js_command_match/keys/delete.rs b/src/js_command_match/keys/delete.rs new file mode 100644 index 000000000..575537a9b --- /dev/null +++ b/src/js_command_match/keys/delete.rs @@ -0,0 +1,102 @@ +use crate::js_command_match::constants::{ + DEFAULT_SEED_PHRASE_PATH, LEDGER_PATH_ALIASES, NETWORK_ID_ALIASES, SIGN_WITH_LEDGER_ALIASES, +}; + +#[derive(Debug, Clone, clap::Parser)] +/// This is a legacy `delete-key` command. Once you run it with the specified arguments, new syntax command will be suggested. +pub struct DeleteKeyArgs { + account_id: String, + access_key: String, + #[clap(long, aliases = SIGN_WITH_LEDGER_ALIASES, default_value_t = false)] + sign_with_ledger: bool, + #[clap(long, aliases = LEDGER_PATH_ALIASES, default_value = DEFAULT_SEED_PHRASE_PATH)] + ledger_path: String, + #[clap(long, aliases = NETWORK_ID_ALIASES)] + network_id: Option, +} + +impl DeleteKeyArgs { + pub fn to_cli_args(&self, network_config: String) -> Vec { + let network_id = self.network_id.clone().unwrap_or(network_config); + + let mut command = vec![ + "account".to_string(), + "delete-keys".to_string(), + self.account_id.to_owned(), + "public-keys".to_string(), + self.access_key.to_owned(), + "network-config".to_string(), + network_id, + ]; + + if self.sign_with_ledger { + command.push("sign-with-ledger".to_string()); + command.push("--seed-phrase-hd-path".to_string()); + command.push(self.ledger_path.to_owned()); + } else { + command.push("sign-with-keychain".to_string()); + } + + command.push("send".to_string()); + + command + } +} + +#[cfg(test)] +mod tests { + use super::super::super::JsCmd; + use super::*; + use clap::Parser; + + #[test] + fn delete_key() { + for (input, expected_output) in [ + ( + "near delete-key bob.testnet ed25519:DReZmNmnGhpsYcCFFeYgPsJ9YCm9xH16GGujCPe3KQEq".to_string(), + "account delete-keys bob.testnet public-keys ed25519:DReZmNmnGhpsYcCFFeYgPsJ9YCm9xH16GGujCPe3KQEq network-config testnet sign-with-keychain send".to_string() + ), + ( + format!("near delete-key bob.testnet ed25519:DReZmNmnGhpsYcCFFeYgPsJ9YCm9xH16GGujCPe3KQEq --{}", SIGN_WITH_LEDGER_ALIASES[0]), + "account delete-keys bob.testnet public-keys ed25519:DReZmNmnGhpsYcCFFeYgPsJ9YCm9xH16GGujCPe3KQEq network-config testnet sign-with-ledger --seed-phrase-hd-path '44'\\''/397'\\''/0'\\''/0'\\''/1'\\''' send".to_string() + ), + ( + format!("near delete-key bob.testnet ed25519:DReZmNmnGhpsYcCFFeYgPsJ9YCm9xH16GGujCPe3KQEq --{}", SIGN_WITH_LEDGER_ALIASES[1]), + "account delete-keys bob.testnet public-keys ed25519:DReZmNmnGhpsYcCFFeYgPsJ9YCm9xH16GGujCPe3KQEq network-config testnet sign-with-ledger --seed-phrase-hd-path '44'\\''/397'\\''/0'\\''/0'\\''/1'\\''' send".to_string() + ), + ( + format!("near delete-key bob.testnet ed25519:DReZmNmnGhpsYcCFFeYgPsJ9YCm9xH16GGujCPe3KQEq --{}", SIGN_WITH_LEDGER_ALIASES[2]), + "account delete-keys bob.testnet public-keys ed25519:DReZmNmnGhpsYcCFFeYgPsJ9YCm9xH16GGujCPe3KQEq network-config testnet sign-with-ledger --seed-phrase-hd-path '44'\\''/397'\\''/0'\\''/0'\\''/1'\\''' send".to_string() + ), + ( + format!("near delete-key bob.testnet ed25519:DReZmNmnGhpsYcCFFeYgPsJ9YCm9xH16GGujCPe3KQEq --{}", SIGN_WITH_LEDGER_ALIASES[3]), + "account delete-keys bob.testnet public-keys ed25519:DReZmNmnGhpsYcCFFeYgPsJ9YCm9xH16GGujCPe3KQEq network-config testnet sign-with-ledger --seed-phrase-hd-path '44'\\''/397'\\''/0'\\''/0'\\''/1'\\''' send".to_string() + ), + ( + format!("near delete-key bob.testnet ed25519:DReZmNmnGhpsYcCFFeYgPsJ9YCm9xH16GGujCPe3KQEq --signWithLedger --{} \"44'/397'/0'/0'/2'\"", LEDGER_PATH_ALIASES[0]), + "account delete-keys bob.testnet public-keys ed25519:DReZmNmnGhpsYcCFFeYgPsJ9YCm9xH16GGujCPe3KQEq network-config testnet sign-with-ledger --seed-phrase-hd-path '44'\\''/397'\\''/0'\\''/0'\\''/2'\\''' send".to_string() + ), + ( + format!("near delete-key bob.testnet ed25519:DReZmNmnGhpsYcCFFeYgPsJ9YCm9xH16GGujCPe3KQEq --signWithLedger --{} \"44'/397'/0'/0'/2'\"", LEDGER_PATH_ALIASES[1]), + "account delete-keys bob.testnet public-keys ed25519:DReZmNmnGhpsYcCFFeYgPsJ9YCm9xH16GGujCPe3KQEq network-config testnet sign-with-ledger --seed-phrase-hd-path '44'\\''/397'\\''/0'\\''/0'\\''/2'\\''' send".to_string() + ), + ( + format!("near delete-key bob.testnet ed25519:DReZmNmnGhpsYcCFFeYgPsJ9YCm9xH16GGujCPe3KQEq --{} testnet", NETWORK_ID_ALIASES[0]), + "account delete-keys bob.testnet public-keys ed25519:DReZmNmnGhpsYcCFFeYgPsJ9YCm9xH16GGujCPe3KQEq network-config testnet sign-with-keychain send".to_string() + ), + ( + format!("near delete-key bob.testnet ed25519:DReZmNmnGhpsYcCFFeYgPsJ9YCm9xH16GGujCPe3KQEq --{} mainnet", NETWORK_ID_ALIASES[1]), + "account delete-keys bob.testnet public-keys ed25519:DReZmNmnGhpsYcCFFeYgPsJ9YCm9xH16GGujCPe3KQEq network-config mainnet sign-with-keychain send".to_string() + ), + ] { + let input_cmd = shell_words::split(&input).expect("Input command must be a valid shell command"); + let JsCmd::DeleteKey(delete_key_args) = JsCmd::parse_from(&input_cmd) else { + panic!("DeleteKey command was expected, but something else was parsed out from {input}"); + }; + assert_eq!( + shell_words::join(DeleteKeyArgs::to_cli_args(&delete_key_args, "testnet".to_string())), + expected_output + ); + } + } +} diff --git a/src/js_command_match/keys/list.rs b/src/js_command_match/keys/list.rs new file mode 100644 index 000000000..8104186b6 --- /dev/null +++ b/src/js_command_match/keys/list.rs @@ -0,0 +1,73 @@ +use crate::js_command_match::constants::NETWORK_ID_ALIASES; + +#[derive(Debug, Clone, clap::Parser)] +/// This is a legacy `keys` command. Once you run it with the specified arguments, new syntax command will be suggested. +pub struct KeysArgs { + account_id: String, + #[clap(long, aliases = NETWORK_ID_ALIASES)] + network_id: Option, +} + +impl KeysArgs { + pub fn to_cli_args(&self, network_config: String) -> Vec { + let network_id = self.network_id.clone().unwrap_or(network_config); + + let command = vec![ + "account".to_string(), + "list-keys".to_string(), + self.account_id.to_owned(), + "network-config".to_string(), + network_id, + "now".to_string(), + ]; + + command + } +} + +#[cfg(test)] +mod tests { + use super::super::super::JsCmd; + use super::*; + use clap::Parser; + + #[test] + fn list_keys() { + for (input, expected_output) in [ + ( + "near keys bob.testnet".to_string(), + "account list-keys bob.testnet network-config testnet now".to_string(), + ), + ( + "near list-keys bob.testnet".to_string(), + "account list-keys bob.testnet network-config testnet now".to_string(), + ), + ( + format!( + "near list-keys bob.testnet --{} testnet", + NETWORK_ID_ALIASES[0] + ), + "account list-keys bob.testnet network-config testnet now".to_string(), + ), + ( + format!( + "near list-keys bob.testnet --{} mainnet", + NETWORK_ID_ALIASES[1] + ), + "account list-keys bob.testnet network-config mainnet now".to_string(), + ), + ] { + let input_cmd = + shell_words::split(&input).expect("Input command must be a valid shell command"); + let JsCmd::ListKeys(keys_args) = JsCmd::parse_from(&input_cmd) else { + panic!( + "ListKeys command was expected, but something else was parsed out from {input}" + ); + }; + assert_eq!( + shell_words::join(KeysArgs::to_cli_args(&keys_args, "testnet".to_string())), + expected_output + ); + } + } +} diff --git a/src/js_command_match/keys/mod.rs b/src/js_command_match/keys/mod.rs new file mode 100644 index 000000000..133ca786a --- /dev/null +++ b/src/js_command_match/keys/mod.rs @@ -0,0 +1,3 @@ +pub mod add; +pub mod delete; +pub mod list; diff --git a/src/js_command_match/login.rs b/src/js_command_match/login.rs deleted file mode 100644 index 4e26f4180..000000000 --- a/src/js_command_match/login.rs +++ /dev/null @@ -1,20 +0,0 @@ -#[derive(Debug, Clone, clap::Parser)] -/// This is a legacy `legacy` command. Once you run it with the specified arguments, new syntax command will be suggested. -pub struct LoginArgs { - #[clap(long, aliases = ["wallet_url", "walletUrl"], default_value = "https://wallet.testnet.near.org")] - wallet_url: String, - #[clap(allow_hyphen_values = true, num_args = 0..)] - _unknown_args: Vec, -} - -impl LoginArgs { - pub fn to_cli_args(&self, network_config: String) -> Vec { - vec![ - "account".to_owned(), - "import-account".to_owned(), - "using-web-wallet".to_owned(), - "network-config".to_owned(), - network_config, - ] - } -} diff --git a/src/js_command_match/mod.rs b/src/js_command_match/mod.rs index 21c6b5464..528fa00bc 100644 --- a/src/js_command_match/mod.rs +++ b/src/js_command_match/mod.rs @@ -1,107 +1,70 @@ -mod add_key; -mod call; -mod clean; -mod create_account; -mod delete; -mod delete_key; -mod deploy; -mod dev_deploy; -mod evm_call; -mod evm_dev_init; -mod evm_view; -mod generate_key; -mod js; +mod constants; + +mod account; +mod contract; mod keys; -mod login; -mod proposals; -mod repl; -mod send; -mod set_api_key; -mod stake; -mod state; -mod tx_status; +mod transactions; mod validators; -mod view; -mod view_state; #[derive(Debug, Clone, clap::Parser)] /// Legacy CLI commands are only supported at best-effort pub enum JsCmd { - CreateAccount(self::create_account::CreateAccountArgs), - State(self::state::StateArgs), - Delete(self::delete::DeleteArgs), - Keys(self::keys::KeysArgs), - TxStatus(self::tx_status::TxStatusArgs), - Deploy(self::deploy::DeployArgs), - DevDeploy(self::dev_deploy::DevDeployArgs), - Call(self::call::CallArgs), - View(self::view::ViewArgs), - ViewState(self::view_state::ViewStateArgs), - Send(self::send::SendArgs), - Clean(self::clean::CleanArgs), - Stake(self::stake::StakeArgs), - Login(self::login::LoginArgs), - Repl(self::repl::ReplArgs), - GenerateKey(self::generate_key::GenerateKeyArgs), - AddKey(self::add_key::AddKeyArgs), - DeleteKey(self::delete_key::DeleteKeyArgs), + #[clap(alias("create"))] + CreateAccount(self::account::create::CreateAccountArgs), + #[clap(alias("delete"))] + DeleteAccount(self::account::delete::DeleteAccountArgs), + #[clap(alias("import-account"))] + Login(self::account::login::LoginArgs), + State(self::account::state::StateArgs), + + Call(self::contract::call::CallArgs), + Deploy(self::contract::deploy::DeployArgs), + #[clap(alias("storage"))] + ViewState(self::contract::storage::ViewStateArgs), + View(self::contract::view::ViewArgs), + + AddKey(self::keys::add::AddKeyArgs), + DeleteKey(self::keys::delete::DeleteKeyArgs), + #[clap(alias("keys"))] + ListKeys(self::keys::list::KeysArgs), + + #[clap(alias("send-near"))] + Send(self::transactions::send::SendArgs), + TxStatus(self::transactions::status::TxStatusArgs), + + #[clap(alias("validator-stake"))] + Stake(self::validators::StakeArgs), Validators(self::validators::ValidatorsArgs), - Proposals(self::proposals::ProposalsArgs), - EvmCall(self::evm_call::EvmCallArgs), - EvmDevInit(self::evm_dev_init::EvmDevInitArgs), - EvmView(self::evm_view::EvmViewArgs), - SetApiKey(self::set_api_key::SetApiKeyArgs), - Js(self::js::JsArgs), } impl JsCmd { - pub fn rust_command_generation( - &self, - ) -> color_eyre::eyre::Result<(Vec, String), String> { - //NEAR_ENV=testnet default - let network_config = std::env::var("NEAR_ENV").unwrap_or_else(|_| "testnet".to_owned()); + pub fn rust_command_generation(&self) -> Result<(Vec, String), String> { + let network = std::env::var("NEAR_NETWORK") + .or_else(|_| std::env::var("NEAR_ENV")) + .unwrap_or_else(|_| "testnet".to_owned()); let message = "The command you tried to run is deprecated in the new NEAR CLI, but we tried our best to match the old command with the new syntax, try it instead:".to_string(); - let near_validator_extension_message = "The command you tried to run has been moved into its own CLI extension called near-validator.\nPlease, follow the installation instructions here: https://github.com/near-cli-rs/near-validator-cli-rs/blob/master/README.md\nThen run the following command:".to_string(); - let err_message = "The command you tried to run is deprecated in the new NEAR CLI and there is no equivalent command in the new NEAR CLI.".to_string(); + let validator_extension_message = "The command you tried to run has been moved into its own CLI extension called near-validator.\nPlease, follow the installation instructions here: https://github.com/near-cli-rs/near-validator-cli-rs/blob/master/README.md".to_string(); + match self { - Self::CreateAccount(create_account_args) => Ok((create_account_args.to_cli_args(network_config), message)), - Self::State(state_args) => Ok((state_args.to_cli_args(network_config), message)), - Self::Delete(delete_args) => Ok((delete_args.to_cli_args(network_config), message)), - Self::Keys(keys_args) => Ok((keys_args.to_cli_args(network_config), message)), - Self::TxStatus(tx_status_args) => Ok((tx_status_args.to_cli_args(network_config), message)), - Self::Deploy(deploy_args) => Ok((deploy_args.to_cli_args(network_config), message)), - Self::DevDeploy(dev_deploy_args) => { - dev_deploy_args.to_cli_args(network_config); - Err("".to_string()) - }, - Self::Call(call_args) => Ok((call_args.to_cli_args(network_config), message)), - Self::View(view_args) => Ok((view_args.to_cli_args(network_config), message)), - Self::ViewState(view_state_args) => Ok((view_state_args.to_cli_args(network_config), message)), - Self::Send(send_args) => Ok((send_args.to_cli_args(network_config), message)), - Self::Clean(_) => Err(format!("{err_message}\n\n`clean` command is not implemented, yet. It will be implemented in a dev extension. Meanwhile, keep using the old CLI.")), - Self::Stake(stake_args) => Ok((stake_args.to_cli_args(network_config), near_validator_extension_message)), - Self::Login(login_args) => Ok((login_args.to_cli_args(network_config), message)), - Self::Repl(_) => Err(format!("{err_message}\n\n`repl` command is not implemented. Use shell scripting for the new CLI.")), - Self::GenerateKey(generate_key_args) => { - match generate_key_args.to_cli_args(network_config){ - Ok(res) => Ok((res, message)), - Err(err) => Err(err.to_string()) - } - }, - Self::AddKey(add_key_args) => Ok((add_key_args.to_cli_args(network_config), message)), - Self::DeleteKey(delete_key_args) => Ok((delete_key_args.to_cli_args(network_config), message)), - Self::Validators(validators_args) => Ok((validators_args.to_cli_args(network_config), near_validator_extension_message)), - Self::Proposals(proposals_args) => Ok((proposals_args.to_cli_args(network_config), near_validator_extension_message)), - Self::EvmCall(_) => Err(format!("{err_message}\n\n`evm-call` command is not implemented, yet. It will be implemented in an evm extension. Meanwhile, keep using the old CLI.")), - Self::EvmDevInit(_) => Err(format!("{err_message}\n\n`evm-dev-init` command is not implemented, yet. It will be implemented in an evm extension. Meanwhile, keep using the old CLI.")), - Self::EvmView(_) => Err(format!("{err_message}\n\n`evm-view` command is not implemented, yet. It will be implemented in an evm extension. Meanwhile, keep using the old CLI.")), - Self::SetApiKey(set_api_key_args) => { - match set_api_key_args.to_cli_args(network_config){ - Ok(res) => Ok((res, message)), - Err(err) => Err(err.to_string()) - } - }, - Self::Js(_) => Err(format!("{err_message}\n\n`js` command is not implemented. Use shell scripting for the new CLI.")), + Self::CreateAccount(args) => Ok((args.to_cli_args(network), message)), + Self::DeleteAccount(args) => Ok((args.to_cli_args(network), message)), + Self::Login(args) => Ok((args.to_cli_args(network), message)), + Self::State(args) => Ok((args.to_cli_args(network), message)), + + Self::Call(args) => Ok((args.to_cli_args(network), message)), + Self::Deploy(args) => Ok((args.to_cli_args(network), message)), + Self::ViewState(args) => Ok((args.to_cli_args(network), message)), + Self::View(args) => Ok((args.to_cli_args(network), message)), + + Self::AddKey(args) => Ok((args.to_cli_args(network), message)), + Self::DeleteKey(args) => Ok((args.to_cli_args(network), message)), + Self::ListKeys(args) => Ok((args.to_cli_args(network), message)), + + Self::Send(args) => Ok((args.to_cli_args(network), message)), + Self::TxStatus(args) => Ok((args.to_cli_args(network), message)), + + Self::Stake(_args) => Ok((vec![], validator_extension_message)), + Self::Validators(_args) => Ok((vec![], validator_extension_message)), } } } diff --git a/src/js_command_match/proposals.rs b/src/js_command_match/proposals.rs deleted file mode 100644 index 156154389..000000000 --- a/src/js_command_match/proposals.rs +++ /dev/null @@ -1,17 +0,0 @@ -#[derive(Debug, Clone, clap::Parser)] -/// This is a legacy `proposals` command. Once you run it with the specified arguments, new syntax command will be suggested. -pub struct ProposalsArgs { - #[clap(allow_hyphen_values = true, num_args = 0..)] - _unknown_args: Vec, -} - -impl ProposalsArgs { - pub fn to_cli_args(&self, network_config: String) -> Vec { - vec![ - "validator".to_owned(), - "proposals".to_owned(), - "network-config".to_owned(), - network_config, - ] - } -} diff --git a/src/js_command_match/repl.rs b/src/js_command_match/repl.rs deleted file mode 100644 index 2a9c68278..000000000 --- a/src/js_command_match/repl.rs +++ /dev/null @@ -1,10 +0,0 @@ -#[derive(Debug, Clone, clap::Parser)] -/// This is a legacy `repl` command. Once you run it with the specified arguments, new syntax command will be suggested. -pub struct ReplArgs { - #[clap(long, aliases = ["account_id", "accountId"])] - account_id: Option, - #[clap(long, short)] - script: Option, - #[clap(allow_hyphen_values = true, num_args = 0..)] - _unknown_args: Vec, -} diff --git a/src/js_command_match/send.rs b/src/js_command_match/send.rs deleted file mode 100644 index e3c8e59fe..000000000 --- a/src/js_command_match/send.rs +++ /dev/null @@ -1,25 +0,0 @@ -#[derive(Debug, Clone, clap::Parser)] -/// This is a legacy `send` command. Once you run it with the specified arguments, new syntax command will be suggested. -pub struct SendArgs { - sender_account_id: String, - receiver_account_id: String, - amount: String, - #[clap(allow_hyphen_values = true, num_args = 0..)] - _unknown_args: Vec, -} - -impl SendArgs { - pub fn to_cli_args(&self, network_config: String) -> Vec { - vec![ - "tokens".to_owned(), - self.sender_account_id.to_owned(), - "send-near".to_owned(), - self.receiver_account_id.to_owned(), - format!("{} NEAR", self.amount), - "network-config".to_owned(), - network_config, - "sign-with-keychain".to_owned(), - "send".to_owned(), - ] - } -} diff --git a/src/js_command_match/set_api_key.rs b/src/js_command_match/set_api_key.rs deleted file mode 100644 index 09c81349f..000000000 --- a/src/js_command_match/set_api_key.rs +++ /dev/null @@ -1,53 +0,0 @@ -#[derive(Debug, Clone, clap::Parser)] -/// This is a legacy `set-api-key` command. Once you run it with the specified arguments, new syntax command will be suggested. -pub struct SetApiKeyArgs { - rpc_server: String, - x_api_key: String, - #[clap(allow_hyphen_values = true, num_args = 0..)] - _unknown_args: Vec, -} - -impl SetApiKeyArgs { - pub fn to_cli_args(&self, network_name: String) -> color_eyre::eyre::Result> { - let config = crate::config::Config::get_config_toml()?; - let network_config = match config.network_connection.get(&network_name) { - Some(network_config) => network_config, - None => { - return Ok(vec![ - "config".to_owned(), - "add-connection".to_owned(), - "--network-name".to_owned(), - network_name.to_owned(), - "--connection-name".to_owned(), - network_name, - "--rpc-url".to_owned(), - self.rpc_server.to_owned(), - "--rpc-api-key".to_owned(), - self.x_api_key.to_owned(), - ]) - } - } - .clone(); - let mut args = vec![ - "config".to_owned(), - "add-connection".to_owned(), - "--network-name".to_owned(), - network_name.to_owned(), - "--connection-name".to_owned(), - network_name, - "--rpc-url".to_owned(), - self.rpc_server.to_owned(), - "--wallet-url".to_owned(), - network_config.wallet_url.to_string(), - "--explorer-transaction-url".to_owned(), - network_config.explorer_transaction_url.to_string(), - "--rpc-api-key".to_owned(), - self.x_api_key.to_owned(), - ]; - if let Some(linkdrop_account_id) = network_config.linkdrop_account_id { - args.push("--linkdrop-account-id".to_owned()); - args.push(linkdrop_account_id.to_string()) - } - Ok(args) - } -} diff --git a/src/js_command_match/stake.rs b/src/js_command_match/stake.rs deleted file mode 100644 index fe40e6baa..000000000 --- a/src/js_command_match/stake.rs +++ /dev/null @@ -1,26 +0,0 @@ -#[derive(Debug, Clone, clap::Parser)] -/// This is a legacy `stake` command. Once you run it with the specified arguments, new syntax command will be suggested. -pub struct StakeArgs { - account_id: String, - staking_key: String, - amount: String, - #[clap(allow_hyphen_values = true, num_args = 0..)] - _unknown_args: Vec, -} - -impl StakeArgs { - pub fn to_cli_args(&self, network_config: String) -> Vec { - vec![ - "validator".to_owned(), - "staking".to_owned(), - "stake-proposal".to_owned(), - self.account_id.to_owned(), - self.staking_key.to_owned(), - format!("{} NEAR", self.amount), - "network-config".to_owned(), - network_config, - "sign-with-keychain".to_owned(), - "send".to_owned(), - ] - } -} diff --git a/src/js_command_match/state.rs b/src/js_command_match/state.rs deleted file mode 100644 index a4a212613..000000000 --- a/src/js_command_match/state.rs +++ /dev/null @@ -1,20 +0,0 @@ -#[derive(Debug, Clone, clap::Parser)] -/// This is a legacy `stake` command. Once you run it with the specified arguments, new syntax command will be suggested. -pub struct StateArgs { - account_id: String, - #[clap(allow_hyphen_values = true, num_args = 0..)] - _unknown_args: Vec, -} - -impl StateArgs { - pub fn to_cli_args(&self, network_config: String) -> Vec { - vec![ - "account".to_owned(), - "view-account-summary".to_owned(), - self.account_id.to_owned(), - "network-config".to_owned(), - network_config, - "now".to_owned(), - ] - } -} diff --git a/src/js_command_match/transactions/mod.rs b/src/js_command_match/transactions/mod.rs new file mode 100644 index 000000000..9a30bf058 --- /dev/null +++ b/src/js_command_match/transactions/mod.rs @@ -0,0 +1,2 @@ +pub mod send; +pub mod status; diff --git a/src/js_command_match/transactions/send.rs b/src/js_command_match/transactions/send.rs new file mode 100644 index 000000000..559fb6df3 --- /dev/null +++ b/src/js_command_match/transactions/send.rs @@ -0,0 +1,101 @@ +use crate::js_command_match::constants::{ + DEFAULT_SEED_PHRASE_PATH, LEDGER_PATH_ALIASES, NETWORK_ID_ALIASES, SIGN_WITH_LEDGER_ALIASES, +}; + +#[derive(Debug, Clone, clap::Parser)] +pub struct SendArgs { + pub sender: String, + pub receiver: String, + pub amount: String, + #[clap(long, aliases = SIGN_WITH_LEDGER_ALIASES, default_value_t = false)] + sign_with_ledger: bool, + #[clap(long, aliases = LEDGER_PATH_ALIASES, default_value = DEFAULT_SEED_PHRASE_PATH)] + ledger_path: String, + #[clap(long, aliases = NETWORK_ID_ALIASES)] + pub network_id: Option, +} + +impl SendArgs { + pub fn to_cli_args(&self, network_config: String) -> Vec { + let network_id = self.network_id.clone().unwrap_or(network_config); + + let mut command = vec![ + "tokens".to_string(), + self.sender.to_owned(), + "send-near".to_string(), + self.receiver.to_owned(), + format!("{} NEAR", self.amount), + "network-config".to_string(), + network_id, + ]; + + if self.sign_with_ledger { + command.push("sign-with-ledger".to_string()); + command.push("--seed-phrase-hd-path".to_string()); + command.push(self.ledger_path.to_owned()); + } else { + command.push("sign-with-keychain".to_string()); + } + + command.push("send".to_string()); + command + } +} + +#[cfg(test)] +mod tests { + use super::super::super::JsCmd; + use super::*; + use clap::Parser; + + #[test] + fn send() { + for (input, expected_output) in [ + ( + format!("near send bob.testnet alice.testnet 1 --{}", SIGN_WITH_LEDGER_ALIASES[0]), + "tokens bob.testnet send-near alice.testnet '1 NEAR' network-config testnet sign-with-ledger --seed-phrase-hd-path '44'\\''/397'\\''/0'\\''/0'\\''/1'\\''' send" + ), + ( + format!("near send-near bob.testnet alice.testnet 1 --{}", SIGN_WITH_LEDGER_ALIASES[0]), + "tokens bob.testnet send-near alice.testnet '1 NEAR' network-config testnet sign-with-ledger --seed-phrase-hd-path '44'\\''/397'\\''/0'\\''/0'\\''/1'\\''' send" + ), + ( + format!("near send-near bob.testnet alice.testnet 1 --{}", SIGN_WITH_LEDGER_ALIASES[1]), + "tokens bob.testnet send-near alice.testnet '1 NEAR' network-config testnet sign-with-ledger --seed-phrase-hd-path '44'\\''/397'\\''/0'\\''/0'\\''/1'\\''' send" + ), + ( + format!("near send-near bob.testnet alice.testnet 1 --{}", SIGN_WITH_LEDGER_ALIASES[2]), + "tokens bob.testnet send-near alice.testnet '1 NEAR' network-config testnet sign-with-ledger --seed-phrase-hd-path '44'\\''/397'\\''/0'\\''/0'\\''/1'\\''' send" + ), + ( + format!("near send-near bob.testnet alice.testnet 1 --{}", SIGN_WITH_LEDGER_ALIASES[3]), + "tokens bob.testnet send-near alice.testnet '1 NEAR' network-config testnet sign-with-ledger --seed-phrase-hd-path '44'\\''/397'\\''/0'\\''/0'\\''/1'\\''' send" + ), + ( + format!("near send-near bob.testnet alice.testnet 1 --signWithLedger --{} \"44'/397'/0'/0'/2'\"", LEDGER_PATH_ALIASES[0]), + "tokens bob.testnet send-near alice.testnet '1 NEAR' network-config testnet sign-with-ledger --seed-phrase-hd-path '44'\\''/397'\\''/0'\\''/0'\\''/2'\\''' send" + ), + ( + format!("near send-near bob.testnet alice.testnet 1 --signWithLedger --{} \"44'/397'/0'/0'/2'\"", LEDGER_PATH_ALIASES[1]), + "tokens bob.testnet send-near alice.testnet '1 NEAR' network-config testnet sign-with-ledger --seed-phrase-hd-path '44'\\''/397'\\''/0'\\''/0'\\''/2'\\''' send" + ), + ( + format!("near send-near bob.testnet alice.testnet 1 --{} testnet", NETWORK_ID_ALIASES[0]), + "tokens bob.testnet send-near alice.testnet '1 NEAR' network-config testnet sign-with-keychain send" + ), + ( + format!("near send-near bob.testnet alice.testnet 1 --{} mainnet", NETWORK_ID_ALIASES[1]), + "tokens bob.testnet send-near alice.testnet '1 NEAR' network-config mainnet sign-with-keychain send" + ), + ] { + let input_cmd = shell_words::split(&input).expect("Input command must be a valid shell command"); + let JsCmd::Send(send_args) = JsCmd::parse_from(&input_cmd) else { + panic!("Send command was expected, but something else was parsed out from {input}"); + }; + assert_eq!( + shell_words::join(SendArgs::to_cli_args(&send_args, "testnet".to_string())), + expected_output + ); + } + } +} diff --git a/src/js_command_match/transactions/status.rs b/src/js_command_match/transactions/status.rs new file mode 100644 index 000000000..a96d2be2f --- /dev/null +++ b/src/js_command_match/transactions/status.rs @@ -0,0 +1,60 @@ +use crate::js_command_match::constants::NETWORK_ID_ALIASES; + +#[derive(Debug, Clone, clap::Parser)] +pub struct TxStatusArgs { + hash: String, + #[clap(long, aliases = NETWORK_ID_ALIASES)] + network_id: Option, + #[clap(allow_hyphen_values = true, num_args = 0..)] + _unknown_args: Vec, +} + +impl TxStatusArgs { + pub fn to_cli_args(&self, network_config: String) -> Vec { + let network_id = self.network_id.clone().unwrap_or(network_config); + + let command = vec![ + "transaction".to_string(), + "view-status".to_string(), + self.hash.to_owned(), + "network-config".to_string(), + network_id, + ]; + + command + } +} + +#[cfg(test)] +mod tests { + use super::super::super::JsCmd; + use super::*; + use clap::Parser; + + #[test] + fn tx_status() { + for (input, expected_output) in [ + ( + "near tx-status 4HxfV69Brk7fJd3NC63ti2H3QCgwiUiMAPvwNmGWbVXo".to_string(), + "transaction view-status 4HxfV69Brk7fJd3NC63ti2H3QCgwiUiMAPvwNmGWbVXo network-config testnet" + ), + ( + format!("near tx-status 4HxfV69Brk7fJd3NC63ti2H3QCgwiUiMAPvwNmGWbVXo --{} testnet", NETWORK_ID_ALIASES[0]), + "transaction view-status 4HxfV69Brk7fJd3NC63ti2H3QCgwiUiMAPvwNmGWbVXo network-config testnet" + ), + ( + format!("near tx-status 4HxfV69Brk7fJd3NC63ti2H3QCgwiUiMAPvwNmGWbVXo --{} mainnet", NETWORK_ID_ALIASES[1]), + "transaction view-status 4HxfV69Brk7fJd3NC63ti2H3QCgwiUiMAPvwNmGWbVXo network-config mainnet" + ), + ] { + let input_cmd = shell_words::split(&input).expect("Input command must be a valid shell command"); + let JsCmd::TxStatus(tx_status_args) = JsCmd::parse_from(&input_cmd) else { + panic!("TxStatus command was expected, but something else was parsed out from {input}"); + }; + assert_eq!( + shell_words::join(TxStatusArgs::to_cli_args(&tx_status_args, "testnet".to_string())), + expected_output + ); + } + } +} diff --git a/src/js_command_match/tx_status.rs b/src/js_command_match/tx_status.rs deleted file mode 100644 index 569039661..000000000 --- a/src/js_command_match/tx_status.rs +++ /dev/null @@ -1,21 +0,0 @@ -#[derive(Debug, Clone, clap::Parser)] -/// This is a legacy `tx-status` command. Once you run it with the specified arguments, new syntax command will be suggested. -pub struct TxStatusArgs { - transaction_hash: String, - #[clap(long, aliases = ["account_id", "accountId"], default_value = "near")] - account_id: String, - #[clap(allow_hyphen_values = true, num_args = 0..)] - _unknown_args: Vec, -} - -impl TxStatusArgs { - pub fn to_cli_args(&self, network_config: String) -> Vec { - vec![ - "transaction".to_owned(), - "view-status".to_owned(), - self.transaction_hash.to_owned(), - "network-config".to_owned(), - network_config, - ] - } -} diff --git a/src/js_command_match/validators.rs b/src/js_command_match/validators.rs index 54a3f0edf..eb4c9fa8d 100644 --- a/src/js_command_match/validators.rs +++ b/src/js_command_match/validators.rs @@ -1,24 +1,12 @@ #[derive(Debug, Clone, clap::Parser)] /// This is a legacy `validators` command. Once you run it with the specified arguments, new syntax command will be suggested. pub struct ValidatorsArgs { - epoch: String, #[clap(allow_hyphen_values = true, num_args = 0..)] _unknown_args: Vec, } -impl ValidatorsArgs { - pub fn to_cli_args(&self, network_config: String) -> Vec { - let mut output_vec = vec![ - "validator".to_owned(), - "validators".to_owned(), - "network-config".to_owned(), - network_config, - ]; - if "current" == &self.epoch { - output_vec.push("now".to_owned()) - } else if "next" == &self.epoch { - output_vec.push("next".to_owned()) - }; - output_vec - } +#[derive(Debug, Clone, clap::Parser)] +pub struct StakeArgs { + #[clap(allow_hyphen_values = true, num_args = 0..)] + _unknown_args: Vec, } diff --git a/src/js_command_match/view.rs b/src/js_command_match/view.rs deleted file mode 100644 index aff0df35b..000000000 --- a/src/js_command_match/view.rs +++ /dev/null @@ -1,27 +0,0 @@ -#[derive(Debug, Clone, clap::Parser)] -/// This is a legacy `view` command. Once you run it with the specified arguments, new syntax command will be suggested. -pub struct ViewArgs { - contract_account_id: String, - method_name: String, - #[clap(default_value = "")] - args: String, - #[clap(allow_hyphen_values = true, num_args = 0..)] - _unknown_args: Vec, -} - -impl ViewArgs { - pub fn to_cli_args(&self, network_config: String) -> Vec { - vec![ - "contract".to_owned(), - "call-function".to_owned(), - "as-read-only".to_owned(), - self.contract_account_id.to_owned(), - self.method_name.to_owned(), - "text-args".to_owned(), - self.args.to_owned(), - "network-config".to_owned(), - network_config, - "now".to_owned(), - ] - } -} diff --git a/src/js_command_match/view_state.rs b/src/js_command_match/view_state.rs deleted file mode 100644 index 4a4540b95..000000000 --- a/src/js_command_match/view_state.rs +++ /dev/null @@ -1,51 +0,0 @@ -#[derive(Debug, Clone, clap::Parser)] -/// This is a legacy `view-state` command. Once you run it with the specified arguments, new syntax command will be suggested. -pub struct ViewStateArgs { - contract_account_id: String, - #[clap(long, default_value = None, conflicts_with = "block_id")] - finality: Option, - #[clap(long, aliases = ["block_id", "blockId"], default_value = None)] - block_id: Option, - #[clap(long, default_value = None)] - prefix: Option, - #[clap(long, default_value_t = false)] - utf8: bool, - #[clap(allow_hyphen_values = true, num_args = 0..)] - _unknown_args: Vec, -} - -impl ViewStateArgs { - pub fn to_cli_args(&self, network_config: String) -> Vec { - if self.prefix.is_none() { - let output_format = if self.utf8 { "as-text" } else { "as-json" }; - if self.finality.is_some() { - vec![ - "contract".to_owned(), - "view-storage".to_owned(), - self.contract_account_id.to_owned(), - "all".to_owned(), - output_format.to_owned(), - "network-config".to_owned(), - network_config, - "now".to_owned(), - ] - } else { - vec![ - "contract".to_owned(), - "view-storage".to_owned(), - self.contract_account_id.to_owned(), - "all".to_owned(), - output_format.to_owned(), - "network-config".to_owned(), - network_config, - ] - } - } else { - vec![ - "contract".to_owned(), - "view-storage".to_owned(), - self.contract_account_id.to_owned(), - ] - } - } -} diff --git a/src/main.rs b/src/main.rs index 504848730..7fc19eee8 100644 --- a/src/main.rs +++ b/src/main.rs @@ -128,7 +128,18 @@ fn main() -> crate::common::CliResult { } Err(error) => { if let clap::error::ErrorKind::DisplayHelp = error.kind() { - error.exit() + error.exit(); + } + if let Some(cmd) = std::env::args().nth(1) { + match cmd.as_str() { + "add-credentials" | "add-key" | "call" | "create" + | "create-account" | "delete" | "delete-account" | "delete-key" + | "deploy" | "generate-key" | "import-account" | "keys" + | "list-keys" | "login" | "send" | "send-near" | "stake" + | "state" | "storage" | "tx-status" | "validator-stake" + | "validators" | "view" | "view-storage" => error.exit(), + _ => {} + } } } },