Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Added the ability to send a signed meta-transaction from a file #428

Merged
merged 1 commit into from
Dec 16, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
}
}
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
}
}
Loading