From e33f0976651fb86140a39ed80e3305886a1c8772 Mon Sep 17 00:00:00 2001 From: Willem Wyndham Date: Tue, 10 Sep 2024 15:58:30 -0400 Subject: [PATCH] feat: tx send Add tx send subcommand to send transaction to the network --- FULL_HELP_DOCS.md | 17 ++++++ .../soroban-test/tests/it/integration/tx.rs | 11 +++- cmd/soroban-cli/src/commands/tx/mod.rs | 8 ++- cmd/soroban-cli/src/commands/tx/send.rs | 61 +++++++++++++++++++ 4 files changed, 92 insertions(+), 5 deletions(-) create mode 100644 cmd/soroban-cli/src/commands/tx/send.rs diff --git a/FULL_HELP_DOCS.md b/FULL_HELP_DOCS.md index 9ba32c35a..ddf954b5f 100644 --- a/FULL_HELP_DOCS.md +++ b/FULL_HELP_DOCS.md @@ -1291,6 +1291,7 @@ Sign, Simulate, and Send transactions * `simulate` — Simulate a transaction envelope from stdin * `hash` — Calculate the hash of a transaction envelope from stdin +* `send` — Send a transaction envelope to the network * `sign` — Sign a transaction envelope appending the signature to the envelope @@ -1327,6 +1328,22 @@ Calculate the hash of a transaction envelope from stdin +## `stellar tx send` + +Send a transaction envelope to the network + +**Usage:** `stellar tx send [OPTIONS]` + +###### **Options:** + +* `--rpc-url ` — RPC server endpoint +* `--network-passphrase ` — Network passphrase to sign the transaction sent to the rpc server +* `--network ` — Name of network to use from config +* `--global` — Use global config +* `--config-dir ` — Location of config directory, default is "." + + + ## `stellar tx sign` Sign a transaction envelope appending the signature to the envelope diff --git a/cmd/crates/soroban-test/tests/it/integration/tx.rs b/cmd/crates/soroban-test/tests/it/integration/tx.rs index 7d7b65116..f1697ed54 100644 --- a/cmd/crates/soroban-test/tests/it/integration/tx.rs +++ b/cmd/crates/soroban-test/tests/it/integration/tx.rs @@ -75,9 +75,14 @@ async fn send() { assert_eq!(rpc_result.status, "SUCCESS"); } -async fn send_manually(sandbox: &TestEnv, tx_env: &TransactionEnvelope) -> GetTransactionResponse { - let client = soroban_rpc::Client::new(&sandbox.rpc_url).unwrap(); - client.send_transaction_polling(tx_env).await.unwrap() +async fn send_manually(sandbox: &TestEnv, tx_env: &TransactionEnvelope) -> String { + sandbox + .new_assert_cmd("tx") + .arg("send") + .write_stdin(tx_env.to_xdr_base64(Limits::none()).unwrap()) + .assert() + .success() + .stdout_as_str() } fn sign_manually(sandbox: &TestEnv, tx_env: &TransactionEnvelope) -> TransactionEnvelope { diff --git a/cmd/soroban-cli/src/commands/tx/mod.rs b/cmd/soroban-cli/src/commands/tx/mod.rs index 5f0f90c4c..d5b8c49d4 100644 --- a/cmd/soroban-cli/src/commands/tx/mod.rs +++ b/cmd/soroban-cli/src/commands/tx/mod.rs @@ -3,6 +3,7 @@ use clap::Parser; use super::global; pub mod hash; +pub mod send; pub mod sign; pub mod simulate; pub mod xdr; @@ -13,19 +14,21 @@ pub enum Cmd { Simulate(simulate::Cmd), /// Calculate the hash of a transaction envelope from stdin Hash(hash::Cmd), + /// Send a transaction envelope to the network + Send(send::Cmd), /// Sign a transaction envelope appending the signature to the envelope Sign(sign::Cmd), } #[derive(thiserror::Error, Debug)] pub enum Error { - /// An error during the simulation #[error(transparent)] Simulate(#[from] simulate::Error), - /// An error during hash calculation #[error(transparent)] Hash(#[from] hash::Error), #[error(transparent)] + Send(#[from] send::Error), + #[error(transparent)] Sign(#[from] sign::Error), } @@ -34,6 +37,7 @@ impl Cmd { match self { Cmd::Simulate(cmd) => cmd.run(global_args).await?, Cmd::Hash(cmd) => cmd.run(global_args)?, + Cmd::Send(cmd) => cmd.run(global_args).await?, Cmd::Sign(cmd) => cmd.run(global_args).await?, }; Ok(()) diff --git a/cmd/soroban-cli/src/commands/tx/send.rs b/cmd/soroban-cli/src/commands/tx/send.rs new file mode 100644 index 000000000..069434609 --- /dev/null +++ b/cmd/soroban-cli/src/commands/tx/send.rs @@ -0,0 +1,61 @@ +use async_trait::async_trait; +use soroban_rpc::GetTransactionResponse; + +use crate::commands::{global, NetworkRunnable}; +use crate::config::{self, locator, network}; + +#[derive(thiserror::Error, Debug)] +pub enum Error { + #[error(transparent)] + XdrArgs(#[from] super::xdr::Error), + #[error(transparent)] + Network(#[from] network::Error), + #[error(transparent)] + Locator(#[from] locator::Error), + #[error(transparent)] + Config(#[from] config::Error), + #[error(transparent)] + Rpc(#[from] crate::rpc::Error), + #[error(transparent)] + SerdeJson(#[from] serde_json::Error), +} + +#[derive(Debug, clap::Parser, Clone)] +#[group(skip)] +/// Command to send a transaction envelope to the network +/// e.g. `cat file.txt | soroban tx send` +pub struct Cmd { + #[clap(flatten)] + pub network: network::Args, + #[clap(flatten)] + pub locator: locator::Args, +} + +impl Cmd { + pub async fn run(&self, global_args: &global::Args) -> Result<(), Error> { + let response = self.run_against_rpc_server(Some(global_args), None).await?; + println!("{}", serde_json::to_string_pretty(&response)?); + Ok(()) + } +} + +#[async_trait] +impl NetworkRunnable for Cmd { + type Error = Error; + + type Result = GetTransactionResponse; + async fn run_against_rpc_server( + &self, + _: Option<&global::Args>, + config: Option<&config::Args>, + ) -> Result { + let network = if let Some(config) = config { + config.get_network()? + } else { + self.network.get(&self.locator)? + }; + let client = crate::rpc::Client::new(&network.rpc_url)?; + let tx_env = super::xdr::tx_envelope_from_stdin()?; + Ok(client.send_transaction_polling(&tx_env).await?) + } +}