soroban_cli/commands/tx/
args.rs

1use crate::{
2    commands::{global, txn_result::TxnEnvelopeResult},
3    config::{
4        self,
5        address::{self, UnresolvedMuxedAccount},
6        data, network, secret,
7    },
8    fee,
9    rpc::{self, Client, GetTransactionResponse},
10    tx::builder::{self, asset, TxExt},
11    xdr::{self, Limits, WriteXdr},
12};
13
14#[derive(Debug, clap::Args, Clone)]
15#[group(skip)]
16pub struct Args {
17    #[clap(flatten)]
18    pub fee: fee::Args,
19    #[clap(flatten)]
20    pub config: config::Args,
21}
22
23#[derive(thiserror::Error, Debug)]
24pub enum Error {
25    #[error(transparent)]
26    Rpc(#[from] rpc::Error),
27    #[error(transparent)]
28    Config(#[from] config::Error),
29    #[error(transparent)]
30    Network(#[from] network::Error),
31    #[error(transparent)]
32    Secret(#[from] secret::Error),
33    #[error(transparent)]
34    Tx(#[from] builder::Error),
35    #[error(transparent)]
36    Data(#[from] data::Error),
37    #[error(transparent)]
38    Xdr(#[from] xdr::Error),
39    #[error(transparent)]
40    Address(#[from] address::Error),
41    #[error(transparent)]
42    Asset(#[from] asset::Error),
43    #[error(transparent)]
44    TxXdr(#[from] super::xdr::Error),
45}
46
47impl Args {
48    pub async fn tx(&self, body: impl Into<xdr::OperationBody>) -> Result<xdr::Transaction, Error> {
49        let source_account = self.source_account().await?;
50        let seq_num = self
51            .config
52            .next_sequence_number(source_account.clone().account_id())
53            .await?;
54        // Once we have a way to add operations this will be updated to allow for a different source account
55        let operation = xdr::Operation {
56            source_account: None,
57            body: body.into(),
58        };
59        Ok(xdr::Transaction::new_tx(
60            source_account,
61            self.fee.fee,
62            seq_num,
63            operation,
64        ))
65    }
66
67    pub fn client(&self) -> Result<Client, Error> {
68        let network = self.config.get_network()?;
69        Ok(Client::new(&network.rpc_url)?)
70    }
71
72    pub async fn handle(
73        &self,
74        op: impl Into<xdr::OperationBody>,
75        global_args: &global::Args,
76    ) -> Result<TxnEnvelopeResult<GetTransactionResponse>, Error> {
77        let tx = self.tx(op).await?;
78        self.handle_tx(tx, global_args).await
79    }
80
81    pub async fn handle_and_print(
82        &self,
83        op: impl Into<xdr::OperationBody>,
84        global_args: &global::Args,
85    ) -> Result<(), Error> {
86        let res = self.handle(op, global_args).await?;
87        if let TxnEnvelopeResult::TxnEnvelope(tx) = res {
88            println!("{}", tx.to_xdr_base64(Limits::none())?);
89        }
90        Ok(())
91    }
92
93    pub async fn handle_tx(
94        &self,
95        tx: xdr::Transaction,
96        args: &global::Args,
97    ) -> Result<TxnEnvelopeResult<GetTransactionResponse>, Error> {
98        let network = self.config.get_network()?;
99        let client = Client::new(&network.rpc_url)?;
100        if self.fee.build_only {
101            return Ok(TxnEnvelopeResult::TxnEnvelope(Box::new(tx.into())));
102        }
103
104        let txn_resp = client
105            .send_transaction_polling(&self.config.sign_with_local_key(tx).await?)
106            .await?;
107
108        if !args.no_cache {
109            data::write(txn_resp.clone().try_into().unwrap(), &network.rpc_uri()?)?;
110        }
111
112        Ok(TxnEnvelopeResult::Res(txn_resp))
113    }
114
115    pub async fn source_account(&self) -> Result<xdr::MuxedAccount, Error> {
116        Ok(self.config.source_account().await?)
117    }
118
119    pub fn resolve_muxed_address(
120        &self,
121        address: &UnresolvedMuxedAccount,
122    ) -> Result<xdr::MuxedAccount, Error> {
123        Ok(address.resolve_muxed_account_sync(&self.config.locator, self.config.hd_path)?)
124    }
125
126    pub fn resolve_account_id(
127        &self,
128        address: &UnresolvedMuxedAccount,
129    ) -> Result<xdr::AccountId, Error> {
130        Ok(address
131            .resolve_muxed_account_sync(&self.config.locator, self.config.hd_path)?
132            .account_id())
133    }
134
135    pub async fn add_op(
136        &self,
137        op_body: impl Into<xdr::OperationBody>,
138        tx_env: xdr::TransactionEnvelope,
139        op_source: Option<&address::UnresolvedMuxedAccount>,
140    ) -> Result<xdr::TransactionEnvelope, Error> {
141        let mut source_account = None;
142        if let Some(account) = op_source {
143            source_account = Some(
144                account
145                    .resolve_muxed_account(&self.config.locator, self.config.hd_path)
146                    .await?,
147            );
148        }
149        let op = xdr::Operation {
150            source_account,
151            body: op_body.into(),
152        };
153        Ok(super::xdr::add_op(tx_env, op)?)
154    }
155
156    pub fn resolve_asset(&self, asset: &builder::Asset) -> Result<xdr::Asset, Error> {
157        Ok(asset.resolve(&self.config.locator)?)
158    }
159}