soroban_cli/config/
mod.rs1use serde::{Deserialize, Serialize};
2use std::{
3 fs::{self, File},
4 io::Write,
5};
6
7use crate::{
8 print::Print,
9 signer::{self, Signer},
10 xdr::{self, SequenceNumber, Transaction, TransactionEnvelope, TransactionV1Envelope, VecM},
11 Pwd,
12};
13use network::Network;
14
15pub mod address;
16pub mod alias;
17pub mod data;
18pub mod key;
19pub mod locator;
20pub mod network;
21pub mod sc_address;
22pub mod secret;
23pub mod sign_with;
24pub mod upgrade_check;
25
26use crate::config::locator::cli_config_file;
27pub use address::UnresolvedMuxedAccount;
28pub use alias::UnresolvedContract;
29pub use sc_address::UnresolvedScAddress;
30
31#[derive(thiserror::Error, Debug)]
32pub enum Error {
33 #[error(transparent)]
34 Network(#[from] network::Error),
35 #[error(transparent)]
36 Secret(#[from] secret::Error),
37 #[error(transparent)]
38 Locator(#[from] locator::Error),
39 #[error(transparent)]
40 Rpc(#[from] soroban_rpc::Error),
41 #[error(transparent)]
42 Signer(#[from] signer::Error),
43 #[error(transparent)]
44 SignWith(#[from] sign_with::Error),
45 #[error(transparent)]
46 StellarStrkey(#[from] stellar_strkey::DecodeError),
47 #[error(transparent)]
48 Address(#[from] address::Error),
49}
50
51#[derive(Debug, clap::Args, Clone, Default)]
52#[group(skip)]
53pub struct Args {
54 #[command(flatten)]
55 pub network: network::Args,
56
57 #[arg(long, short = 's', visible_alias = "source", env = "STELLAR_ACCOUNT")]
58 pub source_account: UnresolvedMuxedAccount,
65
66 #[command(flatten)]
67 pub locator: locator::Args,
68
69 #[command(flatten)]
70 pub sign_with: sign_with::Args,
71}
72
73impl Args {
74 pub async fn source_account(&self) -> Result<xdr::MuxedAccount, Error> {
76 Ok(self
77 .source_account
78 .resolve_muxed_account(&self.locator, self.hd_path())
79 .await?)
80 }
81
82 pub async fn source_signer(&self) -> Result<Signer, Error> {
83 let print = Print::new(true);
84 let secret = &self.source_account.resolve_secret(&self.locator)?;
85 Ok(secret.signer(None, print).await?)
86 }
87
88 pub fn key_pair(&self) -> Result<ed25519_dalek::SigningKey, Error> {
89 let key = &self.source_account.resolve_secret(&self.locator)?;
90 Ok(key.key_pair(self.hd_path())?)
91 }
92
93 pub async fn sign(&self, tx: Transaction, quiet: bool) -> Result<TransactionEnvelope, Error> {
94 let tx_env = TransactionEnvelope::Tx(TransactionV1Envelope {
95 tx,
96 signatures: VecM::default(),
97 });
98 Ok(self
99 .sign_with
100 .sign_tx_env(
101 &tx_env,
102 &self.locator,
103 &self.network.get(&self.locator)?,
104 quiet,
105 Some(&self.source_account),
106 )
107 .await?)
108 }
109
110 pub async fn sign_soroban_authorizations(
111 &self,
112 tx: &Transaction,
113 signers: &[Signer],
114 ) -> Result<Option<Transaction>, Error> {
115 let network = self.get_network()?;
116 let source_signer = self.source_signer().await?;
117 let client = network.rpc_client()?;
118 let latest_ledger = client.get_latest_ledger().await?.sequence;
119 let seq_num = latest_ledger + 60; Ok(signer::sign_soroban_authorizations(
121 tx,
122 &source_signer,
123 signers,
124 seq_num,
125 &network.network_passphrase,
126 )?)
127 }
128
129 pub fn get_network(&self) -> Result<Network, Error> {
130 Ok(self.network.get(&self.locator)?)
131 }
132
133 pub async fn next_sequence_number(
134 &self,
135 account: impl Into<xdr::AccountId>,
136 ) -> Result<SequenceNumber, Error> {
137 let network = self.get_network()?;
138 let client = network.rpc_client()?;
139 Ok((client
140 .get_account(&account.into().to_string())
141 .await?
142 .seq_num
143 .0
144 + 1)
145 .into())
146 }
147
148 pub fn hd_path(&self) -> Option<usize> {
149 self.sign_with.hd_path
150 }
151}
152
153impl Pwd for Args {
154 fn set_pwd(&mut self, pwd: &std::path::Path) {
155 self.locator.set_pwd(pwd);
156 }
157}
158
159#[derive(Debug, clap::Args, Clone, Default)]
160#[group(skip)]
161pub struct ArgsLocatorAndNetwork {
162 #[command(flatten)]
163 pub network: network::Args,
164
165 #[command(flatten)]
166 pub locator: locator::Args,
167}
168
169impl ArgsLocatorAndNetwork {
170 pub fn get_network(&self) -> Result<Network, Error> {
171 Ok(self.network.get(&self.locator)?)
172 }
173}
174
175#[derive(Serialize, Deserialize, Debug, Default)]
176pub struct Config {
177 pub defaults: Defaults,
178}
179
180#[derive(Serialize, Deserialize, Debug, Default)]
181pub struct Defaults {
182 pub network: Option<String>,
183 pub identity: Option<String>,
184}
185
186impl Config {
187 pub fn new() -> Result<Config, locator::Error> {
188 let path = cli_config_file()?;
189
190 if path.exists() {
191 let data = fs::read_to_string(&path).map_err(|_| locator::Error::FileRead { path })?;
192 Ok(toml::from_str(&data)?)
193 } else {
194 Ok(Config::default())
195 }
196 }
197
198 #[must_use]
199 pub fn set_network(mut self, s: &str) -> Self {
200 self.defaults.network = Some(s.to_string());
201 self
202 }
203
204 #[must_use]
205 pub fn set_identity(mut self, s: &str) -> Self {
206 self.defaults.identity = Some(s.to_string());
207 self
208 }
209
210 #[must_use]
211 pub fn unset_identity(mut self) -> Self {
212 self.defaults.identity = None;
213 self
214 }
215
216 #[must_use]
217 pub fn unset_network(mut self) -> Self {
218 self.defaults.network = None;
219 self
220 }
221
222 pub fn save(&self) -> Result<(), locator::Error> {
223 let toml_string = toml::to_string(&self)?;
224 let path = cli_config_file()?;
225 let mut file = File::create(locator::ensure_directory(path)?)?;
227 file.write_all(toml_string.as_bytes())?;
228
229 Ok(())
230 }
231}