soroban_cli/config/
sign_with.rs

1use crate::{
2    config::UnresolvedMuxedAccount,
3    print::Print,
4    signer::{self, ledger, Signer, SignerKind},
5    xdr::{self, TransactionEnvelope},
6};
7
8use super::{
9    locator,
10    network::{self, Network},
11    secret,
12};
13
14#[derive(thiserror::Error, Debug)]
15pub enum Error {
16    #[error(transparent)]
17    Network(#[from] network::Error),
18    #[error(transparent)]
19    Signer(#[from] signer::Error),
20    #[error(transparent)]
21    Secret(#[from] secret::Error),
22    #[error(transparent)]
23    Locator(#[from] locator::Error),
24    #[error(transparent)]
25    Rpc(#[from] soroban_rpc::Error),
26    #[error("No sign with key provided")]
27    NoSignWithKey,
28    #[error(transparent)]
29    StrKey(#[from] stellar_strkey::DecodeError),
30    #[error(transparent)]
31    Xdr(#[from] xdr::Error),
32    #[error(transparent)]
33    Ledger(#[from] signer::ledger::Error),
34}
35
36#[derive(Debug, clap::Args, Clone, Default)]
37#[group(skip)]
38pub struct Args {
39    /// Sign with a local key or key saved in OS secure storage. Can be an identity (--sign-with-key alice), a secret key (--sign-with-key SC36…), or a seed phrase (--sign-with-key "kite urban…"). If using seed phrase, `--hd-path` defaults to the `0` path.
40    #[arg(long, env = "STELLAR_SIGN_WITH_KEY")]
41    pub sign_with_key: Option<String>,
42
43    #[arg(long, conflicts_with = "sign_with_lab")]
44    /// If using a seed phrase to sign, sets which hierarchical deterministic path to use, e.g. `m/44'/148'/{hd_path}`. Example: `--hd-path 1`. Default: `0`
45    pub hd_path: Option<usize>,
46
47    #[allow(clippy::doc_markdown)]
48    /// Sign with https://lab.stellar.org
49    #[arg(long, conflicts_with = "sign_with_key", env = "STELLAR_SIGN_WITH_LAB")]
50    pub sign_with_lab: bool,
51
52    /// Sign with a ledger wallet
53    #[arg(
54        long,
55        conflicts_with = "sign_with_key",
56        conflicts_with = "sign_with_lab",
57        env = "STELLAR_SIGN_WITH_LEDGER"
58    )]
59    pub sign_with_ledger: bool,
60}
61
62impl Args {
63    // when a default_signer_account is provided, it will be used as the tx signer if the user does not specify a signer. The default signer should be the tx's source_account.
64    pub async fn sign_tx_env(
65        &self,
66        tx: &TransactionEnvelope,
67        locator: &locator::Args,
68        network: &Network,
69        quiet: bool,
70        default_signer_account: Option<&UnresolvedMuxedAccount>,
71    ) -> Result<TransactionEnvelope, Error> {
72        let print = Print::new(quiet);
73        let signer = if self.sign_with_lab {
74            Signer {
75                kind: SignerKind::Lab,
76                print,
77            }
78        } else if self.sign_with_ledger {
79            let ledger = ledger::new(
80                self.hd_path
81                    .unwrap_or_default()
82                    .try_into()
83                    .unwrap_or_default(),
84            )
85            .await?;
86            Signer {
87                kind: SignerKind::Ledger(ledger),
88                print,
89            }
90        } else {
91            // default to using the source account local key, if the user did not pass in a key
92            let key_or_name = match self.sign_with_key.as_deref() {
93                Some(k) => k,
94                None => match default_signer_account {
95                    Some(UnresolvedMuxedAccount::AliasOrSecret(ref s)) => s.as_str(),
96                    _ => return Err(Error::NoSignWithKey),
97                },
98            };
99
100            let secret = locator.get_secret_key(key_or_name)?;
101            secret.signer(self.hd_path, print).await?
102        };
103        Ok(signer.sign_tx_env(tx, network).await?)
104    }
105}