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