From b5546bb15492d63e1c8ca6a6a473d897adc1d013 Mon Sep 17 00:00:00 2001 From: Bot Date: Fri, 23 Oct 2026 18:26:27 +0800 Subject: [PATCH] feat(contract): add --ledgers-to-extend and --ledgers-to-expire to upload, deploy, and restore --- .../src/commands/contract/deploy/asset.rs | 31 +++++++- .../src/commands/contract/deploy/wasm.rs | 55 +++++++++++++- .../src/commands/contract/extend.rs | 2 +- .../src/commands/contract/restore.rs | 15 +++- .../src/commands/contract/upload.rs | 71 ++++++++++++++++++- 5 files changed, 168 insertions(+), 6 deletions(-) diff --git a/cmd/soroban-cli/src/commands/contract/deploy/asset.rs b/cmd/soroban-cli/src/commands/contract/deploy/asset.rs index 8b52e8bfe0..d76d4a03d2 100644 --- a/cmd/soroban-cli/src/commands/contract/deploy/asset.rs +++ b/cmd/soroban-cli/src/commands/contract/deploy/asset.rs @@ -15,11 +15,13 @@ use std::{array::TryFromSliceError, fmt::Debug, num::ParseIntError}; use crate::commands::tx::fetch; use crate::{ commands::{ + contract::{self, extend}, global, txn_result::{TxnEnvelopeResult, TxnResult}, HEADING_TRANSACTION, }, - config::{self, data, network, token::UnresolvedToken}, + config::{self, alias, data, network, token::UnresolvedToken}, + key, rpc::Error as SorobanRpcError, tx::builder, }; @@ -64,6 +66,9 @@ pub enum Error { #[error(transparent)] Fetch(#[from] fetch::Error), + + #[error(transparent)] + Extend(#[from] extend::Error), } impl From for Error { @@ -94,6 +99,10 @@ pub struct Cmd { /// Build the transaction and only write the base64 xdr to stdout #[arg(long, help_heading = HEADING_TRANSACTION)] pub build_only: bool, + + /// Number of ledgers to extend the contract instance TTL after deploying + #[arg(long, visible_alias = "ledgers-to-expire")] + pub ledgers_to_extend: Option, } impl Cmd { @@ -199,6 +208,26 @@ impl Cmd { ) .await?; + if let Some(ledgers_to_extend) = self.ledgers_to_extend { + extend::Cmd { + ledgers_to_extend, + ttl_ledger_only: false, + key: key::Args { + contract_id: Some(alias::UnresolvedContract::Resolved(contract_id)), + key: None, + key_xdr: None, + wasm: None, + wasm_hash: None, + durability: contract::Durability::Persistent, + }, + config: config.clone(), + resources: self.resources.clone(), + build_only: self.build_only, + } + .execute(config, quiet, no_cache) + .await?; + } + if let Some(url) = utils::lab_url_for_contract(&network, &contract_id) { print.linkln(url); } diff --git a/cmd/soroban-cli/src/commands/contract/deploy/wasm.rs b/cmd/soroban-cli/src/commands/contract/deploy/wasm.rs index 4655a2c123..7706a6e1d7 100644 --- a/cmd/soroban-cli/src/commands/contract/deploy/wasm.rs +++ b/cmd/soroban-cli/src/commands/contract/deploy/wasm.rs @@ -22,12 +22,13 @@ use crate::commands::tx::fetch; use crate::utils::XDR_DEPTH_LIMIT; use crate::{ commands::{ - contract::{self, arg_parsing, build, id::wasm::get_contract_id, upload}, + contract::{self, arg_parsing, build, extend, id::wasm::get_contract_id, upload}, global, txn_result::{TxnEnvelopeResult, TxnResult}, HEADING_TRANSACTION, }, - config::{self, data, locator, network}, + config::{self, alias, data, locator, network}, + key, print::Print, rpc, utils::{self, rpc::get_remote_wasm_from_hash}, @@ -72,6 +73,9 @@ pub struct Cmd { /// Build the transaction and only write the base64 xdr to stdout #[arg(long, help_heading = HEADING_TRANSACTION)] pub build_only: bool, + /// Number of ledgers to extend the contract instance TTL after deploying + #[arg(long, visible_alias = "ledgers-to-expire")] + pub ledgers_to_extend: Option, /// 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, @@ -87,6 +91,9 @@ pub enum Error { #[error(transparent)] Install(#[from] upload::Error), + #[error(transparent)] + Extend(#[from] extend::Error), + #[error("error parsing int: {0}")] ParseIntError(#[from] ParseIntError), @@ -346,6 +353,7 @@ impl Cmd { auth_mode: self.auth_mode.clone(), ignore_checks: self.ignore_checks, build_only: is_build, + ledgers_to_extend: self.ledgers_to_extend, package: None, build_args: build::BuildArgs::default(), } @@ -457,6 +465,26 @@ impl Cmd { ) .await?; + if let Some(ledgers_to_extend) = self.ledgers_to_extend { + extend::Cmd { + ledgers_to_extend, + ttl_ledger_only: false, + key: key::Args { + contract_id: Some(alias::UnresolvedContract::Resolved(contract_id)), + key: None, + key_xdr: None, + wasm: None, + wasm_hash: None, + durability: contract::Durability::Persistent, + }, + config: config.clone(), + resources: self.resources.clone(), + build_only: self.build_only, + } + .execute(config, quiet, no_cache) + .await?; + } + if let Some(url) = utils::lab_url_for_contract(&network, &contract_id) { print.linkln(url); } @@ -597,4 +625,27 @@ mod tests { assert_eq!(reserved_package_alias(Some(&alias), &contracts), None); } + + #[test] + fn parse_ledgers_to_extend_and_expire_alias() { + let cmd = Cmd::try_parse_from([ + "deploy", + "--wasm", + "test.wasm", + "--ledgers-to-extend", + "5000", + ]) + .unwrap(); + assert_eq!(cmd.ledgers_to_extend, Some(5000)); + + let cmd_alias = Cmd::try_parse_from([ + "deploy", + "--wasm", + "test.wasm", + "--ledgers-to-expire", + "6000", + ]) + .unwrap(); + assert_eq!(cmd_alias.ledgers_to_extend, Some(6000)); + } } diff --git a/cmd/soroban-cli/src/commands/contract/extend.rs b/cmd/soroban-cli/src/commands/contract/extend.rs index c4a32234cb..2fde3a87b8 100644 --- a/cmd/soroban-cli/src/commands/contract/extend.rs +++ b/cmd/soroban-cli/src/commands/contract/extend.rs @@ -32,7 +32,7 @@ use crate::{ #[group(skip)] pub struct Cmd { /// Number of ledgers to extend the entries - #[arg(long, required = true)] + #[arg(long, visible_alias = "ledgers-to-expire", required = true)] pub ledgers_to_extend: u32, /// Only print the new Time To Live ledger diff --git a/cmd/soroban-cli/src/commands/contract/restore.rs b/cmd/soroban-cli/src/commands/contract/restore.rs index a457bac9fc..999c83d290 100644 --- a/cmd/soroban-cli/src/commands/contract/restore.rs +++ b/cmd/soroban-cli/src/commands/contract/restore.rs @@ -34,7 +34,7 @@ pub struct Cmd { pub key: key::Args, /// Number of ledgers to extend the entry - #[arg(long)] + #[arg(long, visible_alias = "ledgers-to-expire")] pub ledgers_to_extend: Option, /// Only print the new Time To Live ledger @@ -547,4 +547,17 @@ mod tests { let result = parse_changes(&changes); assert_eq!(result, None); } + + #[test] + fn test_parse_ledgers_to_expire_alias() { + let cmd = Cmd::try_parse_from([ + "restore", + "--id", + "CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD2KM", + "--ledgers-to-expire", + "4000", + ]) + .unwrap(); + assert_eq!(cmd.ledgers_to_extend, Some(4000)); + } } diff --git a/cmd/soroban-cli/src/commands/contract/upload.rs b/cmd/soroban-cli/src/commands/contract/upload.rs index a61120b8db..f9831db089 100644 --- a/cmd/soroban-cli/src/commands/contract/upload.rs +++ b/cmd/soroban-cli/src/commands/contract/upload.rs @@ -10,7 +10,7 @@ use crate::xdr::{ }; use clap::Parser; -use super::{build, restore}; +use super::{build, extend, restore}; use crate::commands::tx::fetch; use crate::{ commands::{ @@ -58,6 +58,10 @@ pub struct Cmd { #[arg(long, help_heading = HEADING_TRANSACTION)] pub build_only: bool, + /// Number of ledgers to extend the contract code TTL after uploading + #[arg(long, visible_alias = "ledgers-to-expire")] + pub ledgers_to_extend: Option, + /// Package to build when --wasm is not provided #[arg(long, help_heading = "Build Options", conflicts_with = "wasm")] pub package: Option, @@ -91,6 +95,9 @@ pub enum Error { #[error(transparent)] Restore(#[from] restore::Error), + #[error(transparent)] + Extend(#[from] extend::Error), + #[error("cannot parse WASM file {wasm}: {error}")] CannotParseWasm { wasm: std::path::PathBuf, @@ -291,6 +298,25 @@ impl Cmd { // exists. if code.ext.ne(&ContractCodeEntryExt::V0) { print.infoln("Skipping install because wasm already installed"); + if let Some(ledgers_to_extend) = self.ledgers_to_extend { + extend::Cmd { + ledgers_to_extend, + ttl_ledger_only: false, + key: key::Args { + contract_id: None, + key: None, + key_xdr: None, + wasm: Some(wasm_path.clone()), + wasm_hash: None, + durability: super::Durability::Persistent, + }, + config: config.clone(), + resources: self.resources.clone(), + build_only: self.build_only, + } + .execute(config, quiet, no_cache) + .await?; + } return Ok(TxnResult::Res(hash)); } } @@ -344,6 +370,26 @@ impl Cmd { data::write_spec(&hash.to_string(), &wasm_spec.spec)?; } + if let Some(ledgers_to_extend) = self.ledgers_to_extend { + extend::Cmd { + ledgers_to_extend, + ttl_ledger_only: false, + key: key::Args { + contract_id: None, + key: None, + key_xdr: None, + wasm: Some(wasm_path.clone()), + wasm_hash: None, + durability: super::Durability::Persistent, + }, + config: config.clone(), + resources: self.resources.clone(), + build_only: self.build_only, + } + .execute(config, quiet, no_cache) + .await?; + } + Ok(TxnResult::Res(hash)) } } @@ -441,4 +487,27 @@ mod tests { // "rc" detection (used to gate the release-candidate check) still works. assert!(version.contains("rc")); } + + #[test] + fn parse_ledgers_to_extend_and_expire_alias() { + let cmd = Cmd::try_parse_from([ + "upload", + "--wasm", + "test.wasm", + "--ledgers-to-extend", + "1000", + ]) + .unwrap(); + assert_eq!(cmd.ledgers_to_extend, Some(1000)); + + let cmd_alias = Cmd::try_parse_from([ + "upload", + "--wasm", + "test.wasm", + "--ledgers-to-expire", + "2000", + ]) + .unwrap(); + assert_eq!(cmd_alias.ledgers_to_extend, Some(2000)); + } }