use crate::{
commands::{global, txn_result::TxnEnvelopeResult},
config::{
self,
address::{self, UnresolvedMuxedAccount},
data, network, secret,
},
rpc::{self, Client, GetTransactionResponse},
tx::builder::{self, asset, TxExt},
xdr::{self, Limits, WriteXdr},
};
#[derive(Debug, clap::Args, Clone)]
#[group(skip)]
pub struct Args {
#[clap(flatten)]
pub config: config::Args,
#[arg(long)]
pub build_only: bool,
}
#[derive(thiserror::Error, Debug)]
pub enum Error {
#[error(transparent)]
Rpc(#[from] rpc::Error),
#[error(transparent)]
Config(#[from] config::Error),
#[error(transparent)]
Network(#[from] network::Error),
#[error(transparent)]
Secret(#[from] secret::Error),
#[error(transparent)]
Tx(#[from] builder::Error),
#[error(transparent)]
Data(#[from] data::Error),
#[error(transparent)]
Xdr(#[from] xdr::Error),
#[error(transparent)]
Address(#[from] address::Error),
#[error(transparent)]
Asset(#[from] asset::Error),
#[error(transparent)]
TxXdr(#[from] super::xdr::Error),
#[error("invalid price format: {0}")]
InvalidPrice(String),
#[error("invalid path: {0}")]
InvalidPath(String),
#[error("invalid pool ID format: {0}")]
InvalidPoolId(String),
#[error("invalid hex for {name}: {hex}")]
InvalidHex { name: String, hex: String },
}
impl Args {
pub async fn tx(&self, body: impl Into<xdr::OperationBody>) -> Result<xdr::Transaction, Error> {
let source_account = self.source_account().await?;
let seq_num = self
.config
.next_sequence_number(source_account.clone().account_id())
.await?;
let operation = xdr::Operation {
source_account: None,
body: body.into(),
};
Ok(xdr::Transaction::new_tx(
source_account,
self.config.get_inclusion_fee()?,
seq_num,
operation,
))
}
pub fn client(&self) -> Result<Client, Error> {
let network = self.config.get_network()?;
Ok(Client::new(&network.rpc_url)?)
}
pub async fn handle(
&self,
op: impl Into<xdr::OperationBody>,
global_args: &global::Args,
) -> Result<TxnEnvelopeResult<GetTransactionResponse>, Error> {
let tx = self.tx(op).await?;
self.handle_tx(tx, global_args).await
}
pub async fn handle_and_print(
&self,
op: impl Into<xdr::OperationBody>,
global_args: &global::Args,
) -> Result<(), Error> {
let res = self.handle(op, global_args).await?;
if let TxnEnvelopeResult::TxnEnvelope(tx) = res {
println!("{}", tx.to_xdr_base64(Limits::none())?);
}
Ok(())
}
pub async fn handle_tx(
&self,
tx: xdr::Transaction,
args: &global::Args,
) -> Result<TxnEnvelopeResult<GetTransactionResponse>, Error> {
let network = self.config.get_network()?;
let client = Client::new(&network.rpc_url)?;
if self.build_only {
return Ok(TxnEnvelopeResult::TxnEnvelope(Box::new(tx.into())));
}
let txn_resp = client
.send_transaction_polling(&self.config.sign(tx, args.quiet).await?)
.await?;
if !args.no_cache {
data::write(txn_resp.clone().try_into().unwrap(), &network.rpc_uri()?)?;
}
Ok(TxnEnvelopeResult::Res(txn_resp))
}
pub async fn source_account(&self) -> Result<xdr::MuxedAccount, Error> {
Ok(self.config.source_account().await?)
}
pub fn resolve_muxed_address(
&self,
address: &UnresolvedMuxedAccount,
) -> Result<xdr::MuxedAccount, Error> {
Ok(address.resolve_muxed_account_sync(&self.config.locator, self.config.hd_path())?)
}
pub fn resolve_account_id(
&self,
address: &UnresolvedMuxedAccount,
) -> Result<xdr::AccountId, Error> {
Ok(address
.resolve_muxed_account_sync(&self.config.locator, self.config.hd_path())?
.account_id())
}
pub async fn add_op(
&self,
op_body: impl Into<xdr::OperationBody>,
tx_env: xdr::TransactionEnvelope,
op_source: Option<&address::UnresolvedMuxedAccount>,
) -> Result<xdr::TransactionEnvelope, Error> {
let mut source_account = None;
if let Some(account) = op_source {
source_account = Some(
account
.resolve_muxed_account(&self.config.locator, self.config.hd_path())
.await?,
);
}
let op = xdr::Operation {
source_account,
body: op_body.into(),
};
Ok(super::xdr::add_op(tx_env, op)?)
}
pub fn resolve_asset(&self, asset: &builder::Asset) -> Result<xdr::Asset, Error> {
Ok(asset.resolve(&self.config.locator)?)
}
pub fn resolve_signer_key(
&self,
signer_account: &UnresolvedMuxedAccount,
) -> Result<xdr::SignerKey, Error> {
let resolved_account = self.resolve_account_id(signer_account)?;
let signer_key = match resolved_account.0 {
xdr::PublicKey::PublicKeyTypeEd25519(uint256) => xdr::SignerKey::Ed25519(uint256),
};
Ok(signer_key)
}
}