diff --git a/.github/workflows/rpc-tests.yml b/.github/workflows/rpc-tests.yml index 769cd21f7..75b6d7760 100644 --- a/.github/workflows/rpc-tests.yml +++ b/.github/workflows/rpc-tests.yml @@ -1,4 +1,3 @@ - name: RPC Tests on: push: @@ -15,32 +14,32 @@ jobs: runs-on: ubuntu-22.04 services: rpc: - image: stellar/quickstart:v438-testing + image: stellar/quickstart:testing ports: - 8000:8000 env: ENABLE_LOGS: true ENABLE_SOROBAN_DIAGNOSTIC_EVENTS: true NETWORK: local + PROTOCOL_VERSION: 22 options: >- --health-cmd "curl --no-progress-meter --fail-with-body -X POST \"http://localhost:8000/soroban/rpc\" -H 'Content-Type: application/json' -d '{\"jsonrpc\":\"2.0\",\"id\":8675309,\"method\":\"getNetwork\"}' && curl --no-progress-meter \"http://localhost:8000/friendbot\" | grep '\"invalid_field\": \"addr\"'" --health-interval 10s --health-timeout 5s --health-retries 50 steps: - - uses: actions/checkout@v4 - - uses: actions/cache@v4 - with: - path: | - ~/.cargo/bin/ - ~/.cargo/registry/index/ - ~/.cargo/registry/cache/ - ~/.cargo/git/db/ - target/ - key: ${{ runner.os }}-cargo-${{ hashFiles('**/Cargo.lock') }} - - run: rustup update - - run: cargo build - - run: rustup target add wasm32-unknown-unknown - - run: make build-test-wasms - - run: SOROBAN_PORT=8000 cargo test --features it --package soroban-test --test it -- integration - + - uses: actions/checkout@v4 + - uses: actions/cache@v4 + with: + path: | + ~/.cargo/bin/ + ~/.cargo/registry/index/ + ~/.cargo/registry/cache/ + ~/.cargo/git/db/ + target/ + key: ${{ runner.os }}-cargo-${{ hashFiles('**/Cargo.lock') }} + - run: rustup update + - run: cargo build + - run: rustup target add wasm32-unknown-unknown + - run: make build-test-wasms + - run: SOROBAN_PORT=8000 cargo test --features it --package soroban-test --test it -- integration diff --git a/Cargo.lock b/Cargo.lock index 6fdf9ee5b..806e767ec 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4542,6 +4542,7 @@ version = "22.0.0-rc.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "39b6d2ec8955243394278e1fae88be3b367fcfed9cf74e5044799a90786a8642" dependencies = [ + "arbitrary", "crate-git-revision 0.0.6", "ethnum", "num-derive", @@ -4731,7 +4732,10 @@ version = "22.0.0-rc.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6d063d0df000aaec20105aab3d743660322bc0269934ea95d79fa19aa8792385" dependencies = [ + "arbitrary", "bytes-lit", + "ctor", + "ed25519-dalek 2.1.1", "rand", "rustc_version", "serde", @@ -5091,6 +5095,7 @@ version = "22.0.0-rc.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c88dc0e928b9cb65ea43836b52560bb4ead3e32895f5019ca223dc7cd1966cbf" dependencies = [ + "arbitrary", "base64 0.13.1", "clap", "crate-git-revision 0.0.6", @@ -5347,6 +5352,13 @@ dependencies = [ "test-case-core", ] +[[package]] +name = "test_constructor" +version = "21.5.0" +dependencies = [ + "soroban-sdk 22.0.0-rc.3", +] + [[package]] name = "test_custom_account" version = "21.5.0" diff --git a/Cargo.toml b/Cargo.toml index 58caad196..d5f87b38e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -8,8 +8,15 @@ members = [ "cmd/crates/soroban-test/tests/fixtures/hello", "cmd/crates/soroban-test/tests/fixtures/bye", ] -default-members = ["cmd/soroban-cli", "cmd/crates/soroban-spec-tools", "cmd/crates/soroban-test"] -exclude = ["cmd/crates/soroban-test/tests/fixtures/hello", "cmd/crates/soroban-test/tests/fixtures/bye"] +default-members = [ + "cmd/soroban-cli", + "cmd/crates/soroban-spec-tools", + "cmd/crates/soroban-test", +] +exclude = [ + "cmd/crates/soroban-test/tests/fixtures/hello", + "cmd/crates/soroban-test/tests/fixtures/bye", +] [workspace.package] version = "21.5.0" diff --git a/FULL_HELP_DOCS.md b/FULL_HELP_DOCS.md index aa2c8e865..ae25453c6 100644 --- a/FULL_HELP_DOCS.md +++ b/FULL_HELP_DOCS.md @@ -388,7 +388,11 @@ If no keys are specified the contract itself is extended. Deploy a wasm contract -**Usage:** `stellar contract deploy [OPTIONS] --source-account <--wasm |--wasm-hash >` +**Usage:** `stellar contract deploy [OPTIONS] --source-account <--wasm |--wasm-hash > [-- ...]` + +###### **Arguments:** + +* `` — If provided, will be passed to the contract's `__constructor` function with provided arguments for that function as `--arg-name value` ###### **Options:** diff --git a/cmd/crates/soroban-test/tests/fixtures/test-wasms/constructor/Cargo.toml b/cmd/crates/soroban-test/tests/fixtures/test-wasms/constructor/Cargo.toml new file mode 100644 index 000000000..ef1dd882c --- /dev/null +++ b/cmd/crates/soroban-test/tests/fixtures/test-wasms/constructor/Cargo.toml @@ -0,0 +1,18 @@ +[package] +name = "test_constructor" +version = "21.5.0" +authors = ["Stellar Development Foundation "] +license = "Apache-2.0" +edition = "2021" +publish = false +rust-version.workspace = true + +[lib] +crate-type = ["cdylib"] +doctest = false + +[dependencies] +soroban-sdk = { workspace = true } + +[dev-dependencies] +soroban-sdk = { workspace = true, features = ["testutils"]} diff --git a/cmd/crates/soroban-test/tests/fixtures/test-wasms/constructor/src/lib.rs b/cmd/crates/soroban-test/tests/fixtures/test-wasms/constructor/src/lib.rs new file mode 100644 index 000000000..8b867968f --- /dev/null +++ b/cmd/crates/soroban-test/tests/fixtures/test-wasms/constructor/src/lib.rs @@ -0,0 +1,18 @@ +#![no_std] +use soroban_sdk::{contract, contractimpl, symbol_short, Env, Symbol}; + +#[contract] +pub struct Contract; +const COUNTER: Symbol = symbol_short!("COUNTER"); + +#[contractimpl] +impl Contract { + /// Example constructor + pub fn __constructor(env: Env, counter: u32) { + env.storage().persistent().set(&COUNTER, &counter); + } + /// Counter value + pub fn counter(env: Env) -> u32 { + env.storage().persistent().get(&COUNTER).unwrap() + } +} diff --git a/cmd/crates/soroban-test/tests/it/integration.rs b/cmd/crates/soroban-test/tests/it/integration.rs index 3e8521869..d6db5842b 100644 --- a/cmd/crates/soroban-test/tests/it/integration.rs +++ b/cmd/crates/soroban-test/tests/it/integration.rs @@ -1,4 +1,5 @@ mod bindings; +mod constructor; mod cookbook; mod custom_types; mod dotenv; diff --git a/cmd/crates/soroban-test/tests/it/integration/constructor.rs b/cmd/crates/soroban-test/tests/it/integration/constructor.rs new file mode 100644 index 000000000..1919b56b0 --- /dev/null +++ b/cmd/crates/soroban-test/tests/it/integration/constructor.rs @@ -0,0 +1,73 @@ +use assert_cmd::Command; + +use soroban_cli::xdr::{ + self, CreateContractArgsV2, HostFunction, InvokeHostFunctionOp, Limits, OperationBody, ReadXdr, + Transaction, TransactionV1Envelope, +}; +use soroban_test::{AssertExt, TestEnv}; + +use super::util::CONSTRUCTOR; + +fn constructor_cmd(sandbox: &TestEnv, value: u32, arg: &str) -> Command { + let mut cmd = sandbox.new_assert_cmd("contract"); + cmd.arg("deploy") + .arg("--wasm") + .arg(CONSTRUCTOR.path()) + .arg("--alias=init"); + if !arg.is_empty() { + cmd.arg(arg); + } + cmd.arg("--").arg("--counter").arg(value.to_string()); + cmd +} + +#[tokio::test] +async fn deploy_constructor_contract() { + let sandbox = TestEnv::new(); + let value = 100; + let build = constructor_cmd(&sandbox, value, "--build-only") + .assert() + .stdout_as_str(); + let tx = xdr::TransactionEnvelope::from_xdr_base64(&build, Limits::none()).unwrap(); + let ops = if let xdr::TransactionEnvelope::Tx(TransactionV1Envelope { + tx: Transaction { operations, .. }, + .. + }) = tx + { + operations + } else { + panic!() + } + .to_vec(); + let first = ops.first().unwrap(); + let args = match first { + xdr::Operation { + body: + OperationBody::InvokeHostFunction(InvokeHostFunctionOp { + host_function: + HostFunction::CreateContractV2(CreateContractArgsV2 { + constructor_args, .. + }), + .. + }), + .. + } => constructor_args, + _ => panic!("expected invoke host function with create contract v2"), + } + .to_vec(); + + match args.first().unwrap() { + xdr::ScVal::U32(u32) => assert_eq!(*u32, value), + _ => panic!("Expected U32"), + } + + constructor_cmd(&sandbox, value, "").assert().success(); + + let res = sandbox + .new_assert_cmd("contract") + .args(["invoke", "--id=init", "--", "counter"]) + .assert() + .success() + .stdout_as_str(); + assert_eq!(res.trim(), value.to_string()); +} diff --git a/cmd/crates/soroban-test/tests/it/integration/util.rs b/cmd/crates/soroban-test/tests/it/integration/util.rs index 438428e38..486b00a1b 100644 --- a/cmd/crates/soroban-test/tests/it/integration/util.rs +++ b/cmd/crates/soroban-test/tests/it/integration/util.rs @@ -6,6 +6,7 @@ use soroban_test::{TestEnv, Wasm}; use std::fmt::Display; pub const HELLO_WORLD: &Wasm = &Wasm::Custom("test-wasms", "test_hello_world"); +pub const CONSTRUCTOR: &Wasm = &Wasm::Custom("test-wasms", "test_constructor"); pub const CUSTOM_TYPES: &Wasm = &Wasm::Custom("test-wasms", "test_custom_types"); pub const CUSTOM_ACCOUNT: &Wasm = &Wasm::Custom("test-wasms", "test_custom_account"); pub const SWAP: &Wasm = &Wasm::Custom("test-wasms", "test_swap"); diff --git a/cmd/soroban-cli/src/commands/contract/deploy/asset.rs b/cmd/soroban-cli/src/commands/contract/deploy/asset.rs index 263908521..169c0109a 100644 --- a/cmd/soroban-cli/src/commands/contract/deploy/asset.rs +++ b/cmd/soroban-cli/src/commands/contract/deploy/asset.rs @@ -93,11 +93,11 @@ impl NetworkRunnable for Cmd { .verify_network_passphrase(Some(&network.network_passphrase)) .await?; let source_account = config.source_account()?; - // Get the account sequence number - let public_strkey = source_account.to_string(); // TODO: use symbols for the method names (both here and in serve) - let account_details = client.get_account(&public_strkey).await?; + let account_details = client + .get_account(&source_account.clone().to_string()) + .await?; let sequence: i64 = account_details.seq_num.into(); let network_passphrase = &network.network_passphrase; let contract_id = contract_id_hash_from_asset(asset, network_passphrase); diff --git a/cmd/soroban-cli/src/commands/contract/deploy/wasm.rs b/cmd/soroban-cli/src/commands/contract/deploy/wasm.rs index 21c685b93..9bf63802c 100644 --- a/cmd/soroban-cli/src/commands/contract/deploy/wasm.rs +++ b/cmd/soroban-cli/src/commands/contract/deploy/wasm.rs @@ -1,33 +1,38 @@ use std::array::TryFromSliceError; +use std::ffi::OsString; use std::fmt::Debug; use std::num::ParseIntError; use crate::xdr::{ AccountId, ContractExecutable, ContractIdPreimage, ContractIdPreimageFromAddress, - CreateContractArgs, Error as XdrError, Hash, HostFunction, InvokeHostFunctionOp, Limits, Memo, - MuxedAccount, Operation, OperationBody, Preconditions, PublicKey, ScAddress, SequenceNumber, - Transaction, TransactionExt, Uint256, VecM, WriteXdr, + CreateContractArgs, CreateContractArgsV2, Error as XdrError, Hash, HostFunction, + InvokeContractArgs, InvokeHostFunctionOp, Limits, Memo, MuxedAccount, Operation, OperationBody, + Preconditions, PublicKey, ScAddress, SequenceNumber, Transaction, TransactionExt, Uint256, + VecM, WriteXdr, }; use clap::{arg, command, Parser}; use rand::Rng; use regex::Regex; +use soroban_spec_tools::contract as contract_spec; + use crate::{ assembled::simulate_and_assemble_transaction, - commands::{contract::install, HEADING_RPC}, - config::{self, data, locator, network}, - rpc, utils, wasm, -}; -use crate::{ commands::{ - contract::{self, id::wasm::get_contract_id}, + contract::{self, arg_parsing, id::wasm::get_contract_id, install}, global, txn_result::{TxnEnvelopeResult, TxnResult}, - NetworkRunnable, + NetworkRunnable, HEADING_RPC, }, + config::{self, data, locator, network}, print::Print, + rpc, + utils::{self, rpc::get_remote_wasm_from_hash}, + wasm, }; +pub const CONSTRUCTOR_FUNCTION_NAME: &str = "__constructor"; + #[derive(Parser, Debug, Clone)] #[command(group( clap::ArgGroup::new("wasm_src") @@ -60,6 +65,9 @@ pub struct Cmd { /// configuration without asking for confirmation. #[arg(long, value_parser = clap::builder::ValueParser::new(alias_validator))] pub alias: 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, } #[derive(thiserror::Error, Debug)] @@ -110,6 +118,10 @@ pub enum Error { InvalidAliasFormat { alias: String }, #[error(transparent)] Locator(#[from] locator::Error), + #[error(transparent)] + ContractSpec(#[from] contract_spec::Error), + #[error(transparent)] + ArgParse(#[from] arg_parsing::Error), #[error("Only ed25519 accounts are allowed")] OnlyEd25519AccountsAllowed, } @@ -157,6 +169,7 @@ impl NetworkRunnable for Cmd { type Error = Error; type Result = TxnResult; + #[allow(clippy::too_many_lines)] async fn run_against_rpc_server( &self, global_args: Option<&global::Args>, @@ -211,21 +224,54 @@ impl NetworkRunnable for Cmd { client .verify_network_passphrase(Some(&network.network_passphrase)) .await?; + let MuxedAccount::Ed25519(bytes) = config.source_account()? else { return Err(Error::OnlyEd25519AccountsAllowed); }; + let source_account = AccountId(PublicKey::PublicKeyTypeEd25519(bytes)); + let contract_id_preimage = ContractIdPreimage::Address(ContractIdPreimageFromAddress { + address: ScAddress::Account(source_account.clone()), + salt: Uint256(salt), + }); + let contract_id = + get_contract_id(contract_id_preimage.clone(), &network.network_passphrase)?; + let raw_wasm = if let Some(wasm) = self.wasm.as_ref() { + wasm::Args { wasm: wasm.clone() }.read()? + } else { + get_remote_wasm_from_hash(&client, &wasm_hash).await? + }; + let entries = soroban_spec_tools::contract::Spec::new(&raw_wasm)?.spec; + let res = soroban_spec_tools::Spec::new(entries.clone()); + let constructor_params = if let Ok(func) = res.find_function(CONSTRUCTOR_FUNCTION_NAME) { + if func.inputs.len() == 0 { + None + } else { + let mut slop = vec![OsString::from(CONSTRUCTOR_FUNCTION_NAME)]; + slop.extend_from_slice(&self.slop); + Some( + arg_parsing::build_host_function_parameters( + &stellar_strkey::Contract(contract_id.0), + &slop, + &entries, + config, + )? + .2, + ) + } + } else { + None + }; - let key = stellar_strkey::ed25519::PublicKey(bytes.into()); // Get the account sequence number - let account_details = client.get_account(&key.to_string()).await?; + let account_details = client.get_account(&source_account.to_string()).await?; let sequence: i64 = account_details.seq_num.into(); - let (txn, contract_id) = build_create_contract_tx( + let txn = build_create_contract_tx( wasm_hash, sequence + 1, self.fee.fee, - &network.network_passphrase, - salt, - key, + source_account, + contract_id_preimage, + constructor_params.as_ref(), )?; if self.fee.build_only { @@ -266,33 +312,39 @@ impl NetworkRunnable for Cmd { } fn build_create_contract_tx( - hash: Hash, + wasm_hash: Hash, sequence: i64, fee: u32, - network_passphrase: &str, - salt: [u8; 32], - key: stellar_strkey::ed25519::PublicKey, -) -> Result<(Transaction, stellar_strkey::Contract), Error> { - let source_account = AccountId(PublicKey::PublicKeyTypeEd25519(key.0.into())); - - let contract_id_preimage = ContractIdPreimage::Address(ContractIdPreimageFromAddress { - address: ScAddress::Account(source_account), - salt: Uint256(salt), - }); - let contract_id = get_contract_id(contract_id_preimage.clone(), network_passphrase)?; - - let op = Operation { - source_account: None, - body: OperationBody::InvokeHostFunction(InvokeHostFunctionOp { - host_function: HostFunction::CreateContract(CreateContractArgs { - contract_id_preimage, - executable: ContractExecutable::Wasm(hash), + key: AccountId, + contract_id_preimage: ContractIdPreimage, + constructor_params: Option<&InvokeContractArgs>, +) -> Result { + let op = if let Some(InvokeContractArgs { args, .. }) = constructor_params { + Operation { + source_account: None, + body: OperationBody::InvokeHostFunction(InvokeHostFunctionOp { + host_function: HostFunction::CreateContractV2(CreateContractArgsV2 { + contract_id_preimage, + executable: ContractExecutable::Wasm(wasm_hash), + constructor_args: args.clone(), + }), + auth: VecM::default(), }), - auth: VecM::default(), - }), + } + } else { + Operation { + source_account: None, + body: OperationBody::InvokeHostFunction(InvokeHostFunctionOp { + host_function: HostFunction::CreateContract(CreateContractArgs { + contract_id_preimage, + executable: ContractExecutable::Wasm(wasm_hash), + }), + auth: VecM::default(), + }), + } }; let tx = Transaction { - source_account: MuxedAccount::Ed25519(key.0.into()), + source_account: key.into(), fee, seq_num: SequenceNumber(sequence), cond: Preconditions::None, @@ -301,7 +353,7 @@ fn build_create_contract_tx( ext: TransactionExt::V0, }; - Ok((tx, contract_id)) + Ok(tx) } #[cfg(test)] @@ -314,18 +366,26 @@ mod tests { .unwrap() .try_into() .unwrap(); + let salt = [0u8; 32]; + let key = + &utils::parse_secret_key("SBFGFF27Y64ZUGFAIG5AMJGQODZZKV2YQKAVUUN4HNE24XZXD2OEUVUP") + .unwrap(); + let source_account = AccountId(PublicKey::PublicKeyTypeEd25519(Uint256( + key.verifying_key().to_bytes(), + ))); + + let contract_id_preimage = ContractIdPreimage::Address(ContractIdPreimageFromAddress { + address: ScAddress::Account(source_account.clone()), + salt: Uint256(salt), + }); + let result = build_create_contract_tx( Hash(hash), 300, 1, - "Public Global Stellar Network ; September 2015", - [0u8; 32], - stellar_strkey::ed25519::PublicKey( - utils::parse_secret_key("SBFGFF27Y64ZUGFAIG5AMJGQODZZKV2YQKAVUUN4HNE24XZXD2OEUVUP") - .unwrap() - .verifying_key() - .to_bytes(), - ), + source_account, + contract_id_preimage, + None, ); assert!(result.is_ok()); diff --git a/cmd/soroban-cli/src/commands/contract/extend.rs b/cmd/soroban-cli/src/commands/contract/extend.rs index 24aac54c5..6ec8ebdb1 100644 --- a/cmd/soroban-cli/src/commands/contract/extend.rs +++ b/cmd/soroban-cli/src/commands/contract/extend.rs @@ -135,7 +135,9 @@ impl NetworkRunnable for Cmd { let extend_to = self.ledgers_to_extend(); // Get the account sequence number - let account_details = client.get_account(&source_account.to_string()).await?; + let account_details = client + .get_account(&source_account.clone().to_string()) + .await?; let sequence: i64 = account_details.seq_num.into(); let tx = Transaction { @@ -184,10 +186,7 @@ impl NetworkRunnable for Cmd { if !events.is_empty() { tracing::info!("Events:\n {events:#?}"); } - let meta = res - .result_meta - .as_ref() - .ok_or(Error::MissingOperationResult)?; + let meta = res.result_meta.ok_or(Error::MissingOperationResult)?; // The transaction from core will succeed regardless of whether it actually found & extended // the entry, so we have to inspect the result meta to tell if it worked or not. diff --git a/cmd/soroban-cli/src/commands/contract/install.rs b/cmd/soroban-cli/src/commands/contract/install.rs index cd6e93b24..a215a987d 100644 --- a/cmd/soroban-cli/src/commands/contract/install.rs +++ b/cmd/soroban-cli/src/commands/contract/install.rs @@ -137,7 +137,9 @@ impl NetworkRunnable for Cmd { // Get the account sequence number let source_account = config.source_account()?; - let account_details = client.get_account(&source_account.to_string()).await?; + let account_details = client + .get_account(&source_account.clone().to_string()) + .await?; let sequence: i64 = account_details.seq_num.into(); let (tx_without_preflight, hash) = @@ -204,7 +206,7 @@ impl NetworkRunnable for Cmd { if let Some(TransactionResult { result: TransactionResultResult::TxInternalError, .. - }) = txn_resp.result.as_ref() + }) = txn_resp.result { // Now just need to restore it and don't have to install again restore::Cmd { diff --git a/cmd/soroban-cli/src/commands/contract/restore.rs b/cmd/soroban-cli/src/commands/contract/restore.rs index 87a52a9f6..92fc4b41d 100644 --- a/cmd/soroban-cli/src/commands/contract/restore.rs +++ b/cmd/soroban-cli/src/commands/contract/restore.rs @@ -136,8 +136,9 @@ impl NetworkRunnable for Cmd { let source_account = config.source_account()?; // Get the account sequence number - let public_strkey = source_account.to_string(); - let account_details = client.get_account(&public_strkey).await?; + let account_details = client + .get_account(&source_account.clone().to_string()) + .await?; let sequence: i64 = account_details.seq_num.into(); let tx = Transaction { @@ -206,7 +207,7 @@ impl NetworkRunnable for Cmd { ); } Ok(TxnResult::Res( - parse_operations(operations).ok_or(Error::MissingOperationResult)?, + parse_operations(&operations.to_vec()).ok_or(Error::MissingOperationResult)?, )) } } diff --git a/cmd/soroban-cli/src/commands/tx/args.rs b/cmd/soroban-cli/src/commands/tx/args.rs index 7e032fd53..fc99b9591 100644 --- a/cmd/soroban-cli/src/commands/tx/args.rs +++ b/cmd/soroban-cli/src/commands/tx/args.rs @@ -39,7 +39,7 @@ impl Args { let source_account = self.source_account()?; let seq_num = self .config - .next_sequence_number(&source_account.to_string()) + .next_sequence_number(source_account.clone().account_id()) .await?; // Once we have a way to add operations this will be updated to allow for a different source account let operation = xdr::Operation { diff --git a/cmd/soroban-cli/src/config/data.rs b/cmd/soroban-cli/src/config/data.rs index bbfc6994e..f032a94d3 100644 --- a/cmd/soroban-cli/src/config/data.rs +++ b/cmd/soroban-cli/src/config/data.rs @@ -209,7 +209,7 @@ mod test { assert_eq!(rpc_uri, new_rpc_uri); match (action, original_action) { (Action::Simulate { response: a }, Action::Simulate { response: b }) => { - assert_eq!(a.cost.cpu_insns, b.cost.cpu_insns); + assert_eq!(a.min_resource_fee, b.min_resource_fee); } _ => panic!("Action mismatch"), } diff --git a/cmd/soroban-cli/src/config/mod.rs b/cmd/soroban-cli/src/config/mod.rs index 21ebb1fe9..11eb179ee 100644 --- a/cmd/soroban-cli/src/config/mod.rs +++ b/cmd/soroban-cli/src/config/mod.rs @@ -115,10 +115,19 @@ impl Args { Ok(self.network.get(&self.locator)?) } - pub async fn next_sequence_number(&self, account_str: &str) -> Result { + pub async fn next_sequence_number( + &self, + account: impl Into, + ) -> Result { let network = self.get_network()?; let client = network.rpc_client()?; - Ok((client.get_account(account_str).await?.seq_num.0 + 1).into()) + Ok((client + .get_account(&account.into().to_string()) + .await? + .seq_num + .0 + + 1) + .into()) } }