diff --git a/.github/workflows/binaries.yml b/.github/workflows/binaries.yml index d913cf23d..45d74a243 100644 --- a/.github/workflows/binaries.yml +++ b/.github/workflows/binaries.yml @@ -157,6 +157,6 @@ jobs: repo: context.repo.repo, release_id: ${{ github.event.release.id }}, name: '${{ env.STELLAR_CLI_INSTALLER }}', - data: fs.readFileSync('Output/stellar-installer.exe'), + data: fs.readFileSync('${{ env.STELLAR_CLI_INSTALLER }}'), }); diff --git a/.github/workflows/bindings-ts.yml b/.github/workflows/bindings-ts.yml index 9b5f767ac..f6aaae499 100644 --- a/.github/workflows/bindings-ts.yml +++ b/.github/workflows/bindings-ts.yml @@ -19,7 +19,7 @@ jobs: NETWORK: local ENABLE_SOROBAN_RPC: true 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-cmd "curl --no-progress-meter --fail-with-body -X POST \"http://localhost:8000/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 diff --git a/Cargo.lock b/Cargo.lock index 772591e1d..04a65df33 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4288,7 +4288,7 @@ dependencies = [ [[package]] name = "soroban-cli" -version = "22.0.1" +version = "22.1.0" dependencies = [ "assert_cmd", "assert_fs", @@ -4455,7 +4455,7 @@ dependencies = [ [[package]] name = "soroban-hello" -version = "22.0.1" +version = "22.1.0" [[package]] name = "soroban-ledger-snapshot" @@ -4526,7 +4526,7 @@ dependencies = [ [[package]] name = "soroban-spec-json" -version = "22.0.1" +version = "22.1.0" dependencies = [ "pretty_assertions", "serde", @@ -4556,7 +4556,7 @@ dependencies = [ [[package]] name = "soroban-spec-tools" -version = "22.0.1" +version = "22.1.0" dependencies = [ "base64 0.21.7", "ethnum", @@ -4574,7 +4574,7 @@ dependencies = [ [[package]] name = "soroban-spec-typescript" -version = "22.0.1" +version = "22.1.0" dependencies = [ "base64 0.21.7", "heck 0.4.1", @@ -4595,7 +4595,7 @@ dependencies = [ [[package]] name = "soroban-test" -version = "22.0.1" +version = "22.1.0" dependencies = [ "assert_cmd", "assert_fs", @@ -4666,18 +4666,18 @@ checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" [[package]] name = "stellar-bye" -version = "22.0.1" +version = "22.1.0" [[package]] name = "stellar-cli" -version = "22.0.1" +version = "22.1.0" dependencies = [ "soroban-cli", ] [[package]] name = "stellar-ledger" -version = "22.0.1" +version = "22.1.0" dependencies = [ "async-trait", "bollard", @@ -5024,42 +5024,42 @@ dependencies = [ [[package]] name = "test_constructor" -version = "22.0.1" +version = "22.1.0" dependencies = [ "soroban-sdk", ] [[package]] name = "test_custom_account" -version = "22.0.1" +version = "22.1.0" dependencies = [ "soroban-sdk", ] [[package]] name = "test_custom_types" -version = "22.0.1" +version = "22.1.0" dependencies = [ "soroban-sdk", ] [[package]] name = "test_hello_world" -version = "22.0.1" +version = "22.1.0" dependencies = [ "soroban-sdk", ] [[package]] name = "test_swap" -version = "22.0.1" +version = "22.1.0" dependencies = [ "soroban-sdk", ] [[package]] name = "test_token" -version = "22.0.1" +version = "22.1.0" dependencies = [ "soroban-sdk", "soroban-token-sdk", @@ -5067,7 +5067,7 @@ dependencies = [ [[package]] name = "test_udt" -version = "22.0.1" +version = "22.1.0" dependencies = [ "soroban-sdk", ] diff --git a/Cargo.toml b/Cargo.toml index cbfe6dd3c..839ca9cd0 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -19,24 +19,24 @@ exclude = [ ] [workspace.package] -version = "22.0.1" +version = "22.1.0" rust-version = "1.81.0" # Dependencies located in this repo: [workspace.dependencies.soroban-cli] -version = "=22.0.1" +version = "=22.1.0" path = "cmd/soroban-cli" [workspace.dependencies.soroban-spec-json] -version = "=22.0.1" +version = "=22.1.0" path = "./cmd/crates/soroban-spec-json" [workspace.dependencies.soroban-spec-typescript] -version = "22.0.1" +version = "22.1.0" path = "./cmd/crates/soroban-spec-typescript" [workspace.dependencies.soroban-spec-tools] -version = "22.0.1" +version = "22.1.0" path = "./cmd/crates/soroban-spec-tools" # Dependencies from the rs-stellar-xdr repo: diff --git a/FULL_HELP_DOCS.md b/FULL_HELP_DOCS.md index 270cc328e..c1526aea7 100644 --- a/FULL_HELP_DOCS.md +++ b/FULL_HELP_DOCS.md @@ -944,7 +944,7 @@ Create and manage identities including keys and addresses * `generate` — Generate a new identity with a seed phrase, currently 12 words * `ls` — List identities * `rm` — Remove an identity -* `show` — Given an identity return its private key +* `secret` — Output an identity's secret key * `use` — Set the default identity that will be used on all commands. This allows you to skip `--source-account` or setting a environment variable, while reusing this value in all commands that require it @@ -1070,11 +1070,11 @@ Remove an identity -## `stellar keys show` +## `stellar keys secret` -Given an identity return its private key +Output an identity's secret key -**Usage:** `stellar keys show [OPTIONS] ` +**Usage:** `stellar keys secret [OPTIONS] ` ###### **Arguments:** diff --git a/cmd/crates/soroban-spec-typescript/ts-tests/.env b/cmd/crates/soroban-spec-typescript/ts-tests/.env index 93bb4be67..0ea150c3f 100644 --- a/cmd/crates/soroban-spec-typescript/ts-tests/.env +++ b/cmd/crates/soroban-spec-typescript/ts-tests/.env @@ -1,3 +1,3 @@ -SOROBAN_NETWORK_PASSPHRASE="Standalone Network ; February 2017" -SOROBAN_RPC_URL="http://localhost:8000/soroban/rpc" -SOROBAN_FRIENDBOT_URL="http://localhost:8000/friendbot" +STELLAR_NETWORK_PASSPHRASE="Standalone Network ; February 2017" +STELLAR_RPC_URL="http://localhost:8000/rpc" +STELLAR_FRIENDBOT_URL="http://localhost:8000/friendbot" diff --git a/cmd/crates/soroban-spec-typescript/ts-tests/initialize.sh b/cmd/crates/soroban-spec-typescript/ts-tests/initialize.sh index 804820abb..1fb84f081 100755 --- a/cmd/crates/soroban-spec-typescript/ts-tests/initialize.sh +++ b/cmd/crates/soroban-spec-typescript/ts-tests/initialize.sh @@ -10,10 +10,10 @@ done unset IFS echo Network -echo " RPC: $SOROBAN_RPC_URL" -echo " Passphrase: \"$SOROBAN_NETWORK_PASSPHRASE\"" +echo " RPC: $STELLAR_RPC_URL" +echo " Passphrase: \"$STELLAR_NETWORK_PASSPHRASE\"" -NETWORK_STATUS=$(curl -s -X POST "http://localhost:8000/soroban/rpc" -H "Content-Type: application/json" -d '{ "jsonrpc": "2.0", "id": 8675309, "method": "getHealth" }' | sed 's/.*"status":"\([^"]*\)".*/\1/') || { echo "Make sure you're running local RPC network on localhost:8000" && exit 1; } +NETWORK_STATUS=$(curl -s -X POST "http://localhost:8000/rpc" -H "Content-Type: application/json" -d '{ "jsonrpc": "2.0", "id": 8675309, "method": "getHealth" }' | sed 's/.*"status":"\([^"]*\)".*/\1/') || { echo "Make sure you're running local RPC network on localhost:8000" && exit 1; } echo " Status: $NETWORK_STATUS" if [[ "$NETWORK_STATUS" != "healthy" ]]; then @@ -22,28 +22,28 @@ if [[ "$NETWORK_STATUS" != "healthy" ]]; then fi # Print command before executing, from https://stackoverflow.com/a/23342259/249801 -# Discussion: https://github.com/stellar/soroban-tools/pull/1034#pullrequestreview-1690667116 +# Discussion: https://github.com/stellar/stellar-tools/pull/1034#pullrequestreview-1690667116 exe() { echo"${@/eval/}" ; "$@" ; } function fund_all() { - exe eval "./soroban keys generate root" - exe eval "./soroban keys fund root" + exe eval "./stellar keys generate --fund root" } function upload() { - exe eval "(./soroban contract $1 --quiet --source root --wasm $2 --ignore-checks) > $3" + exe eval "(./stellar contract $1 --quiet --source root --wasm $2 --ignore-checks) > $3" } function deploy_all() { upload deploy ../../../../target/wasm32-unknown-unknown/test-wasms/test_custom_types.wasm contract-id-custom-types.txt - # TODO: support `--wasm-hash` with `contract bindings` upload install ../../../../target/wasm32-unknown-unknown/test-wasms/test_constructor.wasm contract-wasm-hash-constructor.txt + exe eval "./stellar contract asset deploy --asset native --source root" } function bind() { - exe eval "./soroban contract bindings typescript $1 $2 --output-dir ./node_modules/$3 --overwrite" + exe eval "./stellar contract bindings typescript $1 $2 --output-dir ./node_modules/$3 --overwrite" exe eval "sh -c \"cd ./node_modules/$3 && npm install && npm run build\"" } function bind_all() { bind --contract-id $(cat contract-id-custom-types.txt) test-custom-types bind --wasm-hash $(cat contract-wasm-hash-constructor.txt) test-constructor + bind --contract-id $(./stellar contract id asset --asset native) xlm } fund_all diff --git a/cmd/crates/soroban-spec-typescript/ts-tests/src/test-xlm-lib-from-sac.ts b/cmd/crates/soroban-spec-typescript/ts-tests/src/test-xlm-lib-from-sac.ts new file mode 100644 index 000000000..e39fb8b7d --- /dev/null +++ b/cmd/crates/soroban-spec-typescript/ts-tests/src/test-xlm-lib-from-sac.ts @@ -0,0 +1,15 @@ +import test from "ava" +import { rpcUrl, root, signer } from "./util.js" +import { Client, networks } from "xlm" + +const contract = new Client({ + ...networks.standalone, + rpcUrl, + allowHttp: true, + publicKey: root.keypair.publicKey(), + ...signer, +}) + +test("can generate a lib from a Stellar Asset Contract", async (t) => { + t.is((await contract.symbol()).result, "native"); +}); diff --git a/cmd/crates/soroban-spec-typescript/ts-tests/src/util.ts b/cmd/crates/soroban-spec-typescript/ts-tests/src/util.ts index a5315a643..eacedbaec 100644 --- a/cmd/crates/soroban-spec-typescript/ts-tests/src/util.ts +++ b/cmd/crates/soroban-spec-typescript/ts-tests/src/util.ts @@ -3,7 +3,7 @@ import { Address, Keypair } from "@stellar/stellar-sdk"; import { basicNodeSigner } from "@stellar/stellar-sdk/contract"; const rootKeypair = Keypair.fromSecret( - spawnSync("./soroban", ["keys", "show", "root"], { + spawnSync("./soroban", ["keys", "secret", "root"], { shell: true, encoding: "utf8", }).stdout.trim(), @@ -14,9 +14,9 @@ export const root = { address: Address.fromString(rootKeypair.publicKey()), }; -export const rpcUrl = process.env.SOROBAN_RPC_URL ?? "http://localhost:8000/"; +export const rpcUrl = process.env.STELLAR_RPC_URL ?? "http://localhost:8000/"; export const networkPassphrase = - process.env.SOROBAN_NETWORK_PASSPHRASE ?? + process.env.STELLAR_NETWORK_PASSPHRASE ?? "Standalone Network ; February 2017"; export const signer = basicNodeSigner(root.keypair, networkPassphrase); diff --git a/cmd/crates/soroban-spec-typescript/ts-tests/soroban b/cmd/crates/soroban-spec-typescript/ts-tests/stellar similarity index 100% rename from cmd/crates/soroban-spec-typescript/ts-tests/soroban rename to cmd/crates/soroban-spec-typescript/ts-tests/stellar diff --git a/cmd/crates/soroban-test/src/lib.rs b/cmd/crates/soroban-test/src/lib.rs index 2c62578ef..492d3bb97 100644 --- a/cmd/crates/soroban-test/src/lib.rs +++ b/cmd/crates/soroban-test/src/lib.rs @@ -285,7 +285,7 @@ impl TestEnv { /// Returns the private key corresponding to the test keys's `hd_path` pub fn test_show(&self, hd_path: usize) -> String { - self.cmd::(&format!("--hd-path={hd_path}")) + self.cmd::(&format!("--hd-path={hd_path}")) .private_key() .unwrap() .to_string() diff --git a/cmd/crates/soroban-test/tests/fixtures/bye/Cargo.toml b/cmd/crates/soroban-test/tests/fixtures/bye/Cargo.toml index 4756225d2..5ca022099 100644 --- a/cmd/crates/soroban-test/tests/fixtures/bye/Cargo.toml +++ b/cmd/crates/soroban-test/tests/fixtures/bye/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "stellar-bye" -version = "22.0.1" +version = "22.1.0" edition = "2021" publish = false diff --git a/cmd/crates/soroban-test/tests/fixtures/hello/Cargo.toml b/cmd/crates/soroban-test/tests/fixtures/hello/Cargo.toml index a2fb7b15c..73d7c781e 100644 --- a/cmd/crates/soroban-test/tests/fixtures/hello/Cargo.toml +++ b/cmd/crates/soroban-test/tests/fixtures/hello/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "soroban-hello" -version = "22.0.1" +version = "22.1.0" edition = "2021" publish = false 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 index 9ccbb23d0..f7f9abac0 100644 --- a/cmd/crates/soroban-test/tests/fixtures/test-wasms/constructor/Cargo.toml +++ b/cmd/crates/soroban-test/tests/fixtures/test-wasms/constructor/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "test_constructor" -version = "22.0.1" +version = "22.1.0" authors = ["Stellar Development Foundation "] license = "Apache-2.0" edition = "2021" diff --git a/cmd/crates/soroban-test/tests/fixtures/test-wasms/custom_account/Cargo.toml b/cmd/crates/soroban-test/tests/fixtures/test-wasms/custom_account/Cargo.toml index e69dd96c9..081312a81 100644 --- a/cmd/crates/soroban-test/tests/fixtures/test-wasms/custom_account/Cargo.toml +++ b/cmd/crates/soroban-test/tests/fixtures/test-wasms/custom_account/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "test_custom_account" -version = "22.0.1" +version = "22.1.0" authors = ["Stellar Development Foundation "] license = "Apache-2.0" edition = "2021" diff --git a/cmd/crates/soroban-test/tests/fixtures/test-wasms/custom_type/Cargo.toml b/cmd/crates/soroban-test/tests/fixtures/test-wasms/custom_type/Cargo.toml index 50b6d8a72..ebe2191e1 100644 --- a/cmd/crates/soroban-test/tests/fixtures/test-wasms/custom_type/Cargo.toml +++ b/cmd/crates/soroban-test/tests/fixtures/test-wasms/custom_type/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "test_custom_types" -version = "22.0.1" +version = "22.1.0" authors = ["Stellar Development Foundation "] license = "Apache-2.0" edition = "2021" diff --git a/cmd/crates/soroban-test/tests/fixtures/test-wasms/hello_world/Cargo.toml b/cmd/crates/soroban-test/tests/fixtures/test-wasms/hello_world/Cargo.toml index a91a6ca62..14b386d2b 100644 --- a/cmd/crates/soroban-test/tests/fixtures/test-wasms/hello_world/Cargo.toml +++ b/cmd/crates/soroban-test/tests/fixtures/test-wasms/hello_world/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "test_hello_world" -version = "22.0.1" +version = "22.1.0" authors = ["Stellar Development Foundation "] license = "Apache-2.0" edition = "2021" diff --git a/cmd/crates/soroban-test/tests/fixtures/test-wasms/swap/Cargo.toml b/cmd/crates/soroban-test/tests/fixtures/test-wasms/swap/Cargo.toml index 15c45c98e..70387d652 100644 --- a/cmd/crates/soroban-test/tests/fixtures/test-wasms/swap/Cargo.toml +++ b/cmd/crates/soroban-test/tests/fixtures/test-wasms/swap/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "test_swap" -version = "22.0.1" +version = "22.1.0" authors = ["Stellar Development Foundation "] license = "Apache-2.0" edition = "2021" diff --git a/cmd/crates/soroban-test/tests/fixtures/test-wasms/token/Cargo.toml b/cmd/crates/soroban-test/tests/fixtures/test-wasms/token/Cargo.toml index d4e7e7436..681bb9b40 100644 --- a/cmd/crates/soroban-test/tests/fixtures/test-wasms/token/Cargo.toml +++ b/cmd/crates/soroban-test/tests/fixtures/test-wasms/token/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "test_token" -version = "22.0.1" +version = "22.1.0" description = "Soroban standard token contract" authors = ["Stellar Development Foundation "] license = "Apache-2.0" diff --git a/cmd/crates/soroban-test/tests/it/config.rs b/cmd/crates/soroban-test/tests/it/config.rs index b796910a8..e81233b6e 100644 --- a/cmd/crates/soroban-test/tests/it/config.rs +++ b/cmd/crates/soroban-test/tests/it/config.rs @@ -283,7 +283,7 @@ fn use_env() { sandbox .new_assert_cmd("keys") - .arg("show") + .arg("secret") .arg("bob") .assert() .success() @@ -330,7 +330,7 @@ fn config_dirs_precedence() { sandbox .new_assert_cmd("keys") - .arg("show") + .arg("secret") .arg("alice") .arg("--verbose") .assert() diff --git a/cmd/crates/soroban-test/tests/it/integration/hello_world.rs b/cmd/crates/soroban-test/tests/it/integration/hello_world.rs index b9ed0196f..4464e532f 100644 --- a/cmd/crates/soroban-test/tests/it/integration/hello_world.rs +++ b/cmd/crates/soroban-test/tests/it/integration/hello_world.rs @@ -53,7 +53,7 @@ async fn invoke() { let secret_key = sandbox .new_assert_cmd("keys") - .arg("show") + .arg("secret") .arg("test") .assert() .stdout_as_str(); @@ -65,7 +65,7 @@ async fn invoke() { .stdout_as_str(); let secret_key_1 = sandbox .new_assert_cmd("keys") - .arg("show") + .arg("secret") .arg("test") .arg("--hd-path=1") .assert() @@ -115,7 +115,7 @@ async fn invoke() { assert_eq!(sk_from_file, format!("secret_key = \"{secret_key_1}\"\n")); let secret_key_1_readin = sandbox .new_assert_cmd("keys") - .arg("show") + .arg("secret") .arg("testone") .assert() .stdout_as_str(); diff --git a/cmd/soroban-cli/src/commands/contract/bindings/typescript.rs b/cmd/soroban-cli/src/commands/contract/bindings/typescript.rs index abe26dd37..1da64eaa5 100644 --- a/cmd/soroban-cli/src/commands/contract/bindings/typescript.rs +++ b/cmd/soroban-cli/src/commands/contract/bindings/typescript.rs @@ -1,12 +1,12 @@ use std::{ffi::OsString, fmt::Debug, path::PathBuf}; use clap::{command, Parser}; -use soroban_spec_tools::contract as contract_spec; +use soroban_spec_tools::contract as spec_tools; use soroban_spec_typescript::boilerplate::Project; use crate::print::Print; use crate::{ - commands::{contract::info::shared as wasm_or_contract, global, NetworkRunnable}, + commands::{contract::info::shared as contract_spec, global, NetworkRunnable}, config, }; use soroban_spec_tools::contract::Spec; @@ -15,7 +15,7 @@ use soroban_spec_tools::contract::Spec; #[group(skip)] pub struct Cmd { #[command(flatten)] - pub wasm_or_hash_or_contract_id: wasm_or_contract::Args, + pub wasm_or_hash_or_contract_id: contract_spec::Args, /// Where to place generated project #[arg(long)] pub output_dir: PathBuf, @@ -39,11 +39,13 @@ pub enum Error { NotUtf8(OsString), #[error(transparent)] - Spec(#[from] contract_spec::Error), + Spec(#[from] spec_tools::Error), #[error("Failed to get file name from path: {0:?}")] FailedToGetFileName(PathBuf), #[error(transparent)] - WasmOrContract(#[from] wasm_or_contract::Error), + WasmOrContract(#[from] contract_spec::Error), + #[error(transparent)] + Xdr(#[from] crate::xdr::Error), } #[async_trait::async_trait] @@ -58,13 +60,14 @@ impl NetworkRunnable for Cmd { ) -> Result<(), Error> { let print = Print::new(global_args.is_some_and(|a| a.quiet)); - let (spec, contract_address, network) = - wasm_or_contract::fetch_wasm(&self.wasm_or_hash_or_contract_id, &print).await?; + let contract_spec::Fetched { contract, source } = + contract_spec::fetch(&self.wasm_or_hash_or_contract_id, &print).await?; - let spec = if let Some(spec) = spec { - Spec::new(&spec)? - } else { - Spec::new(&soroban_sdk::token::StellarAssetSpec::spec_xdr())? + let spec = match contract { + contract_spec::Contract::Wasm { wasm_bytes } => Spec::new(&wasm_bytes)?.spec, + contract_spec::Contract::StellarAssetContract => { + soroban_spec::read::parse_raw(&soroban_sdk::token::StellarAssetSpec::spec_xdr())? + } }; if self.output_dir.is_file() { @@ -86,15 +89,23 @@ impl NetworkRunnable for Cmd { let contract_name = &file_name .to_str() .ok_or_else(|| Error::NotUtf8(file_name.to_os_string()))?; - if let Some(contract_address) = contract_address.clone() { - print.infoln(format!("Embedding contract address: {contract_address}")); - } + let (resolved_address, network) = match source { + contract_spec::Source::Contract { + resolved_address, + network, + } => { + print.infoln(format!("Embedding contract address: {resolved_address}")); + (Some(resolved_address), Some(network)) + } + contract_spec::Source::Wasm { network, .. } => (None, Some(network)), + contract_spec::Source::File { .. } => (None, None), + }; p.init( contract_name, - contract_address.as_deref(), + resolved_address.as_deref(), network.as_ref().map(|n| n.rpc_url.as_ref()), network.as_ref().map(|n| n.network_passphrase.as_ref()), - &spec.spec, + &spec, )?; print.checkln("Generated!"); print.infoln(format!( diff --git a/cmd/soroban-cli/src/commands/contract/info/env_meta.rs b/cmd/soroban-cli/src/commands/contract/info/env_meta.rs index f882d2737..02da6f439 100644 --- a/cmd/soroban-cli/src/commands/contract/info/env_meta.rs +++ b/cmd/soroban-cli/src/commands/contract/info/env_meta.rs @@ -9,7 +9,7 @@ use crate::{ commands::{ contract::info::{ env_meta::Error::{NoEnvMetaPresent, NoSACEnvMeta}, - shared::{self, fetch_wasm, MetasInfoOutput}, + shared::{self, fetch, Fetched, MetasInfoOutput}, }, global, }, @@ -43,12 +43,12 @@ pub enum Error { impl Cmd { pub async fn run(&self, global_args: &global::Args) -> Result { let print = Print::new(global_args.quiet); - let (bytes, ..) = fetch_wasm(&self.common, &print).await?; + let Fetched { contract, .. } = fetch(&self.common, &print).await?; - let Some(bytes) = bytes else { - return Err(NoSACEnvMeta()); + let spec = match contract { + shared::Contract::Wasm { wasm_bytes } => Spec::new(&wasm_bytes)?, + shared::Contract::StellarAssetContract => return Err(NoSACEnvMeta()), }; - let spec = Spec::new(&bytes)?; let Some(env_meta_base64) = spec.env_meta_base64 else { return Err(NoEnvMetaPresent()); diff --git a/cmd/soroban-cli/src/commands/contract/info/interface.rs b/cmd/soroban-cli/src/commands/contract/info/interface.rs index dd71150fa..0c896bc3d 100644 --- a/cmd/soroban-cli/src/commands/contract/info/interface.rs +++ b/cmd/soroban-cli/src/commands/contract/info/interface.rs @@ -1,8 +1,7 @@ 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::contract::info::shared::{self, fetch, Fetched}; use crate::commands::global; use crate::print::Print; use clap::{command, Parser}; @@ -47,18 +46,21 @@ pub enum Error { impl Cmd { pub async fn run(&self, global_args: &global::Args) -> Result { let print = Print::new(global_args.quiet); - let (bytes, ..) = fetch_wasm(&self.common, &print).await?; + let Fetched { contract, .. } = fetch(&self.common, &print).await?; - let (base64, spec) = if bytes.is_none() { - Spec::spec_to_base64(&soroban_sdk::token::StellarAssetSpec::spec_xdr())? - } else { - let spec = Spec::new(&bytes.unwrap())?; + let (base64, spec) = match contract { + shared::Contract::Wasm { wasm_bytes } => { + let spec = Spec::new(&wasm_bytes)?; - if spec.env_meta_base64.is_none() { - return Err(NoInterfacePresent()); - } + if spec.env_meta_base64.is_none() { + return Err(NoInterfacePresent()); + } - (spec.spec_base64.unwrap(), spec.spec) + (spec.spec_base64.unwrap(), spec.spec) + } + shared::Contract::StellarAssetContract => { + Spec::spec_to_base64(&soroban_sdk::token::StellarAssetSpec::spec_xdr())? + } }; let res = match self.output { diff --git a/cmd/soroban-cli/src/commands/contract/info/meta.rs b/cmd/soroban-cli/src/commands/contract/info/meta.rs index 4b6fdee3e..736e8f432 100644 --- a/cmd/soroban-cli/src/commands/contract/info/meta.rs +++ b/cmd/soroban-cli/src/commands/contract/info/meta.rs @@ -1,8 +1,7 @@ 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::contract::info::shared::{self, fetch, Fetched, MetasInfoOutput}; use crate::commands::global; use crate::print::Print; use clap::{command, Parser}; @@ -36,12 +35,12 @@ pub enum Error { impl Cmd { pub async fn run(&self, global_args: &global::Args) -> Result { let print = Print::new(global_args.quiet); - let (bytes, ..) = fetch_wasm(&self.common, &print).await?; + let Fetched { contract, .. } = fetch(&self.common, &print).await?; - let Some(bytes) = bytes else { - return Err(NoSACMeta()); + let spec = match contract { + shared::Contract::Wasm { wasm_bytes } => Spec::new(&wasm_bytes)?, + shared::Contract::StellarAssetContract => return Err(NoSACMeta()), }; - let spec = Spec::new(&bytes)?; let Some(meta_base64) = spec.meta_base64 else { return Err(NoMetaPresent()); diff --git a/cmd/soroban-cli/src/commands/contract/info/shared.rs b/cmd/soroban-cli/src/commands/contract/info/shared.rs index 2ee5d018d..6023b03cf 100644 --- a/cmd/soroban-cli/src/commands/contract/info/shared.rs +++ b/cmd/soroban-cli/src/commands/contract/info/shared.rs @@ -76,23 +76,58 @@ pub enum Error { #[error("provided wasm hash is invalid {0:?}")] InvalidWasmHash(String), #[error("must provide one of --wasm, --wasm-hash, or --contract-id")] - MalformedWasmOrWasmHashOrContractId, + MissingArg, #[error(transparent)] Rpc(#[from] soroban_rpc::Error), #[error(transparent)] Locator(#[from] locator::Error), } -pub async fn fetch_wasm( - args: &Args, - print: &Print, -) -> Result<(Option>, Option, Option), Error> { +pub struct Fetched { + pub contract: Contract, + pub source: Source, +} + +pub enum Contract { + Wasm { wasm_bytes: Vec }, + StellarAssetContract, +} + +pub enum Source { + File { + path: PathBuf, + }, + Wasm { + hash: String, + network: Network, + }, + Contract { + resolved_address: String, + network: Network, + }, +} + +impl Source { + pub fn network(&self) -> Option<&Network> { + match self { + Source::File { .. } => None, + Source::Wasm { ref network, .. } | Source::Contract { ref network, .. } => { + Some(network) + } + } + } +} + +pub async fn fetch(args: &Args, print: &Print) -> Result { // Check if a local WASM file path is provided if let Some(path) = &args.wasm { // Read the WASM file and return its contents print.infoln("Loading contract spec from file..."); let wasm_bytes = wasm::Args { wasm: path.clone() }.read()?; - return Ok((Some(wasm_bytes), None, None)); + return Ok(Fetched { + contract: Contract::Wasm { wasm_bytes }, + source: Source::File { path: path.clone() }, + }); } // If no local wasm, then check for wasm_hash and fetch from the network @@ -116,22 +151,37 @@ pub async fn fetch_wasm( print.globeln(format!( "Downloading contract spec for wasm hash: {wasm_hash}" )); - Ok(( - Some(get_remote_wasm_from_hash(&client, &hash).await?), - None, - Some(network.clone()), - )) + let wasm_bytes = get_remote_wasm_from_hash(&client, &hash).await?; + Ok(Fetched { + contract: Contract::Wasm { wasm_bytes }, + source: Source::Wasm { + hash: wasm_hash.clone(), + network: network.clone(), + }, + }) } else if let Some(contract_id) = &args.contract_id { let contract_id = contract_id.resolve_contract_id(&args.locator, &network.network_passphrase)?; - let contract_address = xdr::ScAddress::Contract(xdr::Hash(contract_id.0)).to_string(); - print.globeln(format!("Downloading contract spec: {contract_address}")); + let derived_address = xdr::ScAddress::Contract(xdr::Hash(contract_id.0)).to_string(); + print.globeln(format!("Downloading contract spec: {derived_address}")); let res = wasm::fetch_from_contract(&contract_id, network).await; if let Some(ContractIsStellarAsset) = res.as_ref().err() { - return Ok((None, Some(contract_address), Some(network.clone()))); + return Ok(Fetched { + contract: Contract::StellarAssetContract, + source: Source::Contract { + resolved_address: derived_address, + network: network.clone(), + }, + }); } - Ok((Some(res?), Some(contract_address), Some(network.clone()))) + Ok(Fetched { + contract: Contract::Wasm { wasm_bytes: res? }, + source: Source::Contract { + resolved_address: derived_address, + network: network.clone(), + }, + }) } else { - return Err(Error::MalformedWasmOrWasmHashOrContractId); + return Err(Error::MissingArg); } } diff --git a/cmd/soroban-cli/src/commands/keys/add.rs b/cmd/soroban-cli/src/commands/keys/add.rs index 5e32944e8..a29286b0a 100644 --- a/cmd/soroban-cli/src/commands/keys/add.rs +++ b/cmd/soroban-cli/src/commands/keys/add.rs @@ -1,12 +1,17 @@ use clap::command; -use crate::config::{key, locator, secret}; +use crate::{ + commands::global, + config::{key, locator, secret}, + print::Print, +}; #[derive(thiserror::Error, Debug)] pub enum Error { + #[error(transparent)] + Secret(#[from] secret::Error), #[error(transparent)] Key(#[from] key::Error), - #[error(transparent)] Config(#[from] locator::Error), } @@ -22,12 +27,22 @@ pub struct Cmd { #[command(flatten)] pub config_locator: locator::Args, + + /// Add a public key, ed25519, or muxed account, e.g. G1.., M2.. + #[arg(long, conflicts_with = "seed_phrase", conflicts_with = "secret_key")] + pub public_key: Option, } impl Cmd { - pub fn run(&self) -> Result<(), Error> { - Ok(self - .config_locator - .write_key(&self.name, &self.secrets.read_key()?)?) + pub fn run(&self, global_args: &global::Args) -> Result<(), Error> { + let key = if let Some(key) = self.public_key.as_ref() { + key.parse()? + } else { + self.secrets.read_secret()?.into() + }; + let print = Print::new(global_args.quiet); + let path = self.config_locator.write_key(&self.name, &key)?; + print.checkln(format!("Key saved with alias {:?} in {path:?}", self.name)); + Ok(()) } } diff --git a/cmd/soroban-cli/src/commands/keys/fund.rs b/cmd/soroban-cli/src/commands/keys/fund.rs index d7100c6cb..2419c4be2 100644 --- a/cmd/soroban-cli/src/commands/keys/fund.rs +++ b/cmd/soroban-cli/src/commands/keys/fund.rs @@ -1,6 +1,6 @@ use clap::command; -use crate::config::network; +use crate::{commands::global, config::network, print::Print}; use super::address; @@ -23,12 +23,15 @@ pub struct Cmd { } impl Cmd { - pub async fn run(&self) -> Result<(), Error> { + pub async fn run(&self, global_args: &global::Args) -> Result<(), Error> { + let print = Print::new(global_args.quiet); let addr = self.address.public_key()?; - self.network - .get(&self.address.locator)? - .fund_address(&addr) - .await?; + let network = self.network.get(&self.address.locator)?; + network.fund_address(&addr).await?; + print.checkln(format!( + "Account {:?} funded on {:?}", + self.address.name, network.network_passphrase + )); Ok(()) } } diff --git a/cmd/soroban-cli/src/commands/keys/generate.rs b/cmd/soroban-cli/src/commands/keys/generate.rs index c6623386c..fda6a3d98 100644 --- a/cmd/soroban-cli/src/commands/keys/generate.rs +++ b/cmd/soroban-cli/src/commands/keys/generate.rs @@ -96,7 +96,8 @@ impl Cmd { seed_phrase }; - self.config_locator.write_identity(&self.name, &secret)?; + let path = self.config_locator.write_identity(&self.name, &secret)?; + print.checkln(format!("Key saved with alias {:?} in {path:?}", self.name)); if !self.no_fund { let addr = secret.public_key(self.hd_path)?; @@ -108,6 +109,10 @@ impl Cmd { tracing::warn!("fund_address failed: {e}"); }) .unwrap_or_default(); + print.checkln(format!( + "Account {:?} funded on {:?}", + self.name, network.network_passphrase + )); } Ok(()) diff --git a/cmd/soroban-cli/src/commands/keys/mod.rs b/cmd/soroban-cli/src/commands/keys/mod.rs index 8729ee9af..b5520abf6 100644 --- a/cmd/soroban-cli/src/commands/keys/mod.rs +++ b/cmd/soroban-cli/src/commands/keys/mod.rs @@ -8,7 +8,7 @@ pub mod fund; pub mod generate; pub mod ls; pub mod rm; -pub mod show; +pub mod secret; #[derive(Debug, Parser)] pub enum Cmd { @@ -30,8 +30,8 @@ pub enum Cmd { /// Remove an identity Rm(rm::Cmd), - /// Given an identity return its private key - Show(show::Cmd), + /// Output an identity's secret key + Secret(secret::Cmd), /// Set the default identity that will be used on all commands. /// This allows you to skip `--source-account` or setting a environment @@ -61,7 +61,7 @@ pub enum Error { Ls(#[from] ls::Error), #[error(transparent)] - Show(#[from] show::Error), + Show(#[from] secret::Error), #[error(transparent)] Default(#[from] default::Error), @@ -70,13 +70,13 @@ pub enum Error { impl Cmd { pub async fn run(&self, global_args: &global::Args) -> Result<(), Error> { match self { - Cmd::Add(cmd) => cmd.run()?, + Cmd::Add(cmd) => cmd.run(global_args)?, Cmd::Address(cmd) => cmd.run()?, - Cmd::Fund(cmd) => cmd.run().await?, + Cmd::Fund(cmd) => cmd.run(global_args).await?, Cmd::Generate(cmd) => cmd.run(global_args).await?, Cmd::Ls(cmd) => cmd.run()?, Cmd::Rm(cmd) => cmd.run()?, - Cmd::Show(cmd) => cmd.run()?, + Cmd::Secret(cmd) => cmd.run()?, Cmd::Default(cmd) => cmd.run(global_args)?, }; Ok(()) diff --git a/cmd/soroban-cli/src/commands/keys/show.rs b/cmd/soroban-cli/src/commands/keys/secret.rs similarity index 95% rename from cmd/soroban-cli/src/commands/keys/show.rs rename to cmd/soroban-cli/src/commands/keys/secret.rs index ae0faab03..38ac83116 100644 --- a/cmd/soroban-cli/src/commands/keys/show.rs +++ b/cmd/soroban-cli/src/commands/keys/secret.rs @@ -13,6 +13,7 @@ pub enum Error { #[derive(Debug, clap::Parser, Clone)] #[group(skip)] +#[command(name = "secret", alias = "show")] pub struct Cmd { /// Name of identity to lookup, default is test identity pub name: String, diff --git a/cmd/soroban-cli/src/commands/network/add.rs b/cmd/soroban-cli/src/commands/network/add.rs index 20b1afa7b..feeea9030 100644 --- a/cmd/soroban-cli/src/commands/network/add.rs +++ b/cmd/soroban-cli/src/commands/network/add.rs @@ -25,8 +25,8 @@ pub struct Cmd { impl Cmd { pub fn run(&self) -> Result<(), Error> { - Ok(self - .config_locator - .write_network(&self.name, &self.network)?) + self.config_locator + .write_network(&self.name, &self.network)?; + Ok(()) } } diff --git a/cmd/soroban-cli/src/config/locator.rs b/cmd/soroban-cli/src/config/locator.rs index 4ab698111..34f99ec69 100644 --- a/cmd/soroban-cli/src/config/locator.rs +++ b/cmd/soroban-cli/src/config/locator.rs @@ -167,7 +167,7 @@ impl Args { ) } - pub fn write_identity(&self, name: &str, secret: &Secret) -> Result<(), Error> { + pub fn write_identity(&self, name: &str, secret: &Secret) -> Result { self.write_key(name, &Key::Secret(secret.clone())) } @@ -175,15 +175,15 @@ impl Args { &self, name: &str, public_key: &stellar_strkey::ed25519::PublicKey, - ) -> Result<(), Error> { + ) -> Result { self.write_key(name, &public_key.into()) } - pub fn write_key(&self, name: &str, key: &Key) -> Result<(), Error> { + pub fn write_key(&self, name: &str, key: &Key) -> Result { KeyType::Identity.write(name, key, &self.config_dir()?) } - pub fn write_network(&self, name: &str, network: &Network) -> Result<(), Error> { + pub fn write_network(&self, name: &str, network: &Network) -> Result { KeyType::Network.write(name, network, &self.config_dir()?) } @@ -471,10 +471,14 @@ impl KeyType { key: &str, value: &T, pwd: &Path, - ) -> Result<(), Error> { + ) -> Result { let filepath = ensure_directory(self.path(pwd, key))?; let data = toml::to_string(value).map_err(|_| Error::ConfigSerialization)?; - std::fs::write(&filepath, data).map_err(|error| Error::IdCreationFailed { filepath, error }) + std::fs::write(&filepath, data).map_err(|error| Error::IdCreationFailed { + filepath: filepath.clone(), + error, + })?; + Ok(filepath) } fn root(&self, pwd: &Path) -> PathBuf { diff --git a/cmd/soroban-cli/src/config/secret.rs b/cmd/soroban-cli/src/config/secret.rs index 82008795b..530c3f6af 100644 --- a/cmd/soroban-cli/src/config/secret.rs +++ b/cmd/soroban-cli/src/config/secret.rs @@ -9,7 +9,7 @@ use crate::{ utils, }; -use super::key::{self, Key}; +use super::key::Key; #[derive(thiserror::Error, Debug)] pub enum Error { @@ -41,26 +41,19 @@ pub struct Args { /// Add using 12 word seed phrase to generate `secret_key` #[arg(long, conflicts_with = "secret_key")] pub seed_phrase: bool, - - /// Add a public key, ed25519, or muxed account, e.g. G1.., M2.. - #[arg(long, conflicts_with = "seed_phrase", conflicts_with = "secret_key")] - pub public_key: Option, } impl Args { - pub fn read_key(&self) -> Result { - if let Some(public_key) = self.public_key.as_ref() { - return public_key.parse(); - }; + pub fn read_secret(&self) -> Result { if let Ok(secret_key) = std::env::var("SOROBAN_SECRET_KEY") { - Ok(Key::Secret(Secret::SecretKey { secret_key })) + Ok(Secret::SecretKey { secret_key }) } else if self.secret_key { println!("Type a secret key: "); let secret_key = read_password()?; let secret_key = PrivateKey::from_string(&secret_key) .map_err(|_| Error::InvalidSecretKey)? .to_string(); - Ok(Key::Secret(Secret::SecretKey { secret_key })) + Ok(Secret::SecretKey { secret_key }) } else if self.seed_phrase { println!("Type a 12 word seed phrase: "); let seed_phrase = read_password()?; @@ -69,15 +62,15 @@ impl Args { // let len = seed_phrase.len(); // return Err(Error::InvalidSeedPhrase { len }); // } - Ok(Key::Secret(Secret::SeedPhrase { + Ok(Secret::SeedPhrase { seed_phrase: seed_phrase .into_iter() .map(ToString::to_string) .collect::>() .join(" "), - })) + }) } else { - Err(Error::PasswordRead {}.into()) + Err(Error::PasswordRead {}) } } } @@ -115,6 +108,12 @@ impl From for Secret { } } +impl From for Key { + fn from(value: Secret) -> Self { + Key::Secret(value) + } +} + impl Secret { pub fn private_key(&self, index: Option) -> Result { Ok(match self { @@ -153,7 +152,7 @@ impl Secret { let seed_phrase = if let Some(seed) = seed.map(str::as_bytes) { sep5::SeedPhrase::from_entropy(seed) } else { - sep5::SeedPhrase::random(sep5::MnemonicType::Words12) + sep5::SeedPhrase::random(sep5::MnemonicType::Words24) }? .seed_phrase .into_phrase();