1use crate::xdr;
2
3use crate::xdr::{ContractDataEntry, ContractExecutable, ScContractInstance, ScSpecEntry, ScVal};
4
5use soroban_spec::read::FromWasmError;
6pub use soroban_spec_tools::contract as contract_spec;
7
8use crate::commands::global;
9use crate::config::{self, data, locator, network};
10use crate::rpc;
11use crate::utils::rpc::{get_remote_wasm_from_hash, resolve_external_ref_wasm_hash};
12
13#[derive(thiserror::Error, Debug)]
14pub enum Error {
15 #[error("parsing contract spec: {0}")]
16 CannotParseContractSpec(FromWasmError),
17 #[error(transparent)]
18 Rpc(#[from] rpc::Error),
19 #[error("missing result")]
20 MissingResult,
21 #[error(transparent)]
22 Data(#[from] data::Error),
23 #[error(transparent)]
24 Xdr(#[from] xdr::Error),
25 #[error(transparent)]
26 Network(#[from] network::Error),
27 #[error(transparent)]
28 Config(#[from] config::Error),
29 #[error(transparent)]
30 ContractSpec(#[from] contract_spec::Error),
31}
32
33pub async fn get_remote_contract_spec(
36 contract_id: &[u8; 32],
37 locator: &locator::Args,
38 network: &network::Args,
39 global_args: Option<&global::Args>,
40 config: Option<&config::Args>,
41) -> Result<Vec<ScSpecEntry>, Error> {
42 let network = config.map_or_else(
43 || network.get(locator).map_err(Error::from),
44 |c| c.get_network().map_err(Error::from),
45 )?;
46 tracing::trace!(?network);
47 let client = network.rpc_client()?;
48 let r = client.get_contract_data(contract_id).await?;
50 tracing::trace!("{r:?}");
51
52 let ContractDataEntry {
53 val: ScVal::ContractInstance(ScContractInstance { executable, .. }),
54 ..
55 } = r
56 else {
57 return Err(Error::MissingResult);
58 };
59
60 Ok(match executable {
64 ContractExecutable::StellarAsset => {
65 soroban_spec::read::parse_raw(stellar_asset_spec::xdr())?
66 }
67 ContractExecutable::Wasm(hash) => {
68 get_spec_for_wasm_hash(&client, &hash, global_args).await?
69 }
70 ContractExecutable::ExternalRef(external_ref) => {
71 let hash = resolve_external_ref_wasm_hash(&client, &external_ref).await?;
72 get_spec_for_wasm_hash(&client, &hash, global_args).await?
73 }
74 })
75}
76
77async fn get_spec_for_wasm_hash(
78 client: &rpc::Client,
79 hash: &xdr::Hash,
80 global_args: Option<&global::Args>,
81) -> Result<Vec<ScSpecEntry>, Error> {
82 let hash_str = hash.to_string();
83 if let Ok(entries) = data::read_spec(&hash_str) {
84 return Ok(entries);
85 }
86 let raw_wasm = get_remote_wasm_from_hash(client, hash).await?;
87 let res = contract_spec::Spec::new(&raw_wasm)?.spec;
88 if global_args.is_none_or(|a| !a.no_cache) {
89 data::write_spec(&hash_str, &res)?;
90 }
91 Ok(res)
92}