diff --git a/src/commands/transaction/mod.rs b/src/commands/transaction/mod.rs index 0407e6767..a2cb5e1a0 100644 --- a/src/commands/transaction/mod.rs +++ b/src/commands/transaction/mod.rs @@ -4,7 +4,7 @@ use strum::{EnumDiscriminants, EnumIter, EnumMessage}; pub mod construct_transaction; mod print_transaction; mod reconstruct_transaction; -mod send_meta_transaction; +pub mod send_meta_transaction; pub mod send_signed_transaction; pub mod sign_transaction; mod view_status; @@ -56,5 +56,5 @@ pub enum TransactionActions { message = "send-meta-transaction - Act as a relayer to send a signed delegate action (meta-transaction)" ))] /// Act as a relayer to send a signed delegate action (meta-transaction) - SendMetaTransaction(self::send_meta_transaction::SendMetaTransaction), + SendMetaTransaction(self::send_meta_transaction::SignedMetaTransaction), } diff --git a/src/commands/transaction/send_meta_transaction/mod.rs b/src/commands/transaction/send_meta_transaction/mod.rs index 0fca40391..7bbf0fbe9 100644 --- a/src/commands/transaction/send_meta_transaction/mod.rs +++ b/src/commands/transaction/send_meta_transaction/mod.rs @@ -1,9 +1,43 @@ +use color_eyre::eyre::WrapErr; +use strum::{EnumDiscriminants, EnumIter, EnumMessage}; + mod sign_as; +#[derive(Debug, Clone, interactive_clap::InteractiveClap)] +#[interactive_clap(context = crate::GlobalContext)] +pub struct SignedMetaTransaction { + #[interactive_clap(subcommand)] + /// Select the base64 signed meta-transaction input method + signed_meta_transaction_type: SignedMetaTransactionType, +} + +#[derive(Debug, EnumDiscriminants, Clone, interactive_clap::InteractiveClap)] +#[interactive_clap(context = crate::GlobalContext)] +#[strum_discriminants(derive(EnumMessage, EnumIter))] +/// Select the Base64 signed meta-transaction input method: +pub enum SignedMetaTransactionType { + #[strum_discriminants(strum( + message = "base64-signed-meta-transaction - Base64-encoded string (e.g. e30=)" + ))] + /// Base64-encoded string (e.g. e30=) + Base64SignedMetaTransaction(Base64SignedMetaTransaction), + #[strum_discriminants(strum( + message = "file-with-base64-signed-meta-transaction - Read base64-encoded string from file (e.g. reusable JSON or binary data)" + ))] + /// Read base64-encoded string from file (e.g. reusable JSON or binary data) + FileWithBase64SignedMetaTransaction(FileWithBase64SignedMetaTransaction), +} + +#[derive(Debug, Clone)] +pub struct SignedMetaTransactionContext { + global_context: crate::GlobalContext, + signed_delegate_action: near_primitives::action::delegate::SignedDelegateAction, +} + #[derive(Debug, Clone, interactive_clap::InteractiveClap)] #[interactive_clap(input_context = crate::GlobalContext)] -#[interactive_clap(output_context = SendMetaTransactionContext)] -pub struct SendMetaTransaction { +#[interactive_clap(output_context = Base64SignedMetaTransactionContext)] +pub struct Base64SignedMetaTransaction { /// Enter a signed delegate action as base64-encoded string: signed_delegate_action: crate::types::signed_delegate_action::SignedDelegateActionAsBase64, #[interactive_clap(named_arg)] @@ -12,19 +46,67 @@ pub struct SendMetaTransaction { } #[derive(Debug, Clone)] -pub struct SendMetaTransactionContext { - global_context: crate::GlobalContext, - signed_delegate_action: near_primitives::action::delegate::SignedDelegateAction, +pub struct Base64SignedMetaTransactionContext(SignedMetaTransactionContext); + +impl Base64SignedMetaTransactionContext { + pub fn from_previous_context( + previous_context: crate::GlobalContext, + scope: &::InteractiveClapContextScope, + ) -> color_eyre::eyre::Result { + Ok(Self(SignedMetaTransactionContext { + global_context: previous_context, + signed_delegate_action: scope.signed_delegate_action.clone().into(), + })) + } } -impl SendMetaTransactionContext { +impl From for SignedMetaTransactionContext { + fn from(item: Base64SignedMetaTransactionContext) -> Self { + item.0 + } +} + +#[derive(Debug, Clone, interactive_clap::InteractiveClap)] +#[interactive_clap(input_context = crate::GlobalContext)] +#[interactive_clap(output_context = FileWithBase64SignedMetaTransactionContext)] +pub struct FileWithBase64SignedMetaTransaction { + /// Enter the path to the file with the meta-transaction as a string in base64 encoding: + file_path: crate::types::path_buf::PathBuf, + #[interactive_clap(named_arg)] + /// What is the relayer account ID? + sign_as: self::sign_as::RelayerAccountId, +} + +#[derive(Debug, Clone)] +pub struct FileWithBase64SignedMetaTransactionContext(SignedMetaTransactionContext); + +#[derive(Debug, serde::Deserialize, serde::Serialize)] +pub struct FileSignedMetaTransaction { + #[serde(rename = "signed_delegate_action_as_base64")] + pub signed_delegate_action: crate::types::signed_delegate_action::SignedDelegateActionAsBase64, +} + +impl FileWithBase64SignedMetaTransactionContext { pub fn from_previous_context( previous_context: crate::GlobalContext, - scope: &::InteractiveClapContextScope, + scope: &::InteractiveClapContextScope, ) -> color_eyre::eyre::Result { - Ok(Self { + let data = std::fs::read_to_string(&scope.file_path) + .wrap_err_with(|| format!("File {:?} not found!", &scope.file_path))?; + + let signed_delegate_action = serde_json::from_str::(&data) + .wrap_err_with(|| format!("Error reading data from file: {:?}", &scope.file_path))? + .signed_delegate_action; + + Ok(Self(SignedMetaTransactionContext { global_context: previous_context, - signed_delegate_action: scope.signed_delegate_action.inner.clone(), - }) + signed_delegate_action: signed_delegate_action.into(), + })) + } +} + +impl From for SignedMetaTransactionContext { + fn from(item: FileWithBase64SignedMetaTransactionContext) -> Self { + item.0 } } diff --git a/src/commands/transaction/send_meta_transaction/sign_as/mod.rs b/src/commands/transaction/send_meta_transaction/sign_as/mod.rs index 3a7a25544..8b16e7e45 100644 --- a/src/commands/transaction/send_meta_transaction/sign_as/mod.rs +++ b/src/commands/transaction/send_meta_transaction/sign_as/mod.rs @@ -1,7 +1,7 @@ use inquire::Select; #[derive(Debug, Clone, interactive_clap::InteractiveClap)] -#[interactive_clap(input_context = super::SendMetaTransactionContext)] +#[interactive_clap(input_context = super::SignedMetaTransactionContext)] #[interactive_clap(output_context = RelayerAccountIdContext)] pub struct RelayerAccountId { #[interactive_clap(skip_default_input_arg)] @@ -17,7 +17,7 @@ pub struct RelayerAccountIdContext(crate::commands::ActionContext); impl RelayerAccountIdContext { pub fn from_previous_context( - previous_context: super::SendMetaTransactionContext, + previous_context: super::SignedMetaTransactionContext, scope: &::InteractiveClapContextScope, ) -> color_eyre::eyre::Result { let get_prepopulated_transaction_after_getting_network_callback: crate::commands::GetPrepopulatedTransactionAfterGettingNetworkCallback = @@ -71,7 +71,7 @@ impl From for crate::commands::ActionContext { impl RelayerAccountId { fn input_relayer_account_id( - context: &super::SendMetaTransactionContext, + context: &super::SignedMetaTransactionContext, ) -> color_eyre::eyre::Result> { loop { let relayer_account_id = if let Some(account_id) = diff --git a/src/transaction_signature_options/save_to_file/mod.rs b/src/transaction_signature_options/save_to_file/mod.rs index fc24b3fa4..c3f757315 100644 --- a/src/transaction_signature_options/save_to_file/mod.rs +++ b/src/transaction_signature_options/save_to_file/mod.rs @@ -3,6 +3,7 @@ use std::io::Write; use color_eyre::eyre::Context; use inquire::CustomType; +use super::super::commands::transaction::send_meta_transaction::FileSignedMetaTransaction; use super::super::commands::transaction::send_signed_transaction::FileSignedTransaction; #[derive(Debug, Clone, interactive_clap_derive::InteractiveClap)] @@ -52,14 +53,10 @@ impl SaveToFileContext { super::SignedTransactionOrSignedDelegateAction::SignedDelegateAction( signed_delegate_action, ) => { - let signed_delegate_action_as_base64 = - crate::types::signed_delegate_action::SignedDelegateActionAsBase64::from( - signed_delegate_action, - ) - .to_string(); - - let data_signed_delegate_action = serde_json::json!( - {"signed_delegate_action_as_base64": signed_delegate_action_as_base64}); + let data_signed_delegate_action = + serde_json::to_value(&FileSignedMetaTransaction { + signed_delegate_action: signed_delegate_action.into(), + })?; std::fs::File::create(&file_path) .wrap_err_with(|| format!("Failed to create file: {:?}", &file_path))? @@ -80,13 +77,21 @@ impl SaveToFileContext { impl SaveToFile { fn input_file_path( - _context: &super::SubmitContext, + context: &super::SubmitContext, ) -> color_eyre::eyre::Result> { + let starting_input = match &context.signed_transaction_or_signed_delegate_action { + super::SignedTransactionOrSignedDelegateAction::SignedTransaction(_) => { + "signed-transaction-info.json" + } + super::SignedTransactionOrSignedDelegateAction::SignedDelegateAction(_) => { + "signed-meta-transaction-info.json" + } + }; Ok(Some( CustomType::new( "What is the location of the file to save the transaction information?", ) - .with_starting_input("signed-transaction-info.json") + .with_starting_input(starting_input) .prompt()?, )) } diff --git a/src/types/signed_delegate_action.rs b/src/types/signed_delegate_action.rs index 0db345fbd..677ab7f6a 100644 --- a/src/types/signed_delegate_action.rs +++ b/src/types/signed_delegate_action.rs @@ -2,7 +2,55 @@ use near_primitives::{borsh, borsh::BorshDeserialize}; #[derive(Debug, Clone)] pub struct SignedDelegateActionAsBase64 { - pub inner: near_primitives::action::delegate::SignedDelegateAction, + inner: near_primitives::action::delegate::SignedDelegateAction, +} + +impl serde::Serialize for SignedDelegateActionAsBase64 { + fn serialize(&self, serializer: S) -> Result + where + S: serde::Serializer, + { + let signed_delegate_action_borsh = borsh::to_vec(&self.inner).map_err(|err| { + serde::ser::Error::custom(format!( + "The value could not be borsh encoded due to: {}", + err + )) + })?; + let signed_delegate_action_as_base64 = + near_primitives::serialize::to_base64(&signed_delegate_action_borsh); + serializer.serialize_str(&signed_delegate_action_as_base64) + } +} + +impl<'de> serde::Deserialize<'de> for SignedDelegateActionAsBase64 { + fn deserialize(deserializer: D) -> Result + where + D: serde::Deserializer<'de>, + { + let signed_delegate_action_as_base64 = + ::deserialize(deserializer)?; + let signed_delegate_action_borsh = near_primitives::serialize::from_base64( + &signed_delegate_action_as_base64, + ) + .map_err(|err| { + serde::de::Error::custom(format!( + "The value could not decoded from base64 due to: {}", + err + )) + })?; + let signed_delegate_action = borsh::from_slice::< + near_primitives::action::delegate::SignedDelegateAction, + >(&signed_delegate_action_borsh) + .map_err(|err| { + serde::de::Error::custom(format!( + "The value could not decoded from borsh due to: {}", + err + )) + })?; + Ok(Self { + inner: signed_delegate_action, + }) + } } impl std::str::FromStr for SignedDelegateActionAsBase64 { @@ -39,3 +87,11 @@ impl From Self { inner: value } } } + +impl From + for near_primitives::action::delegate::SignedDelegateAction +{ + fn from(signed_delegate_action: SignedDelegateActionAsBase64) -> Self { + signed_delegate_action.inner + } +}