diff --git a/crates/rooch/src/commands/transaction/commands/sign.rs b/crates/rooch/src/commands/transaction/commands/sign.rs index 0f8c5e1216..a5b7bb0b88 100644 --- a/crates/rooch/src/commands/transaction/commands/sign.rs +++ b/crates/rooch/src/commands/transaction/commands/sign.rs @@ -3,6 +3,7 @@ use super::{FileOutput, FileOutputData}; use crate::cli_types::{CommandAction, FileOrHexInput, WalletContextOptions}; +use crate::utils::prompt_yes_no; use async_trait::async_trait; use moveos_types::module_binding::MoveFunctionCaller; use rooch_key::keystore::account_keystore::AccountKeystore; @@ -50,7 +51,6 @@ impl SignInput { } } } - pub enum SignOutput { SignedRoochTransaction(RoochTransaction), PartiallySignedRoochTransaction(PartiallySignedRoochTransaction), @@ -91,6 +91,10 @@ pub struct SignCommand { #[clap(long, short = 'o')] output: Option, + /// Automatically answer 'yes' to all prompts + #[clap(long = "yes", short = 'y')] + answer_yes: bool, + /// Return command outputs in json format #[clap(long, default_value = "false")] json: bool, @@ -175,11 +179,47 @@ impl SignCommand { }; Ok(output) } + + fn print_tx_details(input: &SignInput) { + let tx_data = |tx_data: &RoochTransactionData| -> String { + format!( + " Sender: {}\n Sequence number: {}\n Chain id: {}\n Max gas amount: {}\n Action: {}\n Transaction hash: {}\n", + tx_data.sender, + tx_data.sequence_number, + tx_data.chain_id, + tx_data.max_gas_amount, + tx_data.action, + tx_data.tx_hash() + ) + }; + + match input { + SignInput::RoochTransactionData(tx) => { + println!("Transaction data:\n{}", tx_data(tx)); + } + SignInput::PartiallySignedRoochTransaction(pstx) => { + println!( + "Partially signed transaction data:\n{}", + tx_data(&pstx.data) + ); + println!( + " Collected signatures: {}/{}", + pstx.authenticators.len(), + pstx.threshold + ); + } + } + } } #[async_trait] impl CommandAction> for SignCommand { async fn execute(self) -> RoochResult> { + let sign_input = SignInput::try_from(self.input.clone())?; + SignCommand::print_tx_details(&sign_input); + if !self.answer_yes && !prompt_yes_no("Do you want to sign this transaction?") { + return Ok(None); + } let json = self.json; let output = self.output.clone(); let sign_output = self.sign().await?; diff --git a/crates/rooch/src/utils.rs b/crates/rooch/src/utils.rs index a1b6deb952..6a3ba8b115 100644 --- a/crates/rooch/src/utils.rs +++ b/crates/rooch/src/utils.rs @@ -64,3 +64,20 @@ pub fn read_line() -> Result { io::stdin().read_line(&mut s)?; Ok(s.trim_end().to_string()) } + +pub fn prompt_yes_no(question: &str) -> bool { + loop { + println!("{} [yes/no] > ", question); + + let Ok(input) = read_line() else { + println!("Please answer yes or no."); + continue; + }; + + match input.trim_start().to_lowercase().as_str() { + "yes" | "y" => return true, + "no" | "n" => return false, + _ => println!("Please answer yes or no."), + } + } +} diff --git a/crates/testsuite/features/cmd.feature b/crates/testsuite/features/cmd.feature index d112b3b24a..dd0fd3002b 100644 --- a/crates/testsuite/features/cmd.feature +++ b/crates/testsuite/features/cmd.feature @@ -75,7 +75,7 @@ Feature: Rooch CLI integration tests Then cmd: "transaction get-transactions-by-hash --hashes {{$.transaction[-1].data[0].execution_info.tx_hash}}" Then cmd: "transaction build --function rooch_framework::empty::empty --json" Then assert: "'{{$.transaction[-1]}}' not_contains error" - Then cmd: "transaction sign {{$.transaction[-1].path}} --json" + Then cmd: "transaction sign {{$.transaction[-1].path}} --json -y" Then assert: "'{{$.transaction[-1]}}' not_contains error" Then cmd: "transaction submit {{$.transaction[-1].path}}" Then assert: "{{$.transaction[-1].execution_info.status.type}} == executed" diff --git a/crates/testsuite/features/multisign.feature b/crates/testsuite/features/multisign.feature index 943f0d8c09..2765a3d969 100644 --- a/crates/testsuite/features/multisign.feature +++ b/crates/testsuite/features/multisign.feature @@ -60,9 +60,9 @@ Feature: Rooch CLI multisign integration tests # l2 transaction Then cmd: "tx build --sender {{$.account[-3].multisign_address}} --function rooch_framework::empty::empty --json" Then assert: "'{{$.tx[-1]}}' not_contains error" - Then cmd: "tx sign {{$.tx[-1].path}} -s {{$.account[-3].participants[0].participant_address}} --json" + Then cmd: "tx sign {{$.tx[-1].path}} -s {{$.account[-3].participants[0].participant_address}} --json -y" Then assert: "'{{$.tx[-1]}}' not_contains error" - Then cmd: "tx sign {{$.tx[-1].path}} -s {{$.account[-3].participants[1].participant_address}} --json" + Then cmd: "tx sign {{$.tx[-1].path}} -s {{$.account[-3].participants[1].participant_address}} --json -y" Then assert: "'{{$.tx[-1]}}' not_contains error" Then cmd: "tx submit {{$.tx[-1].path}} --json" Then assert: "{{$.tx[-1].execution_info.status.type}} == executed" diff --git a/moveos/moveos-types/src/transaction.rs b/moveos/moveos-types/src/transaction.rs index 705a5dc52d..80eb9e9151 100644 --- a/moveos/moveos-types/src/transaction.rs +++ b/moveos/moveos-types/src/transaction.rs @@ -219,7 +219,7 @@ impl Display for MoveAction { } write!( f, - "MoveAction::FunctionCall( function_id: 0x{:?}, type_args: {:?}, args: {:?})", + "MoveAction::FunctionCall( function_id: {}, type_args: {:?}, args: {:?})", function.function_id, function.ty_args, arg_list ) }