Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(ts-bindings): allow generating from wasm hash #1780

Merged
merged 2 commits into from
Dec 12, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
35 changes: 18 additions & 17 deletions FULL_HELP_DOCS.md
Original file line number Diff line number Diff line change
Expand Up @@ -290,20 +290,21 @@ Generate Rust bindings

Generate a TypeScript / JavaScript package

**Usage:** `stellar contract bindings typescript [OPTIONS] --output-dir <OUTPUT_DIR>`
**Usage:** `stellar contract bindings typescript [OPTIONS] --output-dir <OUTPUT_DIR> <--wasm <WASM>|--wasm-hash <WASM_HASH>|--contract-id <CONTRACT_ID>>`

###### **Options:**

* `--wasm <WASM>` — Path to wasm file on local filesystem. You must either include this OR `--contract-id`
* `--contract-id <CONTRACT_ID>` — A contract ID/address on a network (if no network settings provided, Testnet will be assumed). You must either include this OR `--wasm`
* `--output-dir <OUTPUT_DIR>` — Where to place generated project
* `--overwrite` — Whether to overwrite output directory if it already exists
* `--wasm <WASM>` — Wasm file path on local filesystem. Provide this OR `--wasm-hash` OR `--contract-id`
* `--wasm-hash <WASM_HASH>` — Hash of Wasm blob on a network. Provide this OR `--wasm` OR `--contract-id`
* `--contract-id <CONTRACT_ID>` — Contract ID/alias on a network. Provide this OR `--wasm-hash` OR `--wasm`
* `--rpc-url <RPC_URL>` — RPC server endpoint
* `--rpc-header <RPC_HEADERS>` — RPC Header(s) to include in requests to the RPC provider
* `--network-passphrase <NETWORK_PASSPHRASE>` — Network passphrase to sign the transaction sent to the rpc server
* `--network <NETWORK>` — Name of network to use from config
* `--global` — Use global config
* `--config-dir <CONFIG_DIR>` — Location of config directory, default is "."
* `--output-dir <OUTPUT_DIR>` — Where to place generated project
* `--overwrite` — Whether to overwrite output directory if it already exists



Expand Down Expand Up @@ -525,13 +526,13 @@ The data outputted by this command is a stream of `SCSpecEntry` XDR values. See

Outputs no data when no data is present in the contract.

**Usage:** `stellar contract info interface [OPTIONS] <--wasm <WASM>|--wasm-hash <WASM_HASH>|--id <CONTRACT_ID>>`
**Usage:** `stellar contract info interface [OPTIONS] <--wasm <WASM>|--wasm-hash <WASM_HASH>|--contract-id <CONTRACT_ID>>`

###### **Options:**

* `--wasm <WASM>` — Wasm file to extract the data from
* `--wasm-hash <WASM_HASH>` — Wasm hash to get the data for
* `--id <CONTRACT_ID>` — Contract id or contract alias to get the data for
* `--wasm <WASM>` — Wasm file path on local filesystem. Provide this OR `--wasm-hash` OR `--contract-id`
* `--wasm-hash <WASM_HASH>` — Hash of Wasm blob on a network. Provide this OR `--wasm` OR `--contract-id`
* `--contract-id <CONTRACT_ID>` — Contract ID/alias on a network. Provide this OR `--wasm-hash` OR `--wasm`
* `--rpc-url <RPC_URL>` — RPC server endpoint
* `--rpc-header <RPC_HEADERS>` — RPC Header(s) to include in requests to the RPC provider
* `--network-passphrase <NETWORK_PASSPHRASE>` — Network passphrase to sign the transaction sent to the rpc server
Expand Down Expand Up @@ -565,13 +566,13 @@ The data outputted by this command is a stream of `SCMetaEntry` XDR values. See

Outputs no data when no data is present in the contract.

**Usage:** `stellar contract info meta [OPTIONS] <--wasm <WASM>|--wasm-hash <WASM_HASH>|--id <CONTRACT_ID>>`
**Usage:** `stellar contract info meta [OPTIONS] <--wasm <WASM>|--wasm-hash <WASM_HASH>|--contract-id <CONTRACT_ID>>`

###### **Options:**

* `--wasm <WASM>` — Wasm file to extract the data from
* `--wasm-hash <WASM_HASH>` — Wasm hash to get the data for
* `--id <CONTRACT_ID>` — Contract id or contract alias to get the data for
* `--wasm <WASM>` — Wasm file path on local filesystem. Provide this OR `--wasm-hash` OR `--contract-id`
* `--wasm-hash <WASM_HASH>` — Hash of Wasm blob on a network. Provide this OR `--wasm` OR `--contract-id`
* `--contract-id <CONTRACT_ID>` — Contract ID/alias on a network. Provide this OR `--wasm-hash` OR `--wasm`
* `--rpc-url <RPC_URL>` — RPC server endpoint
* `--rpc-header <RPC_HEADERS>` — RPC Header(s) to include in requests to the RPC provider
* `--network-passphrase <NETWORK_PASSPHRASE>` — Network passphrase to sign the transaction sent to the rpc server
Expand Down Expand Up @@ -605,13 +606,13 @@ The data outputted by this command is a stream of `SCEnvMetaEntry` XDR values. S

Outputs no data when no data is present in the contract.

**Usage:** `stellar contract info env-meta [OPTIONS] <--wasm <WASM>|--wasm-hash <WASM_HASH>|--id <CONTRACT_ID>>`
**Usage:** `stellar contract info env-meta [OPTIONS] <--wasm <WASM>|--wasm-hash <WASM_HASH>|--contract-id <CONTRACT_ID>>`

###### **Options:**

* `--wasm <WASM>` — Wasm file to extract the data from
* `--wasm-hash <WASM_HASH>` — Wasm hash to get the data for
* `--id <CONTRACT_ID>` — Contract id or contract alias to get the data for
* `--wasm <WASM>` — Wasm file path on local filesystem. Provide this OR `--wasm-hash` OR `--contract-id`
* `--wasm-hash <WASM_HASH>` — Hash of Wasm blob on a network. Provide this OR `--wasm` OR `--contract-id`
* `--contract-id <CONTRACT_ID>` — Contract ID/alias on a network. Provide this OR `--wasm-hash` OR `--wasm`
* `--rpc-url <RPC_URL>` — RPC server endpoint
* `--rpc-header <RPC_HEADERS>` — RPC Header(s) to include in requests to the RPC provider
* `--network-passphrase <NETWORK_PASSPHRASE>` — Network passphrase to sign the transaction sent to the rpc server
Expand Down
2 changes: 1 addition & 1 deletion cmd/crates/soroban-spec-typescript/ts-tests/initialize.sh
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ function bind() {
}
function bind_all() {
bind --contract-id $(cat contract-id-custom-types.txt) test-custom-types
bind --wasm ../../../../target/wasm32-unknown-unknown/test-wasms/test_constructor.wasm test-constructor
bind --wasm-hash $(cat contract-wasm-hash-constructor.txt) test-constructor
}

fund_all
Expand Down
2 changes: 1 addition & 1 deletion cmd/soroban-cli/src/commands/contract/bindings.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ pub enum Cmd {
Rust(rust::Cmd),

/// Generate a TypeScript / JavaScript package
Typescript(typescript::Cmd),
Typescript(Box<typescript::Cmd>),

/// Generate Python bindings
Python(python::Cmd),
Expand Down
97 changes: 18 additions & 79 deletions cmd/soroban-cli/src/commands/contract/bindings/typescript.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,43 +2,30 @@ use std::{ffi::OsString, fmt::Debug, path::PathBuf};

use clap::{command, Parser};
use soroban_spec_tools::contract as contract_spec;
use soroban_spec_typescript::{self as typescript, boilerplate::Project};
use stellar_strkey::DecodeError;
use soroban_spec_typescript::boilerplate::Project;

use crate::print::Print;
use crate::wasm;
use crate::{
commands::{contract::fetch, global, NetworkRunnable},
config::{self, locator, network},
get_spec::{self, get_remote_contract_spec},
xdr::{Hash, ScAddress},
commands::{contract::info::shared as wasm_or_contract, global, NetworkRunnable},
config,
};
use soroban_spec_tools::contract::Spec;

#[derive(Parser, Debug, Clone)]
#[group(skip)]
pub struct Cmd {
/// Path to wasm file on local filesystem. You must either include this OR `--contract-id`.
#[arg(long)]
pub wasm: Option<std::path::PathBuf>,
/// A contract ID/address on a network (if no network settings provided, Testnet will be assumed). You must either include this OR `--wasm`.
#[arg(long, visible_alias = "id")]
pub contract_id: Option<String>,
#[command(flatten)]
pub wasm_or_hash_or_contract_id: wasm_or_contract::Args,
/// Where to place generated project
#[arg(long)]
pub output_dir: PathBuf,
/// Whether to overwrite output directory if it already exists
#[arg(long)]
pub overwrite: bool,
#[command(flatten)]
pub network: network::Args,
#[command(flatten)]
pub locator: locator::Args,
}

#[derive(thiserror::Error, Debug)]
pub enum Error {
#[error("failed generate TS from file: {0}")]
GenerateTSFromFile(typescript::GenerateFromFileError),
#[error(transparent)]
Io(#[from] std::io::Error),

Expand All @@ -51,28 +38,12 @@ pub enum Error {
#[error("--output-dir filepath not representable as utf-8: {0:?}")]
NotUtf8(OsString),

#[error("must include either --wasm or --contract-id")]
MissingWasmOrContractId,

#[error(transparent)]
Network(#[from] network::Error),

#[error(transparent)]
Locator(#[from] locator::Error),
#[error(transparent)]
Fetch(#[from] fetch::Error),
#[error(transparent)]
Spec(#[from] contract_spec::Error),
#[error(transparent)]
Wasm(#[from] wasm::Error),
#[error("Failed to get file name from path: {0:?}")]
FailedToGetFileName(PathBuf),
#[error("cannot parse contract ID {0}: {1}")]
CannotParseContractId(String, DecodeError),
#[error(transparent)]
UtilsError(#[from] get_spec::Error),
#[error(transparent)]
Config(#[from] config::Error),
WasmOrContract(#[from] wasm_or_contract::Error),
}

#[async_trait::async_trait]
Expand All @@ -83,51 +54,19 @@ impl NetworkRunnable for Cmd {
async fn run_against_rpc_server(
&self,
global_args: Option<&global::Args>,
config: Option<&config::Args>,
_config: Option<&config::Args>,
) -> Result<(), Error> {
let print = Print::new(global_args.is_some_and(|a| a.quiet));

let (spec, contract_address, rpc_url, network_passphrase) = if let Some(wasm) = &self.wasm {
print.infoln("Loading contract spec from file...");
let wasm: wasm::Args = wasm.into();
(wasm.parse()?.spec, None, None, None)
} else {
let contract_id = self
.contract_id
.as_ref()
.ok_or(Error::MissingWasmOrContractId)?;

let network = self.network.get(&self.locator).ok().unwrap_or_else(|| {
network::DEFAULTS
.get("testnet")
.expect("no network specified and testnet network not found")
.into()
});
print.infoln(format!("Network: {}", network.network_passphrase));
let (spec, contract_address, network) =
wasm_or_contract::fetch_wasm(&self.wasm_or_hash_or_contract_id, &print).await?;

let contract_id = self
.locator
.resolve_contract_id(contract_id, &network.network_passphrase)?
.0;

let contract_address = ScAddress::Contract(Hash(contract_id)).to_string();
print.globeln(format!("Downloading contract spec: {contract_address}"));

(
get_remote_contract_spec(
&contract_id,
&self.locator,
&self.network,
global_args,
config,
)
.await
.map_err(Error::from)?,
Some(contract_address),
Some(network.rpc_url),
Some(network.network_passphrase),
)
let spec = if let Some(spec) = spec {
Spec::new(&spec)?
} else {
Spec::new(&soroban_sdk::token::StellarAssetSpec::spec_xdr())?
};

if self.output_dir.is_file() {
return Err(Error::IsFile(self.output_dir.clone()));
}
Expand All @@ -153,9 +92,9 @@ impl NetworkRunnable for Cmd {
p.init(
contract_name,
contract_address.as_deref(),
rpc_url.as_deref(),
network_passphrase.as_deref(),
&spec,
network.as_ref().map(|n| n.rpc_url.as_ref()),
network.as_ref().map(|n| n.network_passphrase.as_ref()),
&spec.spec,
)?;
print.checkln("Generated!");
print.infoln(format!(
Expand Down
12 changes: 7 additions & 5 deletions cmd/soroban-cli/src/commands/contract/info.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
use std::fmt::Debug;

use crate::commands::global;

pub mod env_meta;
pub mod interface;
pub mod meta;
mod shared;
pub mod shared;

#[derive(Debug, clap::Subcommand)]
pub enum Cmd {
Expand Down Expand Up @@ -60,11 +62,11 @@ pub enum Error {
}

impl Cmd {
pub async fn run(&self) -> Result<(), Error> {
pub async fn run(&self, global_args: &global::Args) -> Result<(), Error> {
let result = match &self {
Cmd::Interface(interface) => interface.run().await?,
Cmd::Meta(meta) => meta.run().await?,
Cmd::EnvMeta(env_meta) => env_meta.run().await?,
Cmd::Interface(interface) => interface.run(global_args).await?,
Cmd::Meta(meta) => meta.run(global_args).await?,
Cmd::EnvMeta(env_meta) => env_meta.run(global_args).await?,
};
println!("{result}");
Ok(())
Expand Down
15 changes: 10 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 @@ -6,10 +6,14 @@ use soroban_spec_tools::contract;
use soroban_spec_tools::contract::Spec;

use crate::{
commands::contract::info::{
env_meta::Error::{NoEnvMetaPresent, NoSACEnvMeta},
shared::{self, fetch_wasm, MetasInfoOutput},
commands::{
contract::info::{
env_meta::Error::{NoEnvMetaPresent, NoSACEnvMeta},
shared::{self, fetch_wasm, MetasInfoOutput},
},
global,
},
print::Print,
xdr::{ScEnvMetaEntry, ScEnvMetaEntryInterfaceVersion},
};

Expand Down Expand Up @@ -37,8 +41,9 @@ pub enum Error {
}

impl Cmd {
pub async fn run(&self) -> Result<String, Error> {
let bytes = fetch_wasm(&self.common).await?;
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 Some(bytes) = bytes else {
return Err(NoSACEnvMeta());
Expand Down
7 changes: 5 additions & 2 deletions cmd/soroban-cli/src/commands/contract/info/interface.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ 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::global;
use crate::print::Print;
use clap::{command, Parser};
use soroban_spec_rust::ToFormattedString;
use soroban_spec_tools::contract;
Expand Down Expand Up @@ -43,8 +45,9 @@ pub enum Error {
}

impl Cmd {
pub async fn run(&self) -> Result<String, Error> {
let bytes = fetch_wasm(&self.common).await?;
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 (base64, spec) = if bytes.is_none() {
Spec::spec_to_base64(&soroban_sdk::token::StellarAssetSpec::spec_xdr())?
Expand Down
7 changes: 5 additions & 2 deletions cmd/soroban-cli/src/commands/contract/info/meta.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ 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::global;
use crate::print::Print;
use clap::{command, Parser};
use soroban_spec_tools::contract;
use soroban_spec_tools::contract::Spec;
Expand Down Expand Up @@ -32,8 +34,9 @@ pub enum Error {
}

impl Cmd {
pub async fn run(&self) -> Result<String, Error> {
let bytes = fetch_wasm(&self.common).await?;
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 Some(bytes) = bytes else {
return Err(NoSACMeta());
Expand Down
Loading
Loading