Skip to content

Commit

Permalink
feat: Added the ability to send a signed meta-transaction from a file (
Browse files Browse the repository at this point in the history
…#428)

Co-authored-by: FroVolod <[email protected]>
  • Loading branch information
FroVolod and FroVolod authored Dec 16, 2024
1 parent 67e8d0f commit c7e2134
Show file tree
Hide file tree
Showing 5 changed files with 169 additions and 26 deletions.
4 changes: 2 additions & 2 deletions src/commands/transaction/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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),
}
102 changes: 92 additions & 10 deletions src/commands/transaction/send_meta_transaction/mod.rs
Original file line number Diff line number Diff line change
@@ -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)]
Expand All @@ -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: &<Base64SignedMetaTransaction as interactive_clap::ToInteractiveClapContextScope>::InteractiveClapContextScope,
) -> color_eyre::eyre::Result<Self> {
Ok(Self(SignedMetaTransactionContext {
global_context: previous_context,
signed_delegate_action: scope.signed_delegate_action.clone().into(),
}))
}
}

impl SendMetaTransactionContext {
impl From<Base64SignedMetaTransactionContext> 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: &<SendMetaTransaction as interactive_clap::ToInteractiveClapContextScope>::InteractiveClapContextScope,
scope: &<FileWithBase64SignedMetaTransaction as interactive_clap::ToInteractiveClapContextScope>::InteractiveClapContextScope,
) -> color_eyre::eyre::Result<Self> {
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::<FileSignedMetaTransaction>(&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<FileWithBase64SignedMetaTransactionContext> for SignedMetaTransactionContext {
fn from(item: FileWithBase64SignedMetaTransactionContext) -> Self {
item.0
}
}
6 changes: 3 additions & 3 deletions src/commands/transaction/send_meta_transaction/sign_as/mod.rs
Original file line number Diff line number Diff line change
@@ -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)]
Expand All @@ -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: &<RelayerAccountId as interactive_clap::ToInteractiveClapContextScope>::InteractiveClapContextScope,
) -> color_eyre::eyre::Result<Self> {
let get_prepopulated_transaction_after_getting_network_callback: crate::commands::GetPrepopulatedTransactionAfterGettingNetworkCallback =
Expand Down Expand Up @@ -71,7 +71,7 @@ impl From<RelayerAccountIdContext> for crate::commands::ActionContext {

impl RelayerAccountId {
fn input_relayer_account_id(
context: &super::SendMetaTransactionContext,
context: &super::SignedMetaTransactionContext,
) -> color_eyre::eyre::Result<Option<crate::types::account_id::AccountId>> {
loop {
let relayer_account_id = if let Some(account_id) =
Expand Down
25 changes: 15 additions & 10 deletions src/transaction_signature_options/save_to_file/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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)]
Expand Down Expand Up @@ -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))?
Expand All @@ -80,13 +77,21 @@ impl SaveToFileContext {

impl SaveToFile {
fn input_file_path(
_context: &super::SubmitContext,
context: &super::SubmitContext,
) -> color_eyre::eyre::Result<Option<crate::types::path_buf::PathBuf>> {
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()?,
))
}
Expand Down
58 changes: 57 additions & 1 deletion src/types/signed_delegate_action.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
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<D>(deserializer: D) -> Result<Self, D::Error>
where
D: serde::Deserializer<'de>,
{
let signed_delegate_action_as_base64 =
<String as serde::Deserialize>::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 {
Expand Down Expand Up @@ -39,3 +87,11 @@ impl From<near_primitives::action::delegate::SignedDelegateAction>
Self { inner: value }
}
}

impl From<SignedDelegateActionAsBase64>
for near_primitives::action::delegate::SignedDelegateAction
{
fn from(signed_delegate_action: SignedDelegateActionAsBase64) -> Self {
signed_delegate_action.inner
}
}

0 comments on commit c7e2134

Please sign in to comment.