Skip to main content

soroban_cli/config/
sign_with.rs

1use crate::{
2    config::UnresolvedMuxedAccount,
3    print::Print,
4    signer::{self, ledger::LedgerEntry, Signer, SignerKind},
5    xdr::{self, TransactionEnvelope},
6};
7
8use super::{
9    locator,
10    network::{self, Network},
11    secret,
12};
13use crate::commands::HEADING_SIGNING;
14
15#[derive(thiserror::Error, Debug)]
16pub enum Error {
17    #[error(transparent)]
18    Network(#[from] network::Error),
19    #[error(transparent)]
20    Signer(#[from] signer::Error),
21    #[error(transparent)]
22    Secret(#[from] secret::Error),
23    #[error(transparent)]
24    Locator(#[from] locator::Error),
25    #[error(transparent)]
26    Rpc(#[from] soroban_rpc::Error),
27    #[error("No sign with key provided")]
28    NoSignWithKey,
29    #[error(transparent)]
30    StrKey(#[from] stellar_strkey::DecodeError),
31    #[error(transparent)]
32    Xdr(#[from] xdr::Error),
33    #[error(transparent)]
34    Ledger(#[from] signer::ledger::Error),
35}
36
37#[derive(Debug, clap::Args, Clone, Default)]
38#[group(skip)]
39pub struct Args {
40    /// 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.
41    #[arg(
42        long,
43        env = "STELLAR_SIGN_WITH_KEY",
44        hide_env_values = true,
45        help_heading = HEADING_SIGNING
46    )]
47    pub sign_with_key: Option<String>,
48
49    #[arg(long, conflicts_with = "sign_with_lab", help_heading = HEADING_SIGNING)]
50    /// 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`
51    pub hd_path: Option<u32>,
52
53    #[allow(clippy::doc_markdown)]
54    /// Sign with https://lab.stellar.org
55    #[arg(
56        long,
57        conflicts_with = "sign_with_key",
58        env = "STELLAR_SIGN_WITH_LAB",
59        help_heading = HEADING_SIGNING
60    )]
61    pub sign_with_lab: bool,
62
63    /// Sign with a ledger wallet
64    #[arg(
65        long,
66        conflicts_with = "sign_with_key",
67        conflicts_with = "sign_with_lab",
68        env = "STELLAR_SIGN_WITH_LEDGER",
69        help_heading = HEADING_SIGNING
70    )]
71    pub sign_with_ledger: bool,
72
73    /// Sign without prompting for approval. Only applies to signatures that require user approval, like non-root Soroban auth entries.
74    #[arg(long, help_heading = HEADING_SIGNING)]
75    pub auto_sign: bool,
76}
77
78impl Args {
79    // 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.
80    pub async fn sign_tx_env(
81        &self,
82        tx: &TransactionEnvelope,
83        locator: &locator::Args,
84        network: &Network,
85        quiet: bool,
86        default_signer_account: Option<&UnresolvedMuxedAccount>,
87    ) -> Result<TransactionEnvelope, Error> {
88        let print = Print::new(quiet);
89        let signer = if self.sign_with_lab {
90            Signer {
91                kind: SignerKind::Lab,
92                print,
93            }
94        } else if self.sign_with_ledger {
95            Signer {
96                kind: SignerKind::Ledger(LedgerEntry {
97                    hd_path: self.hd_path.unwrap_or_default(),
98                    public_key: None,
99                }),
100                print,
101            }
102        } else {
103            // default to using the source account local key, if the user did not pass in a key
104            let key_or_name = match self.sign_with_key.as_deref() {
105                Some(k) => k,
106                None => match default_signer_account {
107                    Some(UnresolvedMuxedAccount::AliasOrSecret(ref s)) => s.as_str(),
108                    _ => return Err(Error::NoSignWithKey),
109                },
110            };
111
112            let secret = locator.get_secret_key_with_hd_path(key_or_name, self.hd_path)?;
113            secret.signer(self.hd_path, print)?
114        };
115        Ok(signer.sign_tx_env(tx, network).await?)
116    }
117}