Skip to content

Commit

Permalink
chore: clean up interface for info::shared::fetch
Browse files Browse the repository at this point in the history
Follow-up to #1780,
implementing @leighmcculloch's [suggestions].

  [suggestions]: #1780 (review)
  • Loading branch information
chadoh committed Dec 19, 2024
1 parent 1efc4c5 commit a81e160
Show file tree
Hide file tree
Showing 5 changed files with 113 additions and 53 deletions.
39 changes: 24 additions & 15 deletions cmd/soroban-cli/src/commands/contract/bindings/typescript.rs
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
use std::{ffi::OsString, fmt::Debug, path::PathBuf};

use clap::{command, Parser};
use soroban_spec_tools::contract as contract_spec;
use soroban_spec_tools::contract as spec_tools;
use soroban_spec_typescript::boilerplate::Project;

use crate::print::Print;
use crate::{
commands::{contract::info::shared as wasm_or_contract, global, NetworkRunnable},
commands::{contract::info::shared as contract_spec, global, NetworkRunnable},
config,
};
use soroban_spec_tools::contract::Spec;
Expand All @@ -15,7 +15,7 @@ use soroban_spec_tools::contract::Spec;
#[group(skip)]
pub struct Cmd {
#[command(flatten)]
pub wasm_or_hash_or_contract_id: wasm_or_contract::Args,
pub wasm_or_hash_or_contract_id: contract_spec::Args,
/// Where to place generated project
#[arg(long)]
pub output_dir: PathBuf,
Expand All @@ -39,11 +39,11 @@ pub enum Error {
NotUtf8(OsString),

#[error(transparent)]
Spec(#[from] contract_spec::Error),
Spec(#[from] spec_tools::Error),
#[error("Failed to get file name from path: {0:?}")]
FailedToGetFileName(PathBuf),
#[error(transparent)]
WasmOrContract(#[from] wasm_or_contract::Error),
WasmOrContract(#[from] contract_spec::Error),
#[error(transparent)]
Xdr(#[from] crate::xdr::Error),
}
Expand All @@ -60,13 +60,14 @@ impl NetworkRunnable for Cmd {
) -> Result<(), Error> {
let print = Print::new(global_args.is_some_and(|a| a.quiet));

let (spec, contract_address, network) =
wasm_or_contract::fetch_wasm(&self.wasm_or_hash_or_contract_id, &print).await?;
let contract_spec::Fetched { contract, source } =
contract_spec::fetch(&self.wasm_or_hash_or_contract_id, &print).await?;

let spec = if let Some(spec) = spec {
Spec::new(&spec)?.spec
} else {
soroban_spec::read::parse_raw(&soroban_sdk::token::StellarAssetSpec::spec_xdr())?
let spec = match contract {
contract_spec::Contract::Wasm { wasm_bytes } => Spec::new(&wasm_bytes)?.spec,
contract_spec::Contract::StellarAssetContract => {
soroban_spec::read::parse_raw(&soroban_sdk::token::StellarAssetSpec::spec_xdr())?
}
};

if self.output_dir.is_file() {
Expand All @@ -88,12 +89,20 @@ impl NetworkRunnable for Cmd {
let contract_name = &file_name
.to_str()
.ok_or_else(|| Error::NotUtf8(file_name.to_os_string()))?;
if let Some(contract_address) = contract_address.clone() {
print.infoln(format!("Embedding contract address: {contract_address}"));
}
let (resolved_address, network) = match source {
contract_spec::Source::Contract {
resolved_address,
network,
} => {
print.infoln(format!("Embedding contract address: {resolved_address}"));
(Some(resolved_address), Some(network))
}
contract_spec::Source::Wasm { network, .. } => (None, Some(network)),
contract_spec::Source::File { .. } => (None, None),
};
p.init(
contract_name,
contract_address.as_deref(),
resolved_address.as_deref(),
network.as_ref().map(|n| n.rpc_url.as_ref()),
network.as_ref().map(|n| n.network_passphrase.as_ref()),
&spec,
Expand Down
10 changes: 5 additions & 5 deletions cmd/soroban-cli/src/commands/contract/info/env_meta.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ use crate::{
commands::{
contract::info::{
env_meta::Error::{NoEnvMetaPresent, NoSACEnvMeta},
shared::{self, fetch_wasm, MetasInfoOutput},
shared::{self, fetch, Fetched, MetasInfoOutput},
},
global,
},
Expand Down Expand Up @@ -43,12 +43,12 @@ pub enum Error {
impl Cmd {
pub async fn run(&self, global_args: &global::Args) -> Result<String, Error> {
let print = Print::new(global_args.quiet);
let (bytes, ..) = fetch_wasm(&self.common, &print).await?;
let Fetched { contract, .. } = fetch(&self.common, &print).await?;

let Some(bytes) = bytes else {
return Err(NoSACEnvMeta());
let spec = match contract {
shared::Contract::Wasm { wasm_bytes } => Spec::new(&wasm_bytes)?,
shared::Contract::StellarAssetContract => return Err(NoSACEnvMeta()),
};
let spec = Spec::new(&bytes)?;

let Some(env_meta_base64) = spec.env_meta_base64 else {
return Err(NoEnvMetaPresent());
Expand Down
24 changes: 13 additions & 11 deletions cmd/soroban-cli/src/commands/contract/info/interface.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
use std::fmt::Debug;

use crate::commands::contract::info::interface::Error::NoInterfacePresent;
use crate::commands::contract::info::shared;
use crate::commands::contract::info::shared::fetch_wasm;
use crate::commands::contract::info::shared::{self, fetch, Fetched};
use crate::commands::global;
use crate::print::Print;
use clap::{command, Parser};
Expand Down Expand Up @@ -47,18 +46,21 @@ pub enum Error {
impl Cmd {
pub async fn run(&self, global_args: &global::Args) -> Result<String, Error> {
let print = Print::new(global_args.quiet);
let (bytes, ..) = fetch_wasm(&self.common, &print).await?;
let Fetched { contract, .. } = fetch(&self.common, &print).await?;

let (base64, spec) = if bytes.is_none() {
Spec::spec_to_base64(&soroban_sdk::token::StellarAssetSpec::spec_xdr())?
} else {
let spec = Spec::new(&bytes.unwrap())?;
let (base64, spec) = match contract {
shared::Contract::Wasm { wasm_bytes } => {
let spec = Spec::new(&wasm_bytes)?;

if spec.env_meta_base64.is_none() {
return Err(NoInterfacePresent());
}
if spec.env_meta_base64.is_none() {
return Err(NoInterfacePresent());
}

(spec.spec_base64.unwrap(), spec.spec)
(spec.spec_base64.unwrap(), spec.spec)
}
shared::Contract::StellarAssetContract => {
Spec::spec_to_base64(&soroban_sdk::token::StellarAssetSpec::spec_xdr())?
}
};

let res = match self.output {
Expand Down
11 changes: 5 additions & 6 deletions cmd/soroban-cli/src/commands/contract/info/meta.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
use std::fmt::Debug;

use crate::commands::contract::info::meta::Error::{NoMetaPresent, NoSACMeta};
use crate::commands::contract::info::shared;
use crate::commands::contract::info::shared::{fetch_wasm, MetasInfoOutput};
use crate::commands::contract::info::shared::{self, fetch, Fetched, MetasInfoOutput};
use crate::commands::global;
use crate::print::Print;
use clap::{command, Parser};
Expand Down Expand Up @@ -36,12 +35,12 @@ pub enum Error {
impl Cmd {
pub async fn run(&self, global_args: &global::Args) -> Result<String, Error> {
let print = Print::new(global_args.quiet);
let (bytes, ..) = fetch_wasm(&self.common, &print).await?;
let Fetched { contract, .. } = fetch(&self.common, &print).await?;

let Some(bytes) = bytes else {
return Err(NoSACMeta());
let spec = match contract {
shared::Contract::Wasm { wasm_bytes } => Spec::new(&wasm_bytes)?,
shared::Contract::StellarAssetContract => return Err(NoSACMeta()),
};
let spec = Spec::new(&bytes)?;

let Some(meta_base64) = spec.meta_base64 else {
return Err(NoMetaPresent());
Expand Down
82 changes: 66 additions & 16 deletions cmd/soroban-cli/src/commands/contract/info/shared.rs
Original file line number Diff line number Diff line change
Expand Up @@ -76,23 +76,58 @@ pub enum Error {
#[error("provided wasm hash is invalid {0:?}")]
InvalidWasmHash(String),
#[error("must provide one of --wasm, --wasm-hash, or --contract-id")]
MalformedWasmOrWasmHashOrContractId,
MissingArg,
#[error(transparent)]
Rpc(#[from] soroban_rpc::Error),
#[error(transparent)]
Locator(#[from] locator::Error),
}

pub async fn fetch_wasm(
args: &Args,
print: &Print,
) -> Result<(Option<Vec<u8>>, Option<String>, Option<Network>), Error> {
pub struct Fetched {
pub contract: Contract,
pub source: Source,
}

pub enum Contract {
Wasm { wasm_bytes: Vec<u8> },
StellarAssetContract,
}

pub enum Source {
File {
path: PathBuf,
},
Wasm {
hash: String,
network: Network,
},
Contract {
resolved_address: String,
network: Network,
},
}

impl Source {
pub fn network(&self) -> Option<&Network> {
match self {
Source::File { .. } => None,
Source::Wasm { ref network, .. } | Source::Contract { ref network, .. } => {
Some(network)
}
}
}
}

pub async fn fetch(args: &Args, print: &Print) -> Result<Fetched, Error> {
// Check if a local WASM file path is provided
if let Some(path) = &args.wasm {
// Read the WASM file and return its contents
print.infoln("Loading contract spec from file...");
let wasm_bytes = wasm::Args { wasm: path.clone() }.read()?;
return Ok((Some(wasm_bytes), None, None));
return Ok(Fetched {
contract: Contract::Wasm { wasm_bytes },
source: Source::File { path: path.clone() },
});
}

// If no local wasm, then check for wasm_hash and fetch from the network
Expand All @@ -116,22 +151,37 @@ pub async fn fetch_wasm(
print.globeln(format!(
"Downloading contract spec for wasm hash: {wasm_hash}"
));
Ok((
Some(get_remote_wasm_from_hash(&client, &hash).await?),
None,
Some(network.clone()),
))
let wasm_bytes = get_remote_wasm_from_hash(&client, &hash).await?;
Ok(Fetched {
contract: Contract::Wasm { wasm_bytes },
source: Source::Wasm {
hash: wasm_hash.clone(),
network: network.clone(),
},
})
} else if let Some(contract_id) = &args.contract_id {
let contract_id =
contract_id.resolve_contract_id(&args.locator, &network.network_passphrase)?;
let contract_address = xdr::ScAddress::Contract(xdr::Hash(contract_id.0)).to_string();
print.globeln(format!("Downloading contract spec: {contract_address}"));
let derived_address = xdr::ScAddress::Contract(xdr::Hash(contract_id.0)).to_string();
print.globeln(format!("Downloading contract spec: {derived_address}"));
let res = wasm::fetch_from_contract(&contract_id, network).await;
if let Some(ContractIsStellarAsset) = res.as_ref().err() {
return Ok((None, Some(contract_address), Some(network.clone())));
return Ok(Fetched {
contract: Contract::StellarAssetContract,
source: Source::Contract {
resolved_address: derived_address,
network: network.clone(),
},
});
}
Ok((Some(res?), Some(contract_address), Some(network.clone())))
Ok(Fetched {
contract: Contract::Wasm { wasm_bytes: res? },
source: Source::Contract {
resolved_address: derived_address,
network: network.clone(),
},
})
} else {
return Err(Error::MalformedWasmOrWasmHashOrContractId);
return Err(Error::MissingArg);
}
}

0 comments on commit a81e160

Please sign in to comment.