From 05e362bd5fa76d0e5c90ce0f5a4996881cd72cc3 Mon Sep 17 00:00:00 2001 From: Nando Vieira Date: Mon, 26 Aug 2024 19:07:11 -0700 Subject: [PATCH] Add `contract alias remove`. (#1563) --- FULL_HELP_DOCS.md | 33 ++++++++++ .../src/commands/contract/alias.rs | 24 +++++++ .../src/commands/contract/alias/remove.rs | 62 +++++++++++++++++++ cmd/soroban-cli/src/commands/contract/mod.rs | 10 +++ cmd/soroban-cli/src/config/locator.rs | 25 ++++++++ 5 files changed, 154 insertions(+) create mode 100644 cmd/soroban-cli/src/commands/contract/alias.rs create mode 100644 cmd/soroban-cli/src/commands/contract/alias/remove.rs diff --git a/FULL_HELP_DOCS.md b/FULL_HELP_DOCS.md index c13e002c1..e61227444 100644 --- a/FULL_HELP_DOCS.md +++ b/FULL_HELP_DOCS.md @@ -76,6 +76,7 @@ Tools for smart contract developers ###### **Subcommands:** * `asset` — Utilities to deploy a Stellar Asset Contract or get its id +* `alias` — Utilities to manage contract aliases * `bindings` — Generate code client bindings for a contract * `build` — Build a contract from source * `extend` — Extend the time to live ledger of a contract-data ledger entry @@ -151,6 +152,38 @@ Deploy builtin Soroban Asset Contract +## `stellar contract alias` + +Utilities to manage contract aliases + +**Usage:** `stellar contract alias ` + +###### **Subcommands:** + +* `remove` — Remove contract alias + + + +## `stellar contract alias remove` + +Remove contract alias + +**Usage:** `stellar contract alias remove [OPTIONS] ` + +###### **Arguments:** + +* `` — The contract alias that will be removed + +###### **Options:** + +* `--global` — Use global config +* `--config-dir ` — Location of config directory, default is "." +* `--rpc-url ` — RPC server endpoint +* `--network-passphrase ` — Network passphrase to sign the transaction sent to the rpc server +* `--network ` — Name of network to use from config + + + ## `stellar contract bindings` Generate code client bindings for a contract diff --git a/cmd/soroban-cli/src/commands/contract/alias.rs b/cmd/soroban-cli/src/commands/contract/alias.rs new file mode 100644 index 000000000..4dedd35ff --- /dev/null +++ b/cmd/soroban-cli/src/commands/contract/alias.rs @@ -0,0 +1,24 @@ +use crate::commands::global; + +pub mod remove; + +#[derive(Debug, clap::Subcommand)] +pub enum Cmd { + /// Remove contract alias + Remove(remove::Cmd), +} + +#[derive(thiserror::Error, Debug)] +pub enum Error { + #[error(transparent)] + Remove(#[from] remove::Error), +} + +impl Cmd { + pub async fn run(&self, global_args: &global::Args) -> Result<(), Error> { + match &self { + Cmd::Remove(remove) => remove.run(global_args).await?, + } + Ok(()) + } +} diff --git a/cmd/soroban-cli/src/commands/contract/alias/remove.rs b/cmd/soroban-cli/src/commands/contract/alias/remove.rs new file mode 100644 index 000000000..27c92e6ea --- /dev/null +++ b/cmd/soroban-cli/src/commands/contract/alias/remove.rs @@ -0,0 +1,62 @@ +use std::fmt::Debug; + +use clap::{command, Parser}; + +use crate::commands::{config::network, global}; +use crate::config::locator; +use crate::print::Print; + +#[derive(Parser, Debug, Clone)] +#[group(skip)] +pub struct Cmd { + #[command(flatten)] + pub config_locator: locator::Args, + + #[command(flatten)] + network: network::Args, + + /// The contract alias that will be removed. + pub alias: String, +} + +#[derive(thiserror::Error, Debug)] +pub enum Error { + #[error(transparent)] + Locator(#[from] locator::Error), + + #[error(transparent)] + Network(#[from] network::Error), + + #[error("no contract found with alias `{alias}`")] + NoContract { alias: String }, +} + +impl Cmd { + #[allow(clippy::unused_async)] + pub async fn run(&self, global_args: &global::Args) -> Result<(), Error> { + let print = Print::new(global_args.quiet); + let alias = &self.alias; + let network = self.network.get(&self.config_locator)?; + let network_passphrase = &network.network_passphrase; + + let Some(contract) = self + .config_locator + .get_contract_id(&self.alias, network_passphrase)? + else { + return Err(Error::NoContract { + alias: alias.into(), + }); + }; + + print.infoln(format!( + "Contract alias '{alias}' references {contract} on network '{network_passphrase}'" + )); + + self.config_locator + .remove_contract_id(&network.network_passphrase, alias)?; + + print.checkln(format!("Contract alias '{alias}' has been removed")); + + Ok(()) + } +} diff --git a/cmd/soroban-cli/src/commands/contract/mod.rs b/cmd/soroban-cli/src/commands/contract/mod.rs index afd49c6ab..45fd00b96 100644 --- a/cmd/soroban-cli/src/commands/contract/mod.rs +++ b/cmd/soroban-cli/src/commands/contract/mod.rs @@ -1,3 +1,4 @@ +pub mod alias; pub mod asset; pub mod bindings; pub mod build; @@ -21,6 +22,11 @@ pub enum Cmd { /// Utilities to deploy a Stellar Asset Contract or get its id #[command(subcommand)] Asset(asset::Cmd), + + /// Utilities to manage contract aliases + #[command(subcommand)] + Alias(alias::Cmd), + /// Generate code client bindings for a contract #[command(subcommand)] Bindings(bindings::Cmd), @@ -82,6 +88,9 @@ pub enum Error { #[error(transparent)] Asset(#[from] asset::Error), + #[error(transparent)] + Alias(#[from] alias::Error), + #[error(transparent)] Bindings(#[from] bindings::Error), @@ -132,6 +141,7 @@ impl Cmd { Cmd::Bindings(bindings) => bindings.run().await?, Cmd::Build(build) => build.run()?, Cmd::Extend(extend) => extend.run().await?, + Cmd::Alias(alias) => alias.run(global_args).await?, Cmd::Deploy(deploy) => deploy.run(global_args).await?, Cmd::Id(id) => id.run()?, Cmd::Info(info) => info.run().await?, diff --git a/cmd/soroban-cli/src/config/locator.rs b/cmd/soroban-cli/src/config/locator.rs index 9aefdd9e0..97386cb7d 100644 --- a/cmd/soroban-cli/src/config/locator.rs +++ b/cmd/soroban-cli/src/config/locator.rs @@ -68,6 +68,8 @@ pub enum Error { Json(#[from] serde_json::Error), #[error("cannot access config dir for alias file")] CannotAccessConfigDir, + #[error("cannot access alias config file (no permission or doesn't exist)")] + CannotAccessAliasConfigFile, #[error("cannot parse contract ID {0}: {1}")] CannotParseContractId(String, DecodeError), } @@ -277,6 +279,29 @@ impl Args { Ok(to_file.write_all(content.as_bytes())?) } + pub fn remove_contract_id(&self, network_passphrase: &str, alias: &str) -> Result<(), Error> { + let path = self.alias_path(alias)?; + + if !path.is_file() { + return Err(Error::CannotAccessAliasConfigFile); + } + + 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.remove::(network_passphrase); + + let content = serde_json::to_string(&data)?; + + Ok(to_file.write_all(content.as_bytes())?) + } + pub fn get_contract_id( &self, alias: &str,