diff --git a/cmd/soroban-cli/src/commands/config/alias.rs b/cmd/soroban-cli/src/commands/config/alias.rs index ca17c411e..68ad556d6 100644 --- a/cmd/soroban-cli/src/commands/config/alias.rs +++ b/cmd/soroban-cli/src/commands/config/alias.rs @@ -1,103 +1,8 @@ -use std::{ - collections::HashMap, - fs::{self, create_dir_all, OpenOptions}, - io::Write, - path::PathBuf, -}; +use std::collections::HashMap; use serde::{Deserialize, Serialize}; -use stellar_strkey::DecodeError; - -use crate::commands::config; - -use super::Args; #[derive(Serialize, Deserialize, Default)] pub struct Data { - ids: HashMap, -} - -#[derive(thiserror::Error, Debug)] -pub enum Error { - #[error(transparent)] - Json(#[from] serde_json::Error), - #[error(transparent)] - Io(#[from] std::io::Error), - #[error("cannot access config dir for alias file")] - CannotAccessConfigDir, - #[error("cannot parse contract ID {0}: {1}")] - CannotParseContractId(String, DecodeError), - #[error(transparent)] - Config(#[from] config::Error), -} - -impl Args { - fn load(&self, alias: &str) -> Result, Error> { - let path = self.alias_path(alias)?; - - if !path.exists() { - return Ok(None); - } - - let content = fs::read_to_string(path)?; - let data: Data = serde_json::from_str(&content).unwrap_or_default(); - - Ok(Some(data)) - } - - fn alias_path(&self, alias: &str) -> Result { - let file_name = format!("{alias}.json"); - let config_dir = self.config_dir()?; - Ok(config_dir.join("contract-ids").join(file_name)) - } - - pub fn save_contract_id(&self, contract_id: &str, alias: &str) -> Result<(), Error> { - let path = self.alias_path(alias)?; - let dir = path.parent().ok_or(Error::CannotAccessConfigDir)?; - - create_dir_all(dir).map_err(|_| Error::CannotAccessConfigDir)?; - - let content = fs::read_to_string(&path).unwrap_or_default(); - let mut data: Data = serde_json::from_str(&content).unwrap_or_default(); - - let mut to_file = OpenOptions::new() - .create(true) - .truncate(true) - .write(true) - .open(path)?; - - let network = self.get_network()?; - - data.ids - .insert(network.network_passphrase, contract_id.into()); - - let content = serde_json::to_string(&data)?; - - Ok(to_file.write_all(content.as_bytes())?) - } - - pub fn get_contract_id( - &self, - alias: &str, - network_passphrase: &str, - ) -> Result, Error> { - let Some(alias_data) = self.load(alias)? else { - return Ok(None); - }; - - Ok(alias_data.ids.get(network_passphrase).cloned()) - } - - pub fn resolve_contract_id( - &self, - alias_or_contract_id: &str, - network_passphrase: &str, - ) -> Result<[u8; 32], Error> { - let contract_id = self - .get_contract_id(alias_or_contract_id, network_passphrase)? - .unwrap_or_else(|| alias_or_contract_id.to_string()); - - soroban_spec_tools::utils::contract_id_from_str(&contract_id) - .map_err(|e| Error::CannotParseContractId(contract_id.clone(), e)) - } + pub ids: HashMap, } diff --git a/cmd/soroban-cli/src/commands/config/locator.rs b/cmd/soroban-cli/src/commands/config/locator.rs index 09a6c2f73..d32ff5e1d 100644 --- a/cmd/soroban-cli/src/commands/config/locator.rs +++ b/cmd/soroban-cli/src/commands/config/locator.rs @@ -3,14 +3,17 @@ use serde::de::DeserializeOwned; use std::{ ffi::OsStr, fmt::Display, - fs, io, + fs::{self, create_dir_all, OpenOptions}, + io, + io::Write, path::{Path, PathBuf}, str::FromStr, }; +use stellar_strkey::DecodeError; use crate::{utils::find_config_dir, Pwd}; -use super::{network::Network, secret::Secret}; +use super::{alias, network::Network, secret::Secret}; #[derive(thiserror::Error, Debug)] pub enum Error { @@ -56,6 +59,12 @@ pub enum Error { String(#[from] std::string::FromUtf8Error), #[error(transparent)] Secret(#[from] crate::commands::config::secret::Error), + #[error(transparent)] + Json(#[from] serde_json::Error), + #[error("cannot access config dir for alias file")] + CannotAccessConfigDir, + #[error("cannot parse contract ID {0}: {1}")] + CannotParseContractId(String, DecodeError), } #[derive(Debug, clap::Args, Default, Clone)] @@ -211,6 +220,78 @@ impl Args { pub fn remove_network(&self, name: &str) -> Result<(), Error> { KeyType::Network.remove(name, &self.config_dir()?) } + + fn load_contract_from_alias(&self, alias: &str) -> Result, Error> { + let path = self.alias_path(alias)?; + + if !path.exists() { + return Ok(None); + } + + let content = fs::read_to_string(path)?; + let data: alias::Data = serde_json::from_str(&content).unwrap_or_default(); + + Ok(Some(data)) + } + + fn alias_path(&self, alias: &str) -> Result { + let file_name = format!("{alias}.json"); + let config_dir = self.config_dir()?; + Ok(config_dir.join("contract-ids").join(file_name)) + } + + pub fn save_contract_id( + &self, + network_passphrase: &str, + contract_id: &str, + alias: &str, + ) -> Result<(), Error> { + let path = self.alias_path(alias)?; + let dir = path.parent().ok_or(Error::CannotAccessConfigDir)?; + + create_dir_all(dir).map_err(|_| Error::CannotAccessConfigDir)?; + + let content = fs::read_to_string(&path).unwrap_or_default(); + let mut data: alias::Data = serde_json::from_str(&content).unwrap_or_default(); + + let mut to_file = OpenOptions::new() + .create(true) + .truncate(true) + .write(true) + .open(path)?; + + data.ids + .insert(network_passphrase.into(), contract_id.into()); + + let content = serde_json::to_string(&data)?; + + Ok(to_file.write_all(content.as_bytes())?) + } + + pub fn get_contract_id( + &self, + alias: &str, + network_passphrase: &str, + ) -> Result, Error> { + let Some(alias_data) = self.load_contract_from_alias(alias)? else { + return Ok(None); + }; + + Ok(alias_data.ids.get(network_passphrase).cloned()) + } + + pub fn resolve_contract_id( + &self, + alias_or_contract_id: &str, + network_passphrase: &str, + ) -> Result<[u8; 32], Error> { + let contract_id = self + .get_contract_id(alias_or_contract_id, network_passphrase)? + .unwrap_or_else(|| alias_or_contract_id.to_string()); + + soroban_spec_tools::utils::contract_id_from_str(&contract_id) + .map_err(|e| Error::CannotParseContractId(contract_id.clone(), e)) + } } fn ensure_directory(dir: PathBuf) -> Result { diff --git a/cmd/soroban-cli/src/commands/contract/deploy/wasm.rs b/cmd/soroban-cli/src/commands/contract/deploy/wasm.rs index 342c668db..36bfb3f5c 100644 --- a/cmd/soroban-cli/src/commands/contract/deploy/wasm.rs +++ b/cmd/soroban-cli/src/commands/contract/deploy/wasm.rs @@ -16,7 +16,7 @@ use soroban_env_host::{ }; use crate::commands::{ - config::{alias, data}, + config::{data, locator}, contract::{self, id::wasm::get_contract_id}, global, network, txn_result::{TxnEnvelopeResult, TxnResult}, @@ -109,7 +109,7 @@ pub enum Error { )] InvalidAliasFormat { alias: String }, #[error(transparent)] - Alias(#[from] alias::Error), + Locator(#[from] locator::Error), } impl Cmd { @@ -120,8 +120,14 @@ impl Cmd { match res { TxnEnvelopeResult::TxnEnvelope(tx) => println!("{}", tx.to_xdr_base64(Limits::none())?), TxnEnvelopeResult::Res(contract) => { + let network = self.config.get_network()?; + if let Some(alias) = self.alias.clone() { - self.config.save_contract_id(&contract, &alias)?; + self.config.locator.save_contract_id( + &network.network_passphrase, + &contract, + &alias, + )?; } println!("{contract}"); diff --git a/cmd/soroban-cli/src/commands/contract/invoke.rs b/cmd/soroban-cli/src/commands/contract/invoke.rs index 0690f121c..537bcf9eb 100644 --- a/cmd/soroban-cli/src/commands/contract/invoke.rs +++ b/cmd/soroban-cli/src/commands/contract/invoke.rs @@ -27,7 +27,6 @@ use super::super::{ config::{self, locator}, events, }; -use crate::commands::config::alias; use crate::commands::txn_result::{TxnEnvelopeResult, TxnResult}; use crate::commands::NetworkRunnable; use crate::get_spec::{self, get_remote_contract_spec}; @@ -147,8 +146,6 @@ pub enum Error { Network(#[from] network::Error), #[error(transparent)] GetSpecError(#[from] get_spec::Error), - #[error(transparent)] - Alias(#[from] alias::Error), } impl From for Error { @@ -315,6 +312,7 @@ impl NetworkRunnable for Cmd { tracing::trace!(?network); let contract_id = self .config + .locator .resolve_contract_id(&self.contract_id, &network.network_passphrase)?; let spec_entries = self.spec_entries()?; if let Some(spec_entries) = &spec_entries {