Skip to content
Closed
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
63 changes: 47 additions & 16 deletions cmd/soroban-cli/src/commands/contract/deploy/asset.rs
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,15 @@ pub struct Cmd {
/// Build the transaction and only write the base64 xdr to stdout
#[arg(long, help_heading = HEADING_TRANSACTION)]
pub build_only: bool,

/// Simulate the transaction only without signing or submitting to the network
#[arg(
long,
visible_alias = "is-view",
visible_alias = "sim-only",
help_heading = HEADING_TRANSACTION
)]
pub simulate_only: bool,
}

impl Cmd {
Expand All @@ -115,23 +124,25 @@ impl Cmd {
TxnEnvelopeResult::Res(contract) => {
let network = self.config.get_network()?;

if let Some(alias) = self.alias.clone() {
if let Some(existing_contract) = self
.config
.locator
.get_contract_id(&alias, &network.network_passphrase)?
{
let print = Print::new(global_args.quiet);
print.warnln(format!(
"Overwriting existing contract id: {existing_contract}"
));
if !self.simulate_only {
if let Some(alias) = self.alias.clone() {
if let Some(existing_contract) = self
.config
.locator
.get_contract_id(&alias, &network.network_passphrase)?
{
let print = Print::new(global_args.quiet);
print.warnln(format!(
"Overwriting existing contract id: {existing_contract}"
));
}

self.config.locator.save_contract_id(
&network.network_passphrase,
&contract,
&alias,
)?;
}

self.config.locator.save_contract_id(
&network.network_passphrase,
&contract,
&alias,
)?;
}

println!("{contract}");
Expand Down Expand Up @@ -186,6 +197,26 @@ impl Cmd {
return Ok(TxnResult::Txn(Box::new(tx)));
}

if self.simulate_only {
print.infoln("Simulating transaction…");
let assembled = crate::assembled::simulate_and_assemble_transaction(
&client,
&tx,
self.resources.resource_config(),
self.resources.resource_fee,
None,
)
.await?;
if let Ok(events) = assembled.sim_res.events() {
crate::log::event::all(&events);
}
let fee = assembled.transaction().fee;
print.checkln(format!(
"Simulation successful! Estimated fee: {fee} stroops"
));
return Ok(TxnResult::Res(stellar_strkey::Contract(contract_id.0)));
}

sim_sign_and_send_tx::<Error>(
&client,
&tx,
Expand Down
75 changes: 61 additions & 14 deletions cmd/soroban-cli/src/commands/contract/deploy/wasm.rs
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,15 @@ pub struct Cmd {
/// Build the transaction and only write the base64 xdr to stdout
#[arg(long, help_heading = HEADING_TRANSACTION)]
pub build_only: bool,

/// Simulate the transaction only without signing or submitting to the network
#[arg(
long,
visible_alias = "is-view",
visible_alias = "sim-only",
help_heading = HEADING_TRANSACTION
)]
pub simulate_only: bool,
/// If provided, will be passed to the contract's `__constructor` function with provided arguments for that function as `--arg-name value`
#[arg(last = true, id = "CONTRACT_CONSTRUCTOR_ARGS")]
pub slop: Vec<OsString>,
Expand Down Expand Up @@ -262,23 +271,25 @@ impl Cmd {
TxnEnvelopeResult::Res(contract) => {
let network = cmd.config.get_network()?;

if let Some(alias) = cmd.alias.clone() {
if let Some(existing_contract) = cmd
.config
.locator
.get_contract_id(&alias, &network.network_passphrase)?
{
let print = Print::new(global_args.quiet);
print.warnln(format!(
if !cmd.simulate_only {
if let Some(alias) = cmd.alias.clone() {
if let Some(existing_contract) = cmd
.config
.locator
.get_contract_id(&alias, &network.network_passphrase)?
{
let print = Print::new(global_args.quiet);
print.warnln(format!(
"Overwriting existing alias '{alias}' that currently links to contract ID: {existing_contract}"
));
}
}

cmd.config.locator.save_contract_id(
&network.network_passphrase,
&contract,
&alias,
)?;
cmd.config.locator.save_contract_id(
&network.network_passphrase,
&contract,
&alias,
)?;
}
}

println!("{contract}");
Expand Down Expand Up @@ -346,6 +357,7 @@ impl Cmd {
auth_mode: self.auth_mode.clone(),
ignore_checks: self.ignore_checks,
build_only: is_build,
simulate_only: self.simulate_only,
package: None,
build_args: build::BuildArgs::default(),
}
Expand Down Expand Up @@ -445,6 +457,26 @@ impl Cmd {
return Ok(TxnResult::Txn(txn));
}

if self.simulate_only {
print.infoln("Simulating transaction…");
let assembled = crate::assembled::simulate_and_assemble_transaction(
&client,
&txn,
self.resources.resource_config(),
self.resources.resource_fee,
self.auth_mode.to_rpc(),
)
.await?;
if let Ok(events) = assembled.sim_res.events() {
crate::log::event::all(&events);
}
let fee = assembled.transaction().fee;
print.checkln(format!(
"Simulation successful! Estimated fee: {fee} stroops"
));
return Ok(TxnResult::Res(contract_id));
}

sim_sign_and_send_tx::<Error>(
&client,
&txn,
Expand Down Expand Up @@ -597,4 +629,19 @@ mod tests {

assert_eq!(reserved_package_alias(Some(&alias), &contracts), None);
}

#[test]
fn parse_simulate_only_and_aliases() {
let cmd =
Cmd::try_parse_from(["deploy", "--source-account", "alice", "--wasm", "test.wasm", "--simulate-only"]).unwrap();
assert!(cmd.simulate_only);

let cmd_is_view =
Cmd::try_parse_from(["deploy", "--source-account", "alice", "--wasm", "test.wasm", "--is-view"]).unwrap();
assert!(cmd_is_view.simulate_only);

let cmd_sim_only =
Cmd::try_parse_from(["deploy", "--source-account", "alice", "--wasm", "test.wasm", "--sim-only"]).unwrap();
assert!(cmd_sim_only.simulate_only);
}
}
44 changes: 44 additions & 0 deletions cmd/soroban-cli/src/commands/contract/upload.rs
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,15 @@ pub struct Cmd {
#[arg(long, help_heading = HEADING_TRANSACTION)]
pub build_only: bool,

/// Simulate the transaction only without signing or submitting to the network
#[arg(
long,
visible_alias = "is-view",
visible_alias = "sim-only",
help_heading = HEADING_TRANSACTION
)]
pub simulate_only: bool,

/// Package to build when --wasm is not provided
#[arg(long, help_heading = "Build Options", conflicts_with = "wasm")]
pub package: Option<String>,
Expand Down Expand Up @@ -267,6 +276,26 @@ impl Cmd {
return Ok(TxnResult::Txn(Box::new(tx_without_preflight)));
}

if self.simulate_only {
print.infoln("Simulating transaction…");
let assembled = crate::assembled::simulate_and_assemble_transaction(
&client,
&tx_without_preflight,
self.resources.resource_config(),
self.resources.resource_fee,
self.auth_mode.to_rpc(),
)
.await?;
if let Ok(events) = assembled.sim_res.events() {
crate::log::event::all(&events);
}
let fee = assembled.transaction().fee;
print.checkln(format!(
"Simulation successful! Estimated fee: {fee} stroops"
));
return Ok(TxnResult::Res(hash));
}

let should_check = true;

if should_check {
Expand Down Expand Up @@ -441,4 +470,19 @@ mod tests {
// "rc" detection (used to gate the release-candidate check) still works.
assert!(version.contains("rc"));
}

#[test]
fn parse_simulate_only_and_aliases() {
let cmd =
Cmd::try_parse_from(["upload", "--source-account", "alice", "--wasm", "test.wasm", "--simulate-only"]).unwrap();
assert!(cmd.simulate_only);

let cmd_is_view =
Cmd::try_parse_from(["upload", "--source-account", "alice", "--wasm", "test.wasm", "--is-view"]).unwrap();
assert!(cmd_is_view.simulate_only);

let cmd_sim_only =
Cmd::try_parse_from(["upload", "--source-account", "alice", "--wasm", "test.wasm", "--sim-only"]).unwrap();
assert!(cmd_sim_only.simulate_only);
}
}