From 71fe052649fe5b241a42236dd310aad5691998ad Mon Sep 17 00:00:00 2001 From: pselle Date: Tue, 13 Oct 2026 12:49:22 -0400 Subject: [PATCH 01/20] feat: add register_import! for testing import_contract! callers `import_contract!` bakes a real on-chain address into a contract, and nothing lives at that address in a sandboxed test `Env`. Add `stellar_registry::testutils::register_import!` (behind a new `testutils` feature), which resolves a name through the same code as `import_contract!` and registers the imported Wasm, or a native mock, at that address with `Env::register_at`. Stellar Asset Contracts are a compile error for now. The new `hello_caller` fixture imports `hello_v1` and tests itself with `register_import!`. It is excluded from the workspace, since it needs seeded cache files to compile; `just test-register-import` seeds them and runs it offline, and CI runs that recipe. Part of #65 Co-Authored-By: Claude --- .github/workflows/rust.yml | 2 + CLAUDE.md | 3 +- Cargo.toml | 2 + crates/stellar-registry-macro/README.md | 2 +- crates/stellar-registry-macro/src/contract.rs | 263 +- crates/stellar-registry-macro/src/lib.rs | 52 +- .../contracts/hello_caller/Cargo.lock | 2203 +++++++++++++++++ .../contracts/hello_caller/Cargo.toml | 27 + .../contracts/hello_caller/src/lib.rs | 74 + crates/stellar-registry/Cargo.toml | 4 + crates/stellar-registry/README.md | 49 + crates/stellar-registry/src/lib.rs | 6 + justfile | 13 + 13 files changed, 2681 insertions(+), 19 deletions(-) create mode 100644 crates/stellar-registry-test/fixtures/contracts/hello_caller/Cargo.lock create mode 100644 crates/stellar-registry-test/fixtures/contracts/hello_caller/Cargo.toml create mode 100644 crates/stellar-registry-test/fixtures/contracts/hello_caller/src/lib.rs diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml index 44eefd1..2c62532 100644 --- a/.github/workflows/rust.yml +++ b/.github/workflows/rust.yml @@ -48,3 +48,5 @@ jobs: run: just clippy-test - name: Unit Tests run: just test + - name: Fixture tests (import_contract! + register_import!) + run: just test-register-import diff --git a/CLAUDE.md b/CLAUDE.md index 1d40e81..4cb4d51 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -42,7 +42,7 @@ Note: the `justfile` still carries some recipes from the monorepo. Prefer the `c | `stellar-registry-cli` | The `stellar registry` CLI: `publish`, `deploy`, `download`, `install`/`create-alias`, `upgrade`, `register-contract` | | `stellar-registry-build` | Library for interacting with the registry at build time | | `stellar-registry-macro` | Macro crate defining procedural macros `import_contract!`, `import_contract_client!`, and `import_asset`. Proc-macro crates are special and need to only export proc-macros. | -| `stellar-registry` | Re-exports the `import_contract!`, `import_contract_client!`, and `import_asset!` macros from `stellar-registry-macro`. Might export more behavior later. (published to crates.io; dev-dependency of `stellar-registry/contracts`) | +| `stellar-registry` | Re-exports the `import_contract!`, `import_contract_client!`, and `import_asset!` macros from `stellar-registry-macro`. Its `testutils` feature adds `testutils::register_import!` for testing contracts that use `import_contract!`. Might export more behavior later. (published to crates.io; dev-dependency of `stellar-registry/contracts`) | | `stellar-registry-name` | Defines standard name parsing/formatting used by `stellar-registry-macro` and `stellar-registry-cli` | | `stellar-registry-test` | Unpublished testing tools used throughout this monorepo as a dev-dependency | @@ -57,6 +57,7 @@ Note: the `justfile` still carries some recipes from the monorepo. Prefer the `c - Unit tests run without external dependencies. - Integration tests require a local Stellar RPC (Docker `stellar/quickstart`) and the `integration-tests` feature flag. +- `just test-register-import` runs the `hello_caller` fixture, which is excluded from the workspace because `import_contract!` needs seeded cache files to compile. - Command tests use `stellar_registry_test::RegistryTest`, defined in local crate `crates/stellar-registry-test` ## Cross-repo dependencies diff --git a/Cargo.toml b/Cargo.toml index 29dd402..ffadd20 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -2,6 +2,8 @@ resolver = "2" default-members = ["crates/*"] members = ["crates/*", "crates/stellar-registry-test/fixtures/contracts/*"] +# Needs seeded `import_contract!` cache files to compile; see its Cargo.toml. +exclude = ["crates/stellar-registry-test/fixtures/contracts/hello_caller"] [workspace.package] repository = "https://github.com/stellar-registry/cli" diff --git a/crates/stellar-registry-macro/README.md b/crates/stellar-registry-macro/README.md index 22ce223..b7ad408 100644 --- a/crates/stellar-registry-macro/README.md +++ b/crates/stellar-registry-macro/README.md @@ -1,6 +1,6 @@ # stellar-registry-macro -The [macro crate] defining `import_contract!`, `import_contract_client!`, and `import_asset!` which get re-exported by the [stellar-registry] crate. +The [macro crate] defining `import_contract!`, `import_contract_client!`, `import_asset!`, and the test helper `register_import!`, which get re-exported by the [stellar-registry] crate. [macro crate]: https://www.reddit.com/r/rust/comments/tuxawv/why_do_procedural_macros_have_to_be_defined_in_a/ [stellar-registry]: https://crates.io/crates/stellar-registry diff --git a/crates/stellar-registry-macro/src/contract.rs b/crates/stellar-registry-macro/src/contract.rs index 48bf7a9..73c2bc8 100644 --- a/crates/stellar-registry-macro/src/contract.rs +++ b/crates/stellar-registry-macro/src/contract.rs @@ -31,6 +31,57 @@ impl Parse for Input { } } +/// `register_import!(env_expr, name [, args = (..)] [, contract = Mock])` — +/// `name` as in [`Input`]; the options may come in either order. +struct RegisterInput { + env: Expr, + name: Name, + /// Constructor arguments; `()` when omitted. + args: Option, + /// A native contract to register instead of the imported Wasm. + contract: Option, +} + +impl Parse for RegisterInput { + fn parse(input: ParseStream) -> syn::Result { + let Input { env, name } = input.parse()?; + let (mut args, mut contract) = (None, None); + while !input.is_empty() { + input.parse::()?; + if input.is_empty() { + break; + } + let key: Ident = input.parse()?; + let slot = match key.to_string().as_str() { + "args" => &mut args, + "contract" => &mut contract, + other => { + return Err(syn::Error::new( + key.span(), + format!( + "unknown option `{other}`: expected `args = (..)` or `contract = ..`" + ), + )); + } + }; + if slot.is_some() { + return Err(syn::Error::new( + key.span(), + format!("`{key}` given more than once"), + )); + } + input.parse::()?; + *slot = Some(input.parse()?); + } + Ok(Self { + env, + name, + args, + contract, + }) + } +} + /// Whether `raw` names an asset rather than a registered contract: exactly /// `native`/`xlm`, or a `CODE:ISSUER` literal. Colons are never valid in a /// registry name (`Prefixed` rejects them outright), so `raw.contains(':')` @@ -275,10 +326,24 @@ fn expand_token_client(env: &Expr, address: &str) -> proc_macro2::TokenStream { } } -pub(crate) fn import_contract( - input: proc_macro::TokenStream, -) -> syn::Result { - let Input { env, name } = syn::parse(input)?; +/// What a name resolved to at build time. +#[derive(Debug)] +enum Resolved { + /// A Wasm-backed contract: its deployed address and cached Wasm. + Wasm { + mod_ident: Ident, + wasm_path: PathBuf, + address: String, + }, + /// A Stellar Asset Contract: asset syntax, or a registered name that + /// points at one. There is no Wasm. + Sac { address: String }, +} + +/// Resolve `name` for `macro_name!` — shared by `import_contract!` and +/// `register_import!` so a test registers at exactly the address (and with +/// exactly the Wasm) the contract under test was compiled against. +fn resolve(name: &Name, macro_name: &str) -> syn::Result { let span = name.span(); let err = |msg: String| syn::Error::new(span, msg); @@ -287,8 +352,8 @@ pub(crate) fn import_contract( let raw = name.raw(); if raw.contains('@') { return Err(err(format!( - "import_contract! does not take a version — a deployed contract has no version \ - (got {raw:?}). Use just the contract name, e.g. `import_contract!(env, our_dao)`." + "{macro_name}! does not take a version — a deployed contract has no version \ + (got {raw:?}). Use just the contract name, e.g. `{macro_name}!(env, our_dao)`." ))); } @@ -299,7 +364,9 @@ pub(crate) fn import_contract( let (contract_id, _code) = asset::generate_asset_id(&raw, &stellar_build::Network::passphrase_from_env()) .map_err(err)?; - return Ok(expand_token_client(&env, &contract_id.to_string())); + return Ok(Resolved::Sac { + address: format!("{contract_id}"), + }); } let contract: Prefixed = name.parse_as()?; @@ -335,7 +402,7 @@ pub(crate) fn import_contract( if cached_sac_address.is_some() && !wasm_is_stale(cached_sac_address.as_deref(), &address, no_registry) { - return Ok(expand_token_client(&env, &address)); + return Ok(Resolved::Sac { address }); } // 3. Ensure the deployed contract's wasm is on disk for `contractimport!`, @@ -357,19 +424,83 @@ pub(crate) fn import_contract( // doesn't repeat a fetch that can only fail the same way again. Err(e) if is_stellar_asset_fetch_error(&e) => { record_sac_marker(&sac_marker_path, &address); - return Ok(expand_token_client(&env, &address)); + return Ok(Resolved::Sac { address }); } Err(e) => return Err(err(e)), } } - // 4. Generate the client from that wasm and bind it to the address. - Ok(expand( - &env, - &mod_ident, - &wasm_path.to_string_lossy(), - &address, - )) + Ok(Resolved::Wasm { + mod_ident, + wasm_path, + address, + }) +} + +pub(crate) fn import_contract( + input: proc_macro::TokenStream, +) -> syn::Result { + let Input { env, name } = syn::parse(input)?; + Ok(match resolve(&name, "import_contract")? { + Resolved::Wasm { + mod_ident, + wasm_path, + address, + } => expand(&env, &mod_ident, &wasm_path.to_string_lossy(), &address), + Resolved::Sac { address } => expand_token_client(&env, &address), + }) +} + +/// Emit a block expression registering a contract at the address +/// `import_contract!` baked in: the imported Wasm, or `contract` (a native +/// mock) when given. Evaluates to the `Address`. +fn expand_register( + input: &RegisterInput, + resolved: &Resolved, +) -> syn::Result { + let (wasm_path, address) = match resolved { + Resolved::Wasm { + wasm_path, address, .. + } => (wasm_path.to_string_lossy(), address), + Resolved::Sac { .. } => { + return Err(syn::Error::new( + input.name.span(), + format!( + "register_import! does not support Stellar Asset Contracts yet, and `{}` \ + resolves to one. SAC support is coming; until then, register a test asset \ + yourself with `Env::register_stellar_asset_contract_v2`.", + input.name.raw() + ), + )); + } + }; + let env = &input.env; + let contract = input.contract.as_ref().map_or_else( + || quote!(::core::include_bytes!(#wasm_path).as_slice()), + |contract| quote!(#contract), + ); + let args = input + .args + .as_ref() + .map_or_else(|| quote!(()), |args| quote!(#args)); + Ok(quote! { + { + let __env: &::soroban_sdk::Env = #env; + ::soroban_sdk::Env::register_at( + __env, + &::soroban_sdk::Address::from_str(__env, #address), + #contract, + #args, + ) + } + }) +} + +pub(crate) fn register_import( + input: proc_macro::TokenStream, +) -> syn::Result { + let input: RegisterInput = syn::parse(input)?; + expand_register(&input, &resolve(&input.name, "register_import")?) } #[cfg(test)] @@ -630,3 +761,103 @@ mod codegen { assert!(err.contains("invalid issuer"), "{err}"); } } + +#[cfg(test)] +mod register { + use super::*; + use proc_macro2::Span; + use quote::quote; + use syn::{Ident, parse2}; + + const A: &str = "CBESJIMX7J53SWJGJ7WQ6QTLJI4S5LPPJNC2BNVD63GIKAYCDTDOO322"; + + fn wasm() -> Resolved { + Resolved::Wasm { + mod_ident: Ident::new("our_dao", Span::call_site()), + wasm_path: "/tmp/target/stellar/local/deployed/our_dao.wasm".into(), + address: A.to_string(), + } + } + + fn expanded(tokens: proc_macro2::TokenStream, resolved: &Resolved) -> String { + let input: RegisterInput = parse2(tokens).unwrap(); + expand_register(&input, resolved).unwrap().to_string() + } + + fn parse_err(tokens: proc_macro2::TokenStream) -> String { + parse2::(tokens).err().unwrap().to_string() + } + + #[test] + fn parses_name_only() { + let input: RegisterInput = parse2(quote!(&env, "unverified/our-dao")).unwrap(); + assert_eq!(input.name.raw(), "unverified/our-dao"); + assert!(input.args.is_none()); + assert!(input.contract.is_none()); + } + + #[test] + fn parses_options_in_either_order_with_trailing_comma() { + for tokens in [ + quote!(&env, our_dao, args = (&admin, 7u32), contract = MockDao,), + quote!(&env, our_dao, contract = MockDao, args = (&admin, 7u32)), + ] { + let input: RegisterInput = parse2(tokens).unwrap(); + assert!(input.args.is_some()); + assert!(input.contract.is_some()); + } + } + + #[test] + fn rejects_unknown_and_repeated_options() { + let msg = parse_err(quote!(&env, our_dao, wasm = X)); + assert!(msg.contains("unknown option `wasm`"), "{msg}"); + let msg = parse_err(quote!(&env, our_dao, args = (), args = ())); + assert!(msg.contains("`args` given more than once"), "{msg}"); + // A positional third argument is not accepted. + assert!(parse2::(quote!(&env, our_dao, (admin,))).is_err()); + } + + #[test] + fn registers_the_cached_wasm_at_the_baked_address() { + let out = expanded(quote!(&env, our_dao), &wasm()); + assert!(out.contains("Env :: register_at"), "{out}"); + assert!(out.contains("include_bytes !"), "{out}"); + assert!(out.contains("our_dao.wasm"), "{out}"); + assert!(out.contains(A), "bakes the resolved id: {out}"); + assert!( + out.contains(". as_slice () , () ,"), + "no constructor args by default: {out}" + ); + } + + #[test] + fn passes_constructor_args_through() { + let out = expanded(quote!(&env, our_dao, args = (&admin, 7u32)), &wasm()); + assert!(out.contains("(& admin , 7u32)"), "{out}"); + } + + #[test] + fn contract_option_replaces_the_wasm() { + let out = expanded(quote!(&env, our_dao, contract = mocks::MockDao), &wasm()); + assert!(out.contains("mocks :: MockDao"), "{out}"); + assert!(!out.contains("include_bytes"), "{out}"); + assert!(out.contains(A), "still at the baked address: {out}"); + } + + #[test] + fn stellar_asset_contracts_are_a_clear_compile_error() { + let sac = Resolved::Sac { + address: A.to_string(), + }; + for tokens in [ + quote!(&env, xlm), + quote!(&env, "circle/usdc", contract = MockToken), + ] { + let input: RegisterInput = parse2(tokens).unwrap(); + let msg = expand_register(&input, &sac).unwrap_err().to_string(); + assert!(msg.contains("Stellar Asset Contracts"), "{msg}"); + assert!(msg.contains("SAC support is coming"), "{msg}"); + } + } +} diff --git a/crates/stellar-registry-macro/src/lib.rs b/crates/stellar-registry-macro/src/lib.rs index 56f9268..ad405e2 100644 --- a/crates/stellar-registry-macro/src/lib.rs +++ b/crates/stellar-registry-macro/src/lib.rs @@ -1,7 +1,8 @@ //! Proc macros for the Stellar Registry: import deployed contracts //! (`import_contract!`), published wasms (`import_contract_client!`), and //! Stellar assets (`import_asset!`) as type-safe soroban clients, resolved at -//! build time. +//! build time — plus `register_import!` for testing contracts that use +//! `import_contract!`. extern crate proc_macro; use proc_macro::TokenStream; @@ -63,11 +64,60 @@ use util::ProcMacroWrapper as _; /// resolved address turns out to be a built-in asset contract rather than a /// wasm-backed one, the same `token::TokenClient` is bound to it instead of /// generating types from a (nonexistent) fetched wasm. +/// +/// # Testing +/// +/// Nothing lives at the baked address in a test `Env`; use +/// [`register_import!`] to register the imported contract (or a mock) there. #[proc_macro] pub fn import_contract(input: TokenStream) -> TokenStream { contract::import_contract(input).to_token_stream() } +/// Register an [`import_contract!`]-ed contract in a test `Env`, at the +/// address `import_contract!` baked in. Evaluates to that `Address`. +/// +/// `import_contract!(env, our_dao)` compiles a real on-chain address into +/// your contract, but nothing lives at that address in a sandboxed test +/// `Env`. `register_import!` puts the imported contract there, so the code +/// under test runs unchanged: +/// +/// ```ignore +/// use stellar_registry::testutils::register_import; +/// +/// let env = Env::default(); +/// // The imported contract's own Wasm, as cached by `import_contract!`: +/// register_import!(&env, our_dao); +/// // ...with constructor arguments: +/// register_import!(&env, our_dao, args = (&admin, 7u32)); +/// // ...or a native mock in place of the Wasm: +/// register_import!(&env, our_dao, contract = MockDao); +/// ``` +/// +/// - `env` is a `&Env`; the name is written exactly as in `import_contract!` +/// and resolved by the same code, so both macros always agree on the +/// address and Wasm (same cache files, same `STELLAR_NO_REGISTRY` +/// handling, same flagged-contract check). +/// - `args = (..)` is the constructor-argument tuple, as for `Env::register` +/// (`()` when omitted). +/// - `contract = ` registers a native `#[contract]` type instead of the +/// Wasm, for when you want to control what the dependency returns. `args` +/// then go to the mock's constructor. +/// +/// Exported as `stellar_registry::testutils::register_import!`, behind +/// `stellar-registry`'s `testutils` feature. The expansion calls +/// `Env::register_at`, so `soroban-sdk`'s `testutils` feature must be enabled +/// too. +/// +/// Stellar Asset Contracts — `xlm`/`native`, `"CODE:ISSUER"`, or a registered +/// name that resolves to a SAC — are not supported yet and fail to compile; +/// register a test asset with `Env::register_stellar_asset_contract_v2` +/// instead. +#[proc_macro] +pub fn register_import(input: TokenStream) -> TokenStream { + contract::register_import(input).to_token_stream() +} + /// Generate a contract client from a published wasm — from your workspace's /// `target` directory if present, otherwise downloaded from the registry. /// diff --git a/crates/stellar-registry-test/fixtures/contracts/hello_caller/Cargo.lock b/crates/stellar-registry-test/fixtures/contracts/hello_caller/Cargo.lock new file mode 100644 index 0000000..b79b0ad --- /dev/null +++ b/crates/stellar-registry-test/fixtures/contracts/hello_caller/Cargo.lock @@ -0,0 +1,2203 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 4 + +[[package]] +name = "ahash" +version = "0.8.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a15f179cd60c4584b8a8c596927aadc462e27f2ca70c04e0071964a73ba7a75" +dependencies = [ + "cfg-if", + "once_cell", + "version_check", + "zerocopy", +] + +[[package]] +name = "allocator-api2" +version = "0.2.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "683d7910e743518b0e34f1186f92494becacb047c7b6bf616c96772180fef923" + +[[package]] +name = "android_system_properties" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae221649c9976a6f6c56ae1facf410f3ddb33cc661c4b7b61020a912d4237fbc" +dependencies = [ + "libc", +] + +[[package]] +name = "arbitrary" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7d5a26814d8dcb93b0e5a0ff3c6d80a8843bafb21b39e8e18a6f05471870e110" +dependencies = [ + "derive_arbitrary", +] + +[[package]] +name = "ark-bls12-381" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3df4dcc01ff89867cd86b0da835f23c3f02738353aaee7dde7495af71363b8d5" +dependencies = [ + "ark-ec", + "ark-ff", + "ark-serialize", + "ark-std", +] + +[[package]] +name = "ark-bn254" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d69eab57e8d2663efa5c63135b2af4f396d66424f88954c21104125ab6b3e6bc" +dependencies = [ + "ark-ec", + "ark-ff", + "ark-std", +] + +[[package]] +name = "ark-ec" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "43d68f2d516162846c1238e755a7c4d131b892b70cc70c471a8e3ca3ed818fce" +dependencies = [ + "ahash", + "ark-ff", + "ark-poly", + "ark-serialize", + "ark-std", + "educe", + "fnv", + "hashbrown 0.15.5", + "itertools", + "num-bigint", + "num-integer", + "num-traits", + "zeroize", +] + +[[package]] +name = "ark-ff" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a177aba0ed1e0fbb62aa9f6d0502e9b46dad8c2eab04c14258a1212d2557ea70" +dependencies = [ + "ark-ff-asm", + "ark-ff-macros", + "ark-serialize", + "ark-std", + "arrayvec", + "digest 0.10.7", + "educe", + "itertools", + "num-bigint", + "num-traits", + "paste", + "zeroize", +] + +[[package]] +name = "ark-ff-asm" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62945a2f7e6de02a31fe400aa489f0e0f5b2502e69f95f853adb82a96c7a6b60" +dependencies = [ + "quote", + "syn 2.0.119", +] + +[[package]] +name = "ark-ff-macros" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09be120733ee33f7693ceaa202ca41accd5653b779563608f1234f78ae07c4b3" +dependencies = [ + "num-bigint", + "num-traits", + "proc-macro2", + "quote", + "syn 2.0.119", +] + +[[package]] +name = "ark-poly" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "579305839da207f02b89cd1679e50e67b4331e2f9294a57693e5051b7703fe27" +dependencies = [ + "ahash", + "ark-ff", + "ark-serialize", + "ark-std", + "educe", + "fnv", + "hashbrown 0.15.5", +] + +[[package]] +name = "ark-serialize" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f4d068aaf107ebcd7dfb52bc748f8030e0fc930ac8e360146ca54c1203088f7" +dependencies = [ + "ark-serialize-derive", + "ark-std", + "arrayvec", + "digest 0.10.7", + "num-bigint", +] + +[[package]] +name = "ark-serialize-derive" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "213888f660fddcca0d257e88e54ac05bca01885f258ccdf695bafd77031bb69d" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.119", +] + +[[package]] +name = "ark-std" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "246a225cc6131e9ee4f24619af0f19d67761fff15d7ccc22e42b80846e69449a" +dependencies = [ + "num-traits", + "rand", +] + +[[package]] +name = "arrayvec" +version = "0.7.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3fb67a6e08acf24fdeccbac2cb6ac4305825bd1f117462e0e6f2f193345ad56" + +[[package]] +name = "autocfg" +version = "1.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2032f911046de80f0a198e0901378627c33f59ea0ac00e363d481118bd70a53" + +[[package]] +name = "base16ct" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c7f02d4ea65f2c1853089ffd8d2787bdbc63de2f0d29dedbcf8ccdfa0ccd4cf" + +[[package]] +name = "base64" +version = "0.22.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" + +[[package]] +name = "base64" +version = "0.23.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac07cdecf99051d9a5238b80f35af32cdeba5b336e55d957b318b50137e18da5" + +[[package]] +name = "base64ct" +version = "1.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2af50177e190e07a26ab74f8b1efbfe2ef87da2116221318cb1c2e82baf7de06" + +[[package]] +name = "bitflags" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" + +[[package]] +name = "block-buffer" +version = "0.10.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" +dependencies = [ + "generic-array", +] + +[[package]] +name = "block-buffer" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d2f6c7dbe95a6ed67ad9f18e57daf93a2f034c524b99fd2b76d18fdfeb6660aa" +dependencies = [ + "hybrid-array", +] + +[[package]] +name = "bs58" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bf88ba1141d185c399bee5288d850d63b8369520c1eafc32a0430b5b6c287bf4" +dependencies = [ + "tinyvec", +] + +[[package]] +name = "bumpalo" +version = "3.20.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72f5acc6cb2ba439de613abc23857ec3d78374d8ed5ac84e9d11336e87da8649" + +[[package]] +name = "byteorder" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" + +[[package]] +name = "bytes-lit" +version = "0.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b04f2b1d34cb428043f14aa4c853d14294532e8bbde3b6a3bc2faaaae31a1dd" +dependencies = [ + "num-bigint", + "proc-macro2", + "quote", + "syn 2.0.119", +] + +[[package]] +name = "camino" +version = "1.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbbad30e4b4c14a39e3cc8aed085a12a327257c316619c93581e017bc52be591" +dependencies = [ + "serde_core", +] + +[[package]] +name = "cargo-platform" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e35af189006b9c0f00a064685c727031e3ed2d8020f7ba284d78cc2671bd36ea" +dependencies = [ + "serde", +] + +[[package]] +name = "cargo_metadata" +version = "0.18.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2d886547e41f740c616ae73108f6eb70afe6d940c7bc697cb30f13daec073037" +dependencies = [ + "camino", + "cargo-platform", + "semver", + "serde", + "serde_json", + "thiserror 1.0.69", +] + +[[package]] +name = "cc" +version = "1.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6651c9ed80effdc7db0ff72512157f901af5e3549e341e24b1dd4887d836d838" +dependencies = [ + "find-msvc-tools", + "shlex", +] + +[[package]] +name = "cfg-if" +version = "1.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4e7648175b45a9a48536d676f68d918270699102aa8dab5496df06904c914600" + +[[package]] +name = "cfg_eval" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "45565fc9416b9896014f5732ac776f810ee53a66730c17e4020c3ec064a8f88f" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.119", +] + +[[package]] +name = "chrono" +version = "0.4.45" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1aa79e62e7697b8e29b513a68abacf485adcd1fe8284a4316c5ae868e6633327" +dependencies = [ + "iana-time-zone", + "num-traits", + "serde", + "windows-link", +] + +[[package]] +name = "const-oid" +version = "0.9.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c2459377285ad874054d797f3ccebf984978aa39129f6eafde5cdc8315b612f8" + +[[package]] +name = "core-foundation-sys" +version = "0.8.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" + +[[package]] +name = "cpufeatures" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "59ed5838eebb26a2bb2e58f6d5b5316989ae9d08bab10e0e6d103e656d1b0280" +dependencies = [ + "libc", +] + +[[package]] +name = "cpufeatures" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5ca28b0ae3115b884660db4118d803791fd6756b6e88f39c0f3f7859060d7566" +dependencies = [ + "libc", +] + +[[package]] +name = "crate-git-revision" +version = "0.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c521bf1f43d31ed2f73441775ed31935d77901cb3451e44b38a1c1612fcbaf98" +dependencies = [ + "serde", + "serde_derive", + "serde_json", +] + +[[package]] +name = "crate-git-revision" +version = "0.0.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "54851b5b3f24621804b1cded2820975623c205e3055d2d44031cdb1237339ac8" +dependencies = [ + "serde", + "serde_derive", + "serde_json", +] + +[[package]] +name = "crypto-bigint" +version = "0.5.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0dc92fb57ca44df6db8059111ab3af99a63d5d0f8375d9972e319a379c6bab76" +dependencies = [ + "generic-array", + "rand_core", + "subtle", + "zeroize", +] + +[[package]] +name = "crypto-common" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78c8292055d1c1df0cce5d180393dc8cce0abec0a7102adb6c7b1eef6016d60a" +dependencies = [ + "generic-array", + "typenum", +] + +[[package]] +name = "crypto-common" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ce6e4c961d6cd6c9a86db418387425e8bdeaf05b3c8bc1411e6dca4c252f1453" +dependencies = [ + "hybrid-array", +] + +[[package]] +name = "ctor" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67773048316103656a637612c4a62477603b777d91d9c62ff2290f9cde178fdb" +dependencies = [ + "ctor-proc-macro", + "dtor", +] + +[[package]] +name = "ctor-proc-macro" +version = "0.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2931af7e13dc045d8e9d26afccc6fa115d64e115c9c84b1166288b46f6782c2" + +[[package]] +name = "curve25519-dalek" +version = "4.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97fb8b7c4503de7d6ae7b42ab72a5a59857b4c937ec27a3d4539dba95b5ab2be" +dependencies = [ + "cfg-if", + "cpufeatures 0.2.17", + "curve25519-dalek-derive", + "digest 0.10.7", + "fiat-crypto 0.2.9", + "rustc_version", + "subtle", + "zeroize", +] + +[[package]] +name = "curve25519-dalek" +version = "5.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b5eed333089e2e1c1ac8c6c0398e5e2497b4c9926ca6d0365ed1e099afa5bc23" +dependencies = [ + "cfg-if", + "cpufeatures 0.3.1", + "curve25519-dalek-derive", + "digest 0.11.3", + "fiat-crypto 0.3.0", + "rustc_version", + "subtle", +] + +[[package]] +name = "curve25519-dalek-derive" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f46882e17999c6cc590af592290432be3bce0428cb0d5f8b6715e4dc7b383eb3" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.119", +] + +[[package]] +name = "darling" +version = "0.20.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc7f46116c46ff9ab3eb1597a45688b6715c6e628b5c133e288e709a29bcb4ee" +dependencies = [ + "darling_core 0.20.11", + "darling_macro 0.20.11", +] + +[[package]] +name = "darling" +version = "0.24.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed17f5901b6630b993ca003def43f2f8ef4014fc13b047b57aad617ff32bc2ec" +dependencies = [ + "darling_core 0.24.1", + "darling_macro 0.24.1", +] + +[[package]] +name = "darling_core" +version = "0.20.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0d00b9596d185e565c2207a0b01f8bd1a135483d02d9b7b0a54b11da8d53412e" +dependencies = [ + "fnv", + "ident_case", + "proc-macro2", + "quote", + "strsim", + "syn 2.0.119", +] + +[[package]] +name = "darling_core" +version = "0.24.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6837e2cf7485aaae18f86181d2f0e9a7ed297a025e220aeabf63fdebd3a2ddff" +dependencies = [ + "ident_case", + "proc-macro2", + "quote", + "strsim", + "syn 3.0.9", +] + +[[package]] +name = "darling_macro" +version = "0.20.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc34b93ccb385b40dc71c6fceac4b2ad23662c7eeb248cf10d529b7e055b6ead" +dependencies = [ + "darling_core 0.20.11", + "quote", + "syn 2.0.119", +] + +[[package]] +name = "darling_macro" +version = "0.24.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2ac7135c3ef02b2f7833bbeb1be5ba7f966dcde8a87c6b87f65a778d71a02785" +dependencies = [ + "darling_core 0.24.1", + "quote", + "syn 3.0.9", +] + +[[package]] +name = "data-encoding" +version = "2.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4583a4551df46e2792f82ceeac45e850d2e2d5debba0b91f102385cda5b11f06" + +[[package]] +name = "defmt" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2953bfe4f93bbd20cc71198842756f77d161884c99ebbabc41d80231ded88d1" +dependencies = [ + "bitflags", + "defmt-macros", +] + +[[package]] +name = "defmt-macros" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bad9c72e7ca2137e0dc3813245a0d282fd6daad32fd800af018306a9169b5fe8" +dependencies = [ + "defmt-parser", + "proc-macro2", + "quote", + "syn 2.0.119", +] + +[[package]] +name = "defmt-parser" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "10d60334b3b2e7c9d91ef8150abfb6fa4c1c39ebbcf4a81c2e346aad939fee3e" +dependencies = [ + "thiserror 2.0.21", +] + +[[package]] +name = "der" +version = "0.7.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e7c1832837b905bbfb5101e07cc24c8deddf52f93225eee6ead5f4d63d53ddcb" +dependencies = [ + "const-oid", + "zeroize", +] + +[[package]] +name = "deranged" +version = "0.5.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e9de72ce2ad1f90dc62fa25f0f430ef85eb4b0d8fa0be4f30373bc40a21d28e" +dependencies = [ + "serde_core", +] + +[[package]] +name = "derive_arbitrary" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67e77553c4162a157adbf834ebae5b415acbecbeafc7a74b0e886657506a7611" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.119", +] + +[[package]] +name = "digest" +version = "0.10.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" +dependencies = [ + "block-buffer 0.10.4", + "const-oid", + "crypto-common 0.1.7", + "subtle", +] + +[[package]] +name = "digest" +version = "0.11.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1dd6dbb5841937940781866fa1281a1ff7bd3bf827091440879f9994983d5c2" +dependencies = [ + "block-buffer 0.12.1", + "crypto-common 0.2.2", +] + +[[package]] +name = "downcast-rs" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75b325c5dbd37f80359721ad39aca5a29fb04c89279657cffdda8736d0c0b9d2" + +[[package]] +name = "dtor" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "404d02eeb088a82cfd873006cb713fe411306c7d182c344905e101fb1167d301" +dependencies = [ + "dtor-proc-macro", +] + +[[package]] +name = "dtor-proc-macro" +version = "0.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f678cf4a922c215c63e0de95eb1ff08a958a81d47e485cf9da1e27bf6305cfa5" + +[[package]] +name = "dyn-clone" +version = "1.0.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d0881ea181b1df73ff77ffaaf9c7544ecc11e82fba9b5f27b262a3c73a332555" + +[[package]] +name = "ecdsa" +version = "0.16.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee27f32b5c5292967d2d4a9d7f1e0b0aed2c15daded5a60300e4abb9d8020bca" +dependencies = [ + "der", + "digest 0.10.7", + "elliptic-curve", + "rfc6979", + "signature", +] + +[[package]] +name = "ed25519" +version = "2.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "115531babc129696a58c64a4fef0a8bf9e9698629fb97e9e40767d235cfbcd53" +dependencies = [ + "pkcs8", + "signature", +] + +[[package]] +name = "ed25519-dalek" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "70e796c081cee67dc755e1a36a0a172b897fab85fc3f6bc48307991f64e4eca9" +dependencies = [ + "curve25519-dalek 4.1.3", + "ed25519", + "rand_core", + "serde", + "sha2", + "subtle", + "zeroize", +] + +[[package]] +name = "educe" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d7bc049e1bd8cdeb31b68bbd586a9464ecf9f3944af3958a7a9d0f8b9799417" +dependencies = [ + "enum-ordinalize", + "proc-macro2", + "quote", + "syn 2.0.119", +] + +[[package]] +name = "either" +version = "1.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0e9c71c2167ca323c882b99918929403426e2373ea17242ff5653e0d5e1058be" + +[[package]] +name = "elliptic-curve" +version = "0.13.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b5e6043086bf7973472e0c7dff2142ea0b680d30e18d9cc40f267efbf222bd47" +dependencies = [ + "base16ct", + "crypto-bigint", + "digest 0.10.7", + "ff", + "generic-array", + "group", + "rand_core", + "sec1", + "subtle", + "zeroize", +] + +[[package]] +name = "enum-ordinalize" +version = "4.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "89dd01549b09589510cf0647475075d12071456586d70f5c75c98ae2a5537677" +dependencies = [ + "enum-ordinalize-derive", +] + +[[package]] +name = "enum-ordinalize-derive" +version = "4.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a65863d15a4ce2888bd2f0f543cc963d3879c3a022c8ee43f6141d479a3ac815" +dependencies = [ + "proc-macro2", + "quote", + "syn 3.0.9", +] + +[[package]] +name = "equivalent" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" + +[[package]] +name = "escape-bytes" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2bfcf67fea2815c2fc3b90873fae90957be12ff417335dfadc7f52927feb03b2" + +[[package]] +name = "ethnum" +version = "1.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "40404c3f5f511ec4da6fe866ddf6a717c309fdbb69fbbad7b0f3edab8f2e835f" + +[[package]] +name = "ff" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c0b50bfb653653f9ca9095b427bed08ab8d75a137839d9ad64eb11810d5b6393" +dependencies = [ + "rand_core", + "subtle", +] + +[[package]] +name = "fiat-crypto" +version = "0.2.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "28dea519a9695b9977216879a3ebfddf92f1c08c05d984f8996aecd6ecdc811d" + +[[package]] +name = "fiat-crypto" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "64cd1e32ddd350061ae6edb1b082d7c54915b5c672c389143b9a63403a109f24" + +[[package]] +name = "find-msvc-tools" +version = "0.1.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aedcfb3409746eddb02b9e19ebda1c3394f759a152e48ee875a0844d1b955484" + +[[package]] +name = "fnv" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" + +[[package]] +name = "futures-core" +version = "0.3.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "92d699e522242e69e3003b94ecc1f960f3a5e015aa7c5d7486e65ad01dd94f5e" + +[[package]] +name = "futures-task" +version = "0.3.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cd417de3d1d015fc3bfd2b1ea46dfc7bab72ef86f1cc7cc9c78e728b34a6d1fd" + +[[package]] +name = "futures-util" +version = "0.3.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0d50a92467f8ba5dd6e3ee5d4bd04d73ab2e4e1c44474a0674821dfce14b79bc" +dependencies = [ + "futures-core", + "futures-task", + "pin-project-lite", + "slab", +] + +[[package]] +name = "generic-array" +version = "0.14.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" +dependencies = [ + "typenum", + "version_check", + "zeroize", +] + +[[package]] +name = "getrandom" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff2abc00be7fca6ebc474524697ae276ad847ad0a6b3faa4bcb027e9a4614ad0" +dependencies = [ + "cfg-if", + "js-sys", + "libc", + "wasi", + "wasm-bindgen", +] + +[[package]] +name = "group" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0f9ef7462f7c099f518d754361858f86d8a07af53ba9af0fe635bbccb151a63" +dependencies = [ + "ff", + "rand_core", + "subtle", +] + +[[package]] +name = "hash32" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "47d60b12902ba28e2730cd37e95b8c9223af2808df9e902d4df49588d1470606" +dependencies = [ + "byteorder", +] + +[[package]] +name = "hashbrown" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" + +[[package]] +name = "hashbrown" +version = "0.15.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9229cfe53dfd69f0609a49f65461bd93001ea1ef889cd5529dd176593f5338a1" +dependencies = [ + "allocator-api2", +] + +[[package]] +name = "hashbrown" +version = "0.17.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed5909b6e89a2db4456e54cd5f673791d7eca6732202bbf2a9cc504fe2f9b84a" + +[[package]] +name = "heapless" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0bfb9eb618601c89945a70e254898da93b13be0388091d42117462b265bb3fad" +dependencies = [ + "hash32", + "stable_deref_trait", +] + +[[package]] +name = "heck" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" + +[[package]] +name = "hello_caller" +version = "0.0.0" +dependencies = [ + "soroban-sdk", + "stellar-registry", +] + +[[package]] +name = "hex" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" +dependencies = [ + "serde", +] + +[[package]] +name = "hex-literal" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6fe2267d4ed49bc07b63801559be28c718ea06c4738b7a03c94df7386d2cde46" + +[[package]] +name = "hmac" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c49c37c09c17a53d937dfbb742eb3a961d65a994e6bcdcf37e7399d0cc8ab5e" +dependencies = [ + "digest 0.10.7", +] + +[[package]] +name = "hybrid-array" +version = "0.4.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "27f864f10dfb56725ce5ce5472bc52252c8f93a4ab86327122cebf62c5f59a17" +dependencies = [ + "typenum", +] + +[[package]] +name = "iana-time-zone" +version = "0.1.65" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e31bc9ad994ba00e440a8aa5c9ef0ec67d5cb5e5cb0cc7f8b744a35b389cc470" +dependencies = [ + "android_system_properties", + "core-foundation-sys", + "iana-time-zone-haiku", + "js-sys", + "log", + "wasm-bindgen", + "windows-core", +] + +[[package]] +name = "iana-time-zone-haiku" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f" +dependencies = [ + "cc", +] + +[[package]] +name = "ident_case" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" + +[[package]] +name = "indexmap" +version = "1.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bd070e393353796e801d209ad339e89596eb4c8d430d18ede6a1cced8fafbd99" +dependencies = [ + "autocfg", + "hashbrown 0.12.3", + "serde", +] + +[[package]] +name = "indexmap" +version = "2.14.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cc4e190f5d26ca7051642629da2c52fc03bde85a03197c99408dcd291734c855" +dependencies = [ + "equivalent", + "hashbrown 0.17.1", + "serde", + "serde_core", +] + +[[package]] +name = "indexmap-nostd" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e04e2fd2b8188ea827b32ef11de88377086d690286ab35747ef7f9bf3ccb590" + +[[package]] +name = "itertools" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "413ee7dfc52ee1a4949ceeb7dbc8a33f2d6c088194d9f922fb8318faf1f01186" +dependencies = [ + "either", +] + +[[package]] +name = "itoa" +version = "1.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f42a60cbdf9a97f5d2305f08a87dc4e09308d1276d28c869c684d7777685682" + +[[package]] +name = "jiff" +version = "0.2.38" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b2b005715dcbeb0089a3c0dab99f2ff1cc3b2525323552703d648585d342a383" +dependencies = [ + "defmt", + "jiff-core", + "jiff-static", + "jiff-tzdb-platform", + "log", + "portable-atomic", + "portable-atomic-util", + "serde_core", + "windows-link", +] + +[[package]] +name = "jiff-core" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e52fe76043ccecc9005d2305ebaadf7d7fc0cc89ca6baa10a94d6bc68c7128c" +dependencies = [ + "defmt", + "log", +] + +[[package]] +name = "jiff-static" +version = "0.2.38" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2cc9817253cf7c7ee4684451bd327e88d6f3658014e54a29198625590650695c" +dependencies = [ + "jiff-core", + "proc-macro2", + "quote", + "syn 2.0.119", +] + +[[package]] +name = "jiff-tzdb" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fa8377070c6bae868759445e5a77f66d84f0b72f3a054bfb00e6d038b8282da7" + +[[package]] +name = "jiff-tzdb-platform" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "875a5a69ac2bab1a891711cf5eccbec1ce0341ea805560dcd90b7a2e925132e8" +dependencies = [ + "jiff-tzdb", +] + +[[package]] +name = "js-sys" +version = "0.3.106" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7883d941dae510fb2d978fc3fe018c71c9e2892fd38854de3e8b92c2e5ad9cc5" +dependencies = [ + "cfg-if", + "futures-util", + "wasm-bindgen", +] + +[[package]] +name = "k256" +version = "0.13.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f6e3919bbaa2945715f0bb6d3934a173d1e9a59ac23767fbaaef277265a7411b" +dependencies = [ + "cfg-if", + "ecdsa", + "elliptic-curve", + "sha2", +] + +[[package]] +name = "keccak" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cb26cec98cce3a3d96cbb7bced3c4b16e3d13f27ec56dbd62cbc8f39cfb9d653" +dependencies = [ + "cpufeatures 0.2.17", +] + +[[package]] +name = "libc" +version = "0.2.190" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ce5d3ddc6d3fa000eb1536d85e147bfe31aacaba692ed6a876f95cb7c855be78" + +[[package]] +name = "libm" +version = "0.2.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6d2cec3eae94f9f509c767b45932f1ada8350c4bdb85af2fcab4a3c14807981" + +[[package]] +name = "log" +version = "0.4.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f9f8bd3e56ce4dfc153cf470fffbfa98c7620958b312ca5c3a4b8d5181fd13c6" + +[[package]] +name = "macro-string" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b27834086c65ec3f9387b096d66e99f221cf081c2b738042aa252bcd41204e3" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.119", +] + +[[package]] +name = "memchr" +version = "2.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf8baf1c55e62ffcace7a9f06f4bd9cd3f0c4beb022d3b367256b91b87513d98" + +[[package]] +name = "num-bigint" +version = "0.4.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c89e69e7e0f03bea5ef08013795c25018e101932225a656383bd384495ecc367" +dependencies = [ + "num-integer", + "num-traits", +] + +[[package]] +name = "num-conv" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "521739c6d2bac4aa25192232afe6841231376b2b26d4d9fae5ecf8ca5772e441" + +[[package]] +name = "num-derive" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed3955f1a9c7c0c15e092f9c887db08b1fc683305fdf6eb6684f22555355e202" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.119", +] + +[[package]] +name = "num-integer" +version = "0.1.47" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ce2d95d4b3734dc35aa2f45e1aa22cd416814592a4f9d9205e11affd5b8e10b" +dependencies = [ + "num-traits", +] + +[[package]] +name = "num-traits" +version = "0.2.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" +dependencies = [ + "autocfg", +] + +[[package]] +name = "once_cell" +version = "1.21.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9f7c3e4beb33f85d45ae3e3a1792185706c8e16d043238c593331cc7cd313b50" + +[[package]] +name = "p256" +version = "0.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c9863ad85fa8f4460f9c48cb909d38a0d689dba1f6f6988a5e3e0d31071bcd4b" +dependencies = [ + "ecdsa", + "elliptic-curve", + "primeorder", + "sha2", +] + +[[package]] +name = "paste" +version = "1.0.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a" + +[[package]] +name = "pin-project-lite" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a89322df9ebe1c1578d689c92318e070967d1042b512afbe49518723f4e6d5cd" + +[[package]] +name = "pkcs8" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f950b2377845cebe5cf8b5165cb3cc1a5e0fa5cfa3e1f7f55707d8fd82e0a7b7" +dependencies = [ + "der", + "spki", +] + +[[package]] +name = "portable-atomic" +version = "1.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05c8b63e8d9609db387f0324918f81d68fe27748f084ef092fb35954d0539a85" + +[[package]] +name = "portable-atomic-util" +version = "0.2.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "10ab3eb7f3becc3a1cbc4f2c6f20267996cfc1a6467a873763411b136a122715" +dependencies = [ + "portable-atomic", +] + +[[package]] +name = "powerfmt" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4a6394b9e965e73d0a289ee54f589087e2c676aedf60885baf52c76b771e4958" + +[[package]] +name = "ppv-lite86" +version = "0.2.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85eae3c4ed2f50dcfe72643da4befc30deadb458a9b590d720cde2f2b1e97da9" +dependencies = [ + "zerocopy", +] + +[[package]] +name = "prettyplease" +version = "0.2.37" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "479ca8adacdd7ce8f1fb39ce9ecccbfe93a3f1344b3d0d97f20bc0196208f62b" +dependencies = [ + "proc-macro2", + "syn 2.0.119", +] + +[[package]] +name = "primeorder" +version = "0.13.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "353e1ca18966c16d9deb1c69278edbc5f194139612772bd9537af60ac231e1e6" +dependencies = [ + "elliptic-curve", +] + +[[package]] +name = "proc-macro2" +version = "1.0.107" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "985e7ec9bb745e6ce6535b544d84d6cd6f7ad8bd711c398938ae983b91a766d9" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.47" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fbf4db142a473a8d80c26bbf18454ed458bf8d26c8219c331daecfdbd079001" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "rand" +version = "0.8.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e058c7de0b26af77780c769414d6257830bb240f3c38477dbc2c16e5f54d6d4c" +dependencies = [ + "libc", + "rand_chacha", + "rand_core", +] + +[[package]] +name = "rand_chacha" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" +dependencies = [ + "ppv-lite86", + "rand_core", +] + +[[package]] +name = "rand_core" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" +dependencies = [ + "getrandom", +] + +[[package]] +name = "ref-cast" +version = "1.0.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7e440fb4e4b4147295338efb76001ab9e4efc0e5839df2c47fc5ac2381d365c3" +dependencies = [ + "ref-cast-impl", +] + +[[package]] +name = "ref-cast-impl" +version = "1.0.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "92ecd8964f8453721699a1ed72037b0db49ce2f5a5138486ee89bed6f67cdf3a" +dependencies = [ + "proc-macro2", + "quote", + "syn 3.0.9", +] + +[[package]] +name = "rfc6979" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8dd2a808d456c4a54e300a23e9f5a67e122c3024119acbfd73e3bf664491cb2" +dependencies = [ + "hmac", + "subtle", +] + +[[package]] +name = "rustc_version" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cfcb3a22ef46e85b45de6ee7e79d063319ebb6594faafcf1c225ea92ab6e9b92" +dependencies = [ + "semver", +] + +[[package]] +name = "rustversion" +version = "1.0.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf54715a573b99ac80df0bc206da022bcd442c974952c7b9720069370852e21f" + +[[package]] +name = "schemars" +version = "0.8.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3fbf2ae1b8bc8e02df939598064d22402220cd5bbcca1c76f7d6a310974d5615" +dependencies = [ + "dyn-clone", + "serde", + "serde_json", +] + +[[package]] +name = "schemars" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4cd191f9397d57d581cddd31014772520aa448f65ef991055d7f61582c65165f" +dependencies = [ + "dyn-clone", + "ref-cast", + "serde", + "serde_json", +] + +[[package]] +name = "schemars" +version = "1.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ab508826f74a77ca9d5aba6ff19b522583ee3eaf28a19384ff3d0e5835fadf6e" +dependencies = [ + "dyn-clone", + "ref-cast", + "serde", + "serde_json", +] + +[[package]] +name = "sec1" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3e97a565f76233a6003f9f5c54be1d9c5bdfa3eccfb189469f11ec4901c47dc" +dependencies = [ + "base16ct", + "der", + "generic-array", + "subtle", + "zeroize", +] + +[[package]] +name = "semver" +version = "1.0.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a7852d02fc848982e0c167ef163aaff9cd91dc640ba85e263cb1ce46fae51cd" +dependencies = [ + "serde", + "serde_core", +] + +[[package]] +name = "serde" +version = "1.0.229" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4148590afebada386688f18773da617792bf2ef03ffc1e4cbd2b1d45b023e0ba" +dependencies = [ + "serde_core", + "serde_derive", +] + +[[package]] +name = "serde_core" +version = "1.0.229" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67dca2c9c51e58a4791a4b1ed58308b39c64224d349a935ab5039aa360942a48" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.229" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e7a5d71263a5a7d47b41f6b3f06ba276f10cc18b0931f1799f710578e2309348" +dependencies = [ + "proc-macro2", + "quote", + "syn 3.0.9", +] + +[[package]] +name = "serde_json" +version = "1.0.154" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e7e9cc8b1b85264074fbcc02a88680c4096b1e47df8f739dceb03bf482f04bd6" +dependencies = [ + "itoa", + "memchr", + "serde", + "serde_core", + "zmij", +] + +[[package]] +name = "serde_with" +version = "3.24.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df9adc193c780ef8f159aee8b61e2d5801aaa555e6eb0947fe45530ec506296f" +dependencies = [ + "base64 0.23.1", + "bs58", + "chrono", + "hex", + "indexmap 1.9.3", + "indexmap 2.14.2", + "jiff", + "schemars 0.8.22", + "schemars 0.9.0", + "schemars 1.2.3", + "serde_core", + "serde_json", + "serde_with_macros", + "time", +] + +[[package]] +name = "serde_with_macros" +version = "3.24.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3e17bbc68e28663bbbb90df47e058aa7eda4fb445b89fe70457bb94fbccf6e49" +dependencies = [ + "darling 0.24.1", + "proc-macro2", + "quote", + "syn 3.0.9", +] + +[[package]] +name = "sha2" +version = "0.10.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a7507d819769d01a365ab707794a4084392c824f54a7a6a7862f8c3d0892b283" +dependencies = [ + "cfg-if", + "cpufeatures 0.2.17", + "digest 0.10.7", +] + +[[package]] +name = "sha3" +version = "0.10.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77fd7028345d415a4034cf8777cd4f8ab1851274233b45f84e3d955502d93874" +dependencies = [ + "digest 0.10.7", + "keccak", +] + +[[package]] +name = "shlex" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8fadd59c855ef2080decdef8ff161eb6661b86933c9d82e5ba29dc602a55aba" + +[[package]] +name = "signature" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77549399552de45a898a580c1b41d445bf730df867cc44e6c0233bbc4b8329de" +dependencies = [ + "digest 0.10.7", + "rand_core", +] + +[[package]] +name = "slab" +version = "0.4.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c790de23124f9ab44544d7ac05d60440adc586479ce501c1d6d7da3cd8c9cf5" + +[[package]] +name = "smallvec" +version = "1.16.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b3dc8af474f516a851ff4bd12db780f948b9250ad37211e4eec0bccea54e01b" + +[[package]] +name = "soroban-builtin-sdk-macros" +version = "27.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b77bc93d930032c487cb1506b6ed166b2af49db76d52678ec4887ac621ecce01" +dependencies = [ + "itertools", + "proc-macro2", + "quote", + "syn 2.0.119", +] + +[[package]] +name = "soroban-env-common" +version = "27.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6b22e9981cdd444f3aa6734bc58d76195bf7eca3ccf1dd432b875af5d02da068" +dependencies = [ + "arbitrary", + "crate-git-revision 0.0.6", + "ethnum", + "num-derive", + "num-traits", + "serde", + "soroban-env-macros", + "soroban-wasmi", + "static_assertions", + "stellar-xdr", + "wasmparser", +] + +[[package]] +name = "soroban-env-guest" +version = "27.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b6072f99ca6bf8e8d5b04e05d083dac785e5357d9c0f36a6658f819c2fd7d67" +dependencies = [ + "soroban-env-common", + "static_assertions", +] + +[[package]] +name = "soroban-env-host" +version = "27.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2c06afd7c75ce150ce53e4d77a77645b18e3fb61856a0ddc42bfcecdc39fa3b9" +dependencies = [ + "ark-bls12-381", + "ark-bn254", + "ark-ec", + "ark-ff", + "ark-serialize", + "curve25519-dalek 5.0.0", + "ecdsa", + "ed25519-dalek", + "elliptic-curve", + "generic-array", + "getrandom", + "hex-literal", + "hmac", + "k256", + "num-derive", + "num-integer", + "num-traits", + "p256", + "rand", + "rand_chacha", + "sec1", + "sha2", + "sha3", + "soroban-builtin-sdk-macros", + "soroban-env-common", + "soroban-wasmi", + "static_assertions", + "stellar-strkey 0.0.13", + "wasmparser", +] + +[[package]] +name = "soroban-env-macros" +version = "27.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "647811bdd28a3ec40296987f6635781e5e1141c8f5affbbd53ba12b6295b7bb6" +dependencies = [ + "itertools", + "proc-macro2", + "quote", + "serde", + "serde_json", + "stellar-xdr", + "syn 2.0.119", +] + +[[package]] +name = "soroban-ledger-snapshot" +version = "27.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b59883d8bd0d1aed8d57579a9974ab88eaf787dd0a1af104f6881b5707450558" +dependencies = [ + "serde", + "serde_json", + "serde_with", + "soroban-env-common", + "soroban-env-host", + "thiserror 1.0.69", +] + +[[package]] +name = "soroban-sdk" +version = "27.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c3f21971c84fcfb08957e3e8f5a9a70f134cb07ad9ee053ac7e6d7a887a82af" +dependencies = [ + "arbitrary", + "bytes-lit", + "crate-git-revision 0.0.9", + "ctor", + "derive_arbitrary", + "ed25519-dalek", + "rand", + "rustc_version", + "serde", + "serde_json", + "soroban-env-guest", + "soroban-env-host", + "soroban-ledger-snapshot", + "soroban-sdk-macros", + "stellar-strkey 0.0.16", + "visibility", +] + +[[package]] +name = "soroban-sdk-macros" +version = "27.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3bd4a847273d749807fe2eb52e2b9c1917ee482cd6a39465cad5c389548996ad" +dependencies = [ + "darling 0.20.11", + "heck", + "itertools", + "macro-string", + "proc-macro2", + "quote", + "sha2", + "soroban-env-common", + "soroban-spec", + "soroban-spec-rust", + "stellar-xdr", + "syn 2.0.119", +] + +[[package]] +name = "soroban-spec" +version = "27.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "473404322827b285cbcd87517f365986bd63af7842c78b2a86ee061715fda61e" +dependencies = [ + "base64 0.22.1", + "sha2", + "stellar-xdr", + "thiserror 1.0.69", + "wasmparser", +] + +[[package]] +name = "soroban-spec-rust" +version = "27.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2f25698b6ce2125850a9ef075cf9ba1e8d25b4cfa0c46aca42dadd80cc29d881" +dependencies = [ + "prettyplease", + "proc-macro2", + "quote", + "sha2", + "soroban-spec", + "stellar-xdr", + "syn 2.0.119", + "thiserror 1.0.69", +] + +[[package]] +name = "soroban-wasmi" +version = "0.31.1-soroban.20.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "710403de32d0e0c35375518cb995d4fc056d0d48966f2e56ea471b8cb8fc9719" +dependencies = [ + "smallvec", + "spin", + "wasmi_arena", + "wasmi_core", + "wasmparser-nostd", +] + +[[package]] +name = "spin" +version = "0.9.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3763264f6b73151db08c50ff20d7d8a0b8796e021cdea7ceedad07b80155fa0e" + +[[package]] +name = "spki" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d91ed6c858b01f942cd56b37a94b3e0a1798290327d1236e4d9cf4eaca44d29d" +dependencies = [ + "base64ct", + "der", +] + +[[package]] +name = "stable_deref_trait" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ce2be8dc25455e1f91df71bfa12ad37d7af1092ae736f3a6cd0e37bc7810596" + +[[package]] +name = "static_assertions" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" + +[[package]] +name = "stellar-build" +version = "0.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97fd5e3be902a9658fb9f642d4eef7837ab1d51e52dde9be1d9b218326499d97" +dependencies = [ + "cargo_metadata", + "sha2", + "thiserror 2.0.21", + "topological-sort", +] + +[[package]] +name = "stellar-registry" +version = "0.1.1" +dependencies = [ + "stellar-registry-macro", +] + +[[package]] +name = "stellar-registry-macro" +version = "0.1.1" +dependencies = [ + "proc-macro2", + "quote", + "sha2", + "stellar-build", + "stellar-registry-name", + "stellar-strkey 0.0.16", + "stellar-xdr", + "syn 2.0.119", +] + +[[package]] +name = "stellar-registry-name" +version = "0.1.0" +dependencies = [ + "semver", + "thiserror 2.0.21", +] + +[[package]] +name = "stellar-strkey" +version = "0.0.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee1832fb50c651ad10f734aaf5d31ca5acdfb197a6ecda64d93fcdb8885af913" +dependencies = [ + "crate-git-revision 0.0.6", + "data-encoding", +] + +[[package]] +name = "stellar-strkey" +version = "0.0.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "084afcb0d458c3d5d5baa2d294b18f881e62cc258ef539d8fdf68be7dbe45520" +dependencies = [ + "crate-git-revision 0.0.6", + "data-encoding", + "heapless", +] + +[[package]] +name = "stellar-xdr" +version = "27.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05ff843326969bdf1ef673dcdba94c08f4a3c8f1e58d6e6ef39b1bd4f749179a" +dependencies = [ + "arbitrary", + "base64 0.22.1", + "cfg_eval", + "crate-git-revision 0.0.6", + "escape-bytes", + "ethnum", + "hex", + "serde", + "serde_with", + "sha2", + "stellar-strkey 0.0.13", +] + +[[package]] +name = "strsim" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" + +[[package]] +name = "subtle" +version = "2.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" + +[[package]] +name = "syn" +version = "2.0.119" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "872831b642d1a07999a962a351ed35b955ea2cfc8f3862091e2a240a84f17297" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "syn" +version = "3.0.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d78c8dee4c7bf0e14673097256fed6142ce9d3b85a408189d07482442145823b" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "thiserror" +version = "1.0.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6aaf5339b578ea85b50e080feb250a3e8ae8cfcdff9a461c9ec2904bc923f52" +dependencies = [ + "thiserror-impl 1.0.69", +] + +[[package]] +name = "thiserror" +version = "2.0.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09e52cb86a36cede5cb101bf8908837b3e4c6e5e59fe7fd85c23fb56200d189e" +dependencies = [ + "thiserror-impl 2.0.21", +] + +[[package]] +name = "thiserror-impl" +version = "1.0.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.119", +] + +[[package]] +name = "thiserror-impl" +version = "2.0.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fe5197923287db20a58125f0bc85c062f7f2c892de97b18c356f9efb14b28524" +dependencies = [ + "proc-macro2", + "quote", + "syn 3.0.9", +] + +[[package]] +name = "time" +version = "0.3.55" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cdb87b95ec50ddfa440816d227a17b2ccbdda963a316a727fda0fc4334f7d134" +dependencies = [ + "deranged", + "num-conv", + "powerfmt", + "serde_core", + "time-core", + "time-macros", +] + +[[package]] +name = "time-core" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e1c906769ad99c88eaa54e728060edef082f8e358ff32030cb7c7d315e81109" + +[[package]] +name = "time-macros" +version = "0.2.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7e689342a48d2ea927c87ea50cabf8594854bf940e9310208848d680d668ed85" +dependencies = [ + "num-conv", + "time-core", +] + +[[package]] +name = "tinyvec" +version = "1.13.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fd3ca314f692efd6c868f8408f53fe444634a845f96c028b97d35f6a1f79f0ee" + +[[package]] +name = "topological-sort" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ea68304e134ecd095ac6c3574494fc62b909f416c4fca77e440530221e549d3d" + +[[package]] +name = "typenum" +version = "1.20.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6f5e870be6c3b371b77fe0ee0bafb859fa4964b4404c27de1d380043c4dda20" + +[[package]] +name = "unicode-ident" +version = "1.0.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a2c754d6c33795a1c324727428e5a7dedb5b06195f9890bdbcba760d3e246563" + +[[package]] +name = "version_check" +version = "0.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" + +[[package]] +name = "visibility" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d674d135b4a8c1d7e813e2f8d1c9a58308aee4a680323066025e53132218bd91" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.119", +] + +[[package]] +name = "wasi" +version = "0.11.1+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b" + +[[package]] +name = "wasm-bindgen" +version = "0.2.129" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9bb54f33acc68fd454578d9820b0bde1a1a3d17aa17bb7b6595806d02886d409" +dependencies = [ + "cfg-if", + "once_cell", + "rustversion", + "wasm-bindgen-macro", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-macro" +version = "0.2.129" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2e29d0c35b16e224a7eeb5cd2d25e3e1968fbd65604117b44d3b789d00ee8535" +dependencies = [ + "quote", + "wasm-bindgen-macro-support", +] + +[[package]] +name = "wasm-bindgen-macro-support" +version = "0.2.129" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6f501a8bc3719dba86ef8ae4728879c08001bea749eb1333ac5b91e040e2a6b7" +dependencies = [ + "bumpalo", + "proc-macro2", + "quote", + "syn 3.0.9", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-shared" +version = "0.2.129" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "23f0c9c52aa7cd7d77769a4cfe2a9adb1b331f489a41d912ce14513d5ab995c6" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "wasmi_arena" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "104a7f73be44570cac297b3035d76b169d6599637631cf37a1703326a0727073" + +[[package]] +name = "wasmi_core" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dcf1a7db34bff95b85c261002720c00c3a6168256dcb93041d3fa2054d19856a" +dependencies = [ + "downcast-rs", + "libm", + "num-traits", + "paste", +] + +[[package]] +name = "wasmparser" +version = "0.116.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a58e28b80dd8340cb07b8242ae654756161f6fc8d0038123d679b7b99964fa50" +dependencies = [ + "indexmap 2.14.2", + "semver", +] + +[[package]] +name = "wasmparser-nostd" +version = "0.100.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d5a015fe95f3504a94bb1462c717aae75253e39b9dd6c3fb1062c934535c64aa" +dependencies = [ + "indexmap-nostd", +] + +[[package]] +name = "windows-core" +version = "0.62.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8e83a14d34d0623b51dce9581199302a221863196a1dde71a7663a4c2be9deb" +dependencies = [ + "windows-implement", + "windows-interface", + "windows-link", + "windows-result", + "windows-strings", +] + +[[package]] +name = "windows-implement" +version = "0.60.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "053e2e040ab57b9dc951b72c264860db7eb3b0200ba345b4e4c3b14f67855ddf" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.119", +] + +[[package]] +name = "windows-interface" +version = "0.59.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f316c4a2570ba26bbec722032c4099d8c8bc095efccdc15688708623367e358" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.119", +] + +[[package]] +name = "windows-link" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5" + +[[package]] +name = "windows-result" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7781fa89eaf60850ac3d2da7af8e5242a5ea78d1a11c49bf2910bb5a73853eb5" +dependencies = [ + "windows-link", +] + +[[package]] +name = "windows-strings" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7837d08f69c77cf6b07689544538e017c1bfcf57e34b4c0ff58e6c2cd3b37091" +dependencies = [ + "windows-link", +] + +[[package]] +name = "zerocopy" +version = "0.8.62" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "86502bf56ac7c77571a32e2647bb2a15894565e981fb2a48d7bde2d91c965a9d" +dependencies = [ + "zerocopy-derive", +] + +[[package]] +name = "zerocopy-derive" +version = "0.8.62" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5457206954b06561e2608c7e19cf58b1926586d999c246eebe4502f7e2039d1a" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.119", +] + +[[package]] +name = "zeroize" +version = "1.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e13084392c5e4bc371903e2935a5eaeed24905a7511356b883835e18a78f6879" +dependencies = [ + "zeroize_derive", +] + +[[package]] +name = "zeroize_derive" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c50655cbb0fe3fc43170059e702f1ce5e19b84cec58dc87b037a09935c2f328" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.119", +] + +[[package]] +name = "zmij" +version = "1.0.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "29666d0abbfad1e3dc4dcf6144730dd3a3ab225bbbdac83319345b1b44ccfc1b" diff --git a/crates/stellar-registry-test/fixtures/contracts/hello_caller/Cargo.toml b/crates/stellar-registry-test/fixtures/contracts/hello_caller/Cargo.toml new file mode 100644 index 0000000..3b43161 --- /dev/null +++ b/crates/stellar-registry-test/fixtures/contracts/hello_caller/Cargo.toml @@ -0,0 +1,27 @@ +[package] +name = "hello_caller" +version = "0.0.0" +edition = "2024" +publish = false + +# Demonstrates testing a contract that uses `import_contract!` with +# `stellar_registry::testutils::register_import!` — see src/lib.rs. +# +# Excluded from the repo workspace: `import_contract!(env, hello_v1)` needs +# hello_v1's id and Wasm in this crate's `target/stellar/local/deployed/` +# before it compiles, so a plain `cargo build --workspace` could not build it. +# `just test-register-import` seeds those files and runs the tests offline. +[workspace] + +[lib] +crate-type = ["cdylib"] +doctest = false + +[dependencies] +# Same requirement as the root workspace's soroban-sdk. +soroban-sdk = "27.0.0-rc.1" +stellar-registry = { path = "../../../../stellar-registry" } + +[dev-dependencies] +soroban-sdk = { version = "27.0.0-rc.1", features = ["testutils"] } +stellar-registry = { path = "../../../../stellar-registry", features = ["testutils"] } diff --git a/crates/stellar-registry-test/fixtures/contracts/hello_caller/src/lib.rs b/crates/stellar-registry-test/fixtures/contracts/hello_caller/src/lib.rs new file mode 100644 index 0000000..aa1dc54 --- /dev/null +++ b/crates/stellar-registry-test/fixtures/contracts/hello_caller/src/lib.rs @@ -0,0 +1,74 @@ +#![no_std] +//! Demonstrates unit-testing a contract that calls another contract through +//! `stellar_registry::import_contract!`. +//! +//! `import_contract!(env, hello_v1)` bakes `hello_v1`'s deployed address into +//! this contract at build time, and nothing lives at that address in a +//! sandboxed test `Env`. `register_import!` resolves the same name the same +//! way and registers a contract there — `hello_v1`'s own Wasm, or a native +//! mock — so `Contract` below needs no `#[cfg(test)]` seam. +use soroban_sdk::{Env, String, contract, contractimpl}; + +#[contract] +pub struct Contract; + +#[contractimpl] +impl Contract { + /// Greet `to` by way of the deployed `hello_v1` contract. + pub fn greet(env: &Env, to: String) -> String { + stellar_registry::import_contract!(env, hello_v1).hello(&to) + } +} + +#[cfg(test)] +mod test { + use super::*; + use soroban_sdk::{Address, testutils::Address as _}; + use stellar_registry::testutils::register_import; + + #[contract] + struct MockHello; + + #[contractimpl] + impl MockHello { + pub fn hello(env: &Env, to: String) -> String { + let _ = to; + String::from_str(env, "mocked") + } + } + + fn greet(env: &Env, to: &str) -> String { + let client = ContractClient::new(env, &env.register(Contract, ())); + client.greet(&String::from_str(env, to)) + } + + #[test] + fn greets_through_the_imported_wasm() { + let env = Env::default(); + let admin = Address::generate(&env); + // `hello_v1`'s constructor takes an admin. + register_import!(&env, hello_v1, args = (&admin,)); + + assert_eq!(greet(&env, "world"), String::from_str(&env, "world")); + } + + #[test] + fn greets_through_a_native_mock() { + let env = Env::default(); + register_import!(&env, hello_v1, contract = MockHello); + + assert_eq!(greet(&env, "world"), String::from_str(&env, "mocked")); + } + + #[test] + fn registers_at_the_same_address_either_way() { + let env = Env::default(); + let admin = Address::generate(&env); + let wasm = register_import!(&env, hello_v1, args = (&admin,)); + let mock = register_import!(&env, hello_v1, contract = MockHello); + + assert_eq!(wasm, mock); + // Registering again replaced the Wasm with the mock. + assert_eq!(greet(&env, "world"), String::from_str(&env, "mocked")); + } +} diff --git a/crates/stellar-registry/Cargo.toml b/crates/stellar-registry/Cargo.toml index f6561af..19fe73f 100644 --- a/crates/stellar-registry/Cargo.toml +++ b/crates/stellar-registry/Cargo.toml @@ -12,5 +12,9 @@ crate-type = ["rlib"] # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html +[features] +# Test helpers (`testutils::register_import!`); enable from dev-dependencies. +testutils = [] + [dependencies] stellar-registry-macro = { workspace = true } diff --git a/crates/stellar-registry/README.md b/crates/stellar-registry/README.md index 00a463d..50f761e 100644 --- a/crates/stellar-registry/README.md +++ b/crates/stellar-registry/README.md @@ -26,6 +26,55 @@ pub fn your_fn(env: &Env) { A registered name that itself points at a SAC (e.g. `"circle/usdc"`) works the same way, going through the registry to resolve the address and falling back to the same token client instead of trying (and failing) to fetch a wasm that doesn't exist for a built-in asset contract. +## Testing with `import_contract!` + +`import_contract!` bakes the contract's real on-chain address into your contract at build time. A unit test's sandboxed `Env` has nothing at that address, so calling through the import fails until you put a contract there. `register_import!` does that: it resolves the name exactly as `import_contract!` does and registers the imported contract at the same address, returning the `Address`. + +Enable the `testutils` feature for tests (alongside `soroban-sdk`'s own): + +```toml +[dependencies] +soroban-sdk = "27" +stellar-registry = "0.1" + +[dev-dependencies] +soroban-sdk = { version = "27", features = ["testutils"] } +stellar-registry = { version = "0.1", features = ["testutils"] } +``` + +Then, given a contract that imports `our_dao`: + +```rs +pub fn propose(env: &Env, title: String) -> u32 { + stellar_registry::import_contract!(env, our_dao).create_proposal(&title) +} +``` + +...its tests register `our_dao` before calling it. Your contract code stays the same in tests and on-chain, with no `#[cfg(test)]` switch: + +```rs +use stellar_registry::testutils::register_import; + +#[test] +fn proposes() { + let env = Env::default(); + // Runs the same Wasm `import_contract!` generated its client from. + register_import!(&env, our_dao); + + let client = ContractClient::new(&env, &env.register(Contract, ())); + assert_eq!(client.propose(&String::from_str(&env, "Ship it")), 1); +} +``` + +Two options, in either order: + +- `args = (..)` passes constructor arguments, as a tuple like `Env::register` takes: `register_import!(&env, our_dao, args = (&admin, 7u32))`. +- `contract = ..` registers a native contract in place of the Wasm, when you want to control what the dependency returns: `register_import!(&env, our_dao, contract = MockDao)`. `MockDao` is an ordinary `#[contract]` type with the functions your contract calls. + +Both macros read the same cache files (`target/stellar//deployed/.id` and `.wasm`), so once `import_contract!` has built, `register_import!` needs nothing more. To run tests with no network access, such as in CI, set `STELLAR_NO_REGISTRY=1` and create those two files first (`stellar registry fetch-contract-id` and `stellar contract fetch`, or commit copies and put them in place before building). The [`hello_caller` fixture](https://github.com/stellar-registry/cli/tree/main/crates/stellar-registry-test/fixtures/contracts/hello_caller) is a complete example, run by `just test-register-import`. + +Stellar Asset Contracts (`xlm`, `"CODE:ISSUER"`, or a registered name that resolves to a SAC) are not supported by `register_import!` yet and give a compile error. Until they are, register a test asset with `Env::register_stellar_asset_contract_v2`. + # Import wasm with `import_contract_client!` Import a wasm (https://stellar.rgstry.xyz/wasms), which defines only behavior. You can optionally include a version, otherwise it fetches the latest. You need to instantiate with a contract ID. diff --git a/crates/stellar-registry/src/lib.rs b/crates/stellar-registry/src/lib.rs index 7d820c0..c19198f 100644 --- a/crates/stellar-registry/src/lib.rs +++ b/crates/stellar-registry/src/lib.rs @@ -3,3 +3,9 @@ //! existing smart contracts on Stellar. //! pub use stellar_registry_macro::{import_asset, import_contract, import_contract_client}; + +/// Helpers for unit-testing contracts that use [`import_contract!`]. +#[cfg(feature = "testutils")] +pub mod testutils { + pub use stellar_registry_macro::register_import; +} diff --git a/justfile b/justfile index 9f5f15f..1cead5f 100644 --- a/justfile +++ b/justfile @@ -49,6 +49,19 @@ _test-integration package filter ci="false": test-integration-registry ci="false": just _test-integration stellar-registry-cli 'test(/./)' {{ ci }} +# Test the hello_caller fixture (import_contract! + register_import!) offline, seeding the cache files import_contract! would otherwise fetch +test-register-import: + #!/usr/bin/env bash + set -euo pipefail + fixture=crates/stellar-registry-test/fixtures/contracts/hello_caller + deployed=$fixture/target/stellar/local/deployed + stellar contract build --profile contracts --package hello_v1 + mkdir -p $deployed + cp target/wasm32v1-none/contracts/hello_v1.wasm $deployed/hello_v1.wasm + # Any valid contract id works: nothing is looked up on a network. + echo CBESJIMX7J53SWJGJ7WQ6QTLJI4S5LPPJNC2BNVD63GIKAYCDTDOO322 > $deployed/hello_v1.id + STELLAR_NO_REGISTRY=1 STELLAR_NETWORK=local cargo test --manifest-path $fixture/Cargo.toml + clippy *args: cargo clippy --all {{ args }} \ -- -Dclippy::pedantic -Aclippy::must_use_candidate -Aclippy::missing_errors_doc -Aclippy::missing_panics_doc From 13319f228e8354e129d99fadfbed713cbb010e5d Mon Sep 17 00:00:00 2001 From: pselle Date: Tue, 13 Oct 2026 14:15:54 -0400 Subject: [PATCH 02/20] build(deps): move hello_caller fixture to soroban-sdk 28 The fixture sits outside the workspace, so it does not pick up the workspace's soroban-sdk bump. Co-Authored-By: Claude --- .../contracts/hello_caller/Cargo.lock | 85 ++++++++++++------- .../contracts/hello_caller/Cargo.toml | 4 +- 2 files changed, 56 insertions(+), 33 deletions(-) diff --git a/crates/stellar-registry-test/fixtures/contracts/hello_caller/Cargo.lock b/crates/stellar-registry-test/fixtures/contracts/hello_caller/Cargo.lock index b79b0ad..4c6ac03 100644 --- a/crates/stellar-registry-test/fixtures/contracts/hello_caller/Cargo.lock +++ b/crates/stellar-registry-test/fixtures/contracts/hello_caller/Cargo.lock @@ -404,9 +404,9 @@ dependencies = [ [[package]] name = "crypto-common" -version = "0.1.7" +version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "78c8292055d1c1df0cce5d180393dc8cce0abec0a7102adb6c7b1eef6016d60a" +checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" dependencies = [ "generic-array", "typenum", @@ -623,7 +623,7 @@ checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" dependencies = [ "block-buffer 0.10.4", "const-oid", - "crypto-common 0.1.7", + "crypto-common 0.1.6", "subtle", ] @@ -836,9 +836,9 @@ dependencies = [ [[package]] name = "generic-array" -version = "0.14.7" +version = "0.14.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" +checksum = "4bb6743198531e02858aeaea5398fcc883e71851fcbcb5a2f773e2fb6cb1edf2" dependencies = [ "typenum", "version_check", @@ -909,6 +909,17 @@ dependencies = [ "stable_deref_trait", ] +[[package]] +name = "heapless" +version = "0.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "25ba4bd83f9415b58b4ed8dc5714c76e626a105be4646c02630ad730ad3b5aa4" +dependencies = [ + "hash32", + "stable_deref_trait", + "zeroize", +] + [[package]] name = "heck" version = "0.5.0" @@ -1564,9 +1575,9 @@ checksum = "5b3dc8af474f516a851ff4bd12db780f948b9250ad37211e4eec0bccea54e01b" [[package]] name = "soroban-builtin-sdk-macros" -version = "27.0.1" +version = "28.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b77bc93d930032c487cb1506b6ed166b2af49db76d52678ec4887ac621ecce01" +checksum = "86ffd329211a50add3965a8f4a8f70e24fc5c8268c945566ab963029b12dce40" dependencies = [ "itertools", "proc-macro2", @@ -1576,12 +1587,12 @@ dependencies = [ [[package]] name = "soroban-env-common" -version = "27.0.1" +version = "28.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6b22e9981cdd444f3aa6734bc58d76195bf7eca3ccf1dd432b875af5d02da068" +checksum = "610bb12ee937a56c2b629f8eb16efb7e47607cba4e79ab0c24132b9a53dd19a0" dependencies = [ "arbitrary", - "crate-git-revision 0.0.6", + "crate-git-revision 0.0.9", "ethnum", "num-derive", "num-traits", @@ -1595,9 +1606,9 @@ dependencies = [ [[package]] name = "soroban-env-guest" -version = "27.0.1" +version = "28.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2b6072f99ca6bf8e8d5b04e05d083dac785e5357d9c0f36a6658f819c2fd7d67" +checksum = "7d383176b27bb286f718ab0ff5713bd9b01132089151266359bd9b20f38684cf" dependencies = [ "soroban-env-common", "static_assertions", @@ -1605,9 +1616,9 @@ dependencies = [ [[package]] name = "soroban-env-host" -version = "27.0.1" +version = "28.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2c06afd7c75ce150ce53e4d77a77645b18e3fb61856a0ddc42bfcecdc39fa3b9" +checksum = "36d7e5920c200939164cc58038224e2d90d7e2fabbfa505d6e2129d648dce7e2" dependencies = [ "ark-bls12-381", "ark-bn254", @@ -1642,9 +1653,9 @@ dependencies = [ [[package]] name = "soroban-env-macros" -version = "27.0.1" +version = "28.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "647811bdd28a3ec40296987f6635781e5e1141c8f5affbbd53ba12b6295b7bb6" +checksum = "077600bb175ea54592c1ac5983b8fb34ec9ab7a65f298af11d687b61e9d646a4" dependencies = [ "itertools", "proc-macro2", @@ -1657,9 +1668,9 @@ dependencies = [ [[package]] name = "soroban-ledger-snapshot" -version = "27.0.6" +version = "28.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b59883d8bd0d1aed8d57579a9974ab88eaf787dd0a1af104f6881b5707450558" +checksum = "b5caeb0a4c8af6d04dc1dfbf96199e0d50ae086c7d54c9001abe78bb891fc44f" dependencies = [ "serde", "serde_json", @@ -1671,9 +1682,9 @@ dependencies = [ [[package]] name = "soroban-sdk" -version = "27.0.6" +version = "28.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c3f21971c84fcfb08957e3e8f5a9a70f134cb07ad9ee053ac7e6d7a887a82af" +checksum = "e4946477e75667215a25b652b0febb446485686c944d364c117c903e3fb98bab" dependencies = [ "arbitrary", "bytes-lit", @@ -1695,9 +1706,9 @@ dependencies = [ [[package]] name = "soroban-sdk-macros" -version = "27.0.6" +version = "28.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3bd4a847273d749807fe2eb52e2b9c1917ee482cd6a39465cad5c389548996ad" +checksum = "168f2d2c3fd4da57584c71e0a8315a8e656486495bc53e18c93b8ec4db085438" dependencies = [ "darling 0.20.11", "heck", @@ -1715,9 +1726,9 @@ dependencies = [ [[package]] name = "soroban-spec" -version = "27.0.6" +version = "28.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "473404322827b285cbcd87517f365986bd63af7842c78b2a86ee061715fda61e" +checksum = "2f48c8e183fc140ccd89e1fe1663cc605fb18641967c0bb591f64887599fde59" dependencies = [ "base64 0.22.1", "sha2", @@ -1728,9 +1739,9 @@ dependencies = [ [[package]] name = "soroban-spec-rust" -version = "27.0.6" +version = "28.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2f25698b6ce2125850a9ef075cf9ba1e8d25b4cfa0c46aca42dadd80cc29d881" +checksum = "40cd0021887fd047e9f63cea36da35b202881bc0e0dbcc067a146137ca46f192" dependencies = [ "prettyplease", "proc-macro2", @@ -1811,7 +1822,7 @@ dependencies = [ "sha2", "stellar-build", "stellar-registry-name", - "stellar-strkey 0.0.16", + "stellar-strkey 0.0.18", "stellar-xdr", "syn 2.0.119", ] @@ -1842,19 +1853,31 @@ checksum = "084afcb0d458c3d5d5baa2d294b18f881e62cc258ef539d8fdf68be7dbe45520" dependencies = [ "crate-git-revision 0.0.6", "data-encoding", - "heapless", + "heapless 0.8.0", +] + +[[package]] +name = "stellar-strkey" +version = "0.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0f34ff61c0ca6f1c2b4169e8d1633417bd9225f58b6175e4e317204565d16277" +dependencies = [ + "crate-git-revision 0.0.9", + "data-encoding", + "heapless 0.9.3", + "zeroize", ] [[package]] name = "stellar-xdr" -version = "27.0.0" +version = "28.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "05ff843326969bdf1ef673dcdba94c08f4a3c8f1e58d6e6ef39b1bd4f749179a" +checksum = "f93d09ff8b9f919b084f664003c4c546ac66a76affd5429460dbe29f4b326f8e" dependencies = [ "arbitrary", "base64 0.22.1", "cfg_eval", - "crate-git-revision 0.0.6", + "crate-git-revision 0.0.9", "escape-bytes", "ethnum", "hex", diff --git a/crates/stellar-registry-test/fixtures/contracts/hello_caller/Cargo.toml b/crates/stellar-registry-test/fixtures/contracts/hello_caller/Cargo.toml index 3b43161..daa2837 100644 --- a/crates/stellar-registry-test/fixtures/contracts/hello_caller/Cargo.toml +++ b/crates/stellar-registry-test/fixtures/contracts/hello_caller/Cargo.toml @@ -19,9 +19,9 @@ doctest = false [dependencies] # Same requirement as the root workspace's soroban-sdk. -soroban-sdk = "27.0.0-rc.1" +soroban-sdk = "28.0.0" stellar-registry = { path = "../../../../stellar-registry" } [dev-dependencies] -soroban-sdk = { version = "27.0.0-rc.1", features = ["testutils"] } +soroban-sdk = { version = "28.0.0", features = ["testutils"] } stellar-registry = { path = "../../../../stellar-registry", features = ["testutils"] } From c7bc241c9ce488e20fe1de24332c679e376713b1 Mon Sep 17 00:00:00 2001 From: Pam S Date: Fri, 16 Oct 2026 09:09:53 -0400 Subject: [PATCH 03/20] Apply suggestion from @chadoh Co-authored-by: Chad Ostrowski <221614+chadoh@users.noreply.github.com> --- .../fixtures/contracts/hello_caller/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/stellar-registry-test/fixtures/contracts/hello_caller/src/lib.rs b/crates/stellar-registry-test/fixtures/contracts/hello_caller/src/lib.rs index aa1dc54..8402347 100644 --- a/crates/stellar-registry-test/fixtures/contracts/hello_caller/src/lib.rs +++ b/crates/stellar-registry-test/fixtures/contracts/hello_caller/src/lib.rs @@ -6,7 +6,7 @@ //! this contract at build time, and nothing lives at that address in a //! sandboxed test `Env`. `register_import!` resolves the same name the same //! way and registers a contract there — `hello_v1`'s own Wasm, or a native -//! mock — so `Contract` below needs no `#[cfg(test)]` seam. +//! mock. use soroban_sdk::{Env, String, contract, contractimpl}; #[contract] From e2a29c0338d2d609fda8193ad8debb2ee2e52d18 Mon Sep 17 00:00:00 2001 From: pselle Date: Fri, 16 Oct 2026 09:25:09 -0400 Subject: [PATCH 04/20] docs: show running import_contract! tests against testnet first Co-Authored-By: Claude --- crates/stellar-registry/README.md | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/crates/stellar-registry/README.md b/crates/stellar-registry/README.md index 50f761e..61d903f 100644 --- a/crates/stellar-registry/README.md +++ b/crates/stellar-registry/README.md @@ -71,7 +71,13 @@ Two options, in either order: - `args = (..)` passes constructor arguments, as a tuple like `Env::register` takes: `register_import!(&env, our_dao, args = (&admin, 7u32))`. - `contract = ..` registers a native contract in place of the Wasm, when you want to control what the dependency returns: `register_import!(&env, our_dao, contract = MockDao)`. `MockDao` is an ordinary `#[contract]` type with the functions your contract calls. -Both macros read the same cache files (`target/stellar//deployed/.id` and `.wasm`), so once `import_contract!` has built, `register_import!` needs nothing more. To run tests with no network access, such as in CI, set `STELLAR_NO_REGISTRY=1` and create those two files first (`stellar registry fetch-contract-id` and `stellar contract fetch`, or commit copies and put them in place before building). The [`hello_caller` fixture](https://github.com/stellar-registry/cli/tree/main/crates/stellar-registry-test/fixtures/contracts/hello_caller) is a complete example, run by `just test-register-import`. +Running tests is an ordinary build, so there is nothing to build first. Both macros resolve the name at compile time on the network the build uses, and share the cached id and Wasm (`target/stellar//deployed/.id` and `.wasm`). Run tests on a network where the name is registered: + +```bash +STELLAR_NETWORK=testnet cargo test +``` + +For tests with no network access, such as in CI, set `STELLAR_NO_REGISTRY=1` and put those two files in place before building (from `stellar registry fetch-contract-id` and `stellar contract fetch`, or from copies you commit). The [`hello_caller` fixture](https://github.com/stellar-registry/cli/tree/main/crates/stellar-registry-test/fixtures/contracts/hello_caller) does this, run by `just test-register-import`. Stellar Asset Contracts (`xlm`, `"CODE:ISSUER"`, or a registered name that resolves to a SAC) are not supported by `register_import!` yet and give a compile error. Until they are, register a test asset with `Env::register_stellar_asset_contract_v2`. From e9f61983fd11f825d0f77228808326657162ceab Mon Sep 17 00:00:00 2001 From: pselle Date: Fri, 16 Oct 2026 10:33:28 -0400 Subject: [PATCH 05/20] feat: named constructor args for register_import! `args = { admin, quorum: 7u32 }` passes constructor arguments by name. The macro reads the constructor's parameters from the imported Wasm's spec, orders the values to match, and calls the typed `Args::__constructor` that `contractimport!` generates, so names and types are checked at compile time. Leaving out `args` when the constructor takes parameters is a compile error listing them. The tuple form still works and is type-checked too. Co-Authored-By: Claude --- Cargo.lock | 1 + Cargo.toml | 1 + crates/stellar-registry-macro/Cargo.toml | 1 + crates/stellar-registry-macro/src/contract.rs | 383 ++++++++++++++++-- crates/stellar-registry-macro/src/lib.rs | 17 +- .../contracts/hello_caller/Cargo.lock | 1 + .../contracts/hello_caller/src/lib.rs | 4 +- crates/stellar-registry/README.md | 4 +- 8 files changed, 373 insertions(+), 39 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index b558910..cbf487b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4265,6 +4265,7 @@ dependencies = [ "proc-macro2", "quote", "sha2 0.10.9", + "soroban-spec", "stellar-build", "stellar-registry-name", "stellar-strkey 0.0.18", diff --git a/Cargo.toml b/Cargo.toml index 468bf1f..b8c2f70 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -23,6 +23,7 @@ stellar-cli = { version = "28.1.0", package = "soroban-cli", default-features = soroban-rpc = { package = "stellar-rpc-client", version = "28.0.0" } soroban-sdk = { version = "28.0.0" } soroban-spec-tools = "28.1.0" +soroban-spec = "28.0.0" admin-sep = "0.28.1" stellar-xdr = "=28.0.0" diff --git a/crates/stellar-registry-macro/Cargo.toml b/crates/stellar-registry-macro/Cargo.toml index c25c519..9bcf86a 100644 --- a/crates/stellar-registry-macro/Cargo.toml +++ b/crates/stellar-registry-macro/Cargo.toml @@ -19,6 +19,7 @@ stellar-build = { workspace = true } stellar-registry-name = { workspace = true } stellar-strkey = { workspace = true } stellar-xdr = { workspace = true } +soroban-spec = { workspace = true } sha2 = { workspace = true } [lints] diff --git a/crates/stellar-registry-macro/src/contract.rs b/crates/stellar-registry-macro/src/contract.rs index 73c2bc8..2a5b218 100644 --- a/crates/stellar-registry-macro/src/contract.rs +++ b/crates/stellar-registry-macro/src/contract.rs @@ -12,6 +12,8 @@ use syn::{ use stellar_registry_name::Prefixed; +use stellar_xdr::{ScSpecEntry, ScSpecTypeDef}; + use crate::asset; use crate::util::{Name, explorer_url, manifest, mod_ident, network_name}; @@ -31,13 +33,49 @@ impl Parse for Input { } } +/// `args = { name: value, .. }`, or a positional tuple `args = (..)`. +enum Args { + Named { + brace: syn::token::Brace, + fields: Vec<(Ident, Expr)>, + }, + Positional(Expr), +} + +impl Parse for Args { + fn parse(input: ParseStream) -> syn::Result { + if !input.peek(syn::token::Brace) { + return Ok(Self::Positional(input.parse()?)); + } + let content; + let brace = syn::braced!(content in input); + let mut fields = Vec::new(); + while !content.is_empty() { + let name: Ident = content.parse()?; + // `{ admin }` is shorthand for `{ admin: admin }`, as in a struct literal. + let value = if content.peek(Token![:]) { + content.parse::()?; + content.parse()? + } else { + syn::parse_quote!(#name) + }; + fields.push((name, value)); + if content.is_empty() { + break; + } + content.parse::()?; + } + Ok(Self::Named { brace, fields }) + } +} + /// `register_import!(env_expr, name [, args = (..)] [, contract = Mock])` — /// `name` as in [`Input`]; the options may come in either order. struct RegisterInput { env: Expr, name: Name, - /// Constructor arguments; `()` when omitted. - args: Option, + /// Constructor arguments, checked against the imported Wasm's constructor. + args: Option, /// A native contract to register instead of the imported Wasm. contract: Option, } @@ -52,9 +90,9 @@ impl Parse for RegisterInput { break; } let key: Ident = input.parse()?; - let slot = match key.to_string().as_str() { - "args" => &mut args, - "contract" => &mut contract, + let given = match key.to_string().as_str() { + "args" => args.is_some(), + "contract" => contract.is_some(), other => { return Err(syn::Error::new( key.span(), @@ -64,14 +102,18 @@ impl Parse for RegisterInput { )); } }; - if slot.is_some() { + if given { return Err(syn::Error::new( key.span(), format!("`{key}` given more than once"), )); } input.parse::()?; - *slot = Some(input.parse()?); + if key == "args" { + args = Some(input.parse()?); + } else { + contract = Some(input.parse()?); + } } Ok(Self { env, @@ -451,17 +493,166 @@ pub(crate) fn import_contract( }) } +/// Emit a block expression registering a contract at the address +/// `import_contract!` baked in: the imported Wasm, or `contract` (a native +/// mock) when given. Evaluates to the `Address`. +/// The constructor's parameters (name and type), read from the contract spec +/// embedded in the Wasm; `None` if the contract has no constructor. +fn constructor_params(wasm: &[u8]) -> Result>, String> { + let entries = soroban_spec::read::from_wasm(wasm) + .map_err(|e| format!("could not read the contract spec from the Wasm: {e}"))?; + Ok(entries.into_iter().find_map(|entry| match entry { + ScSpecEntry::FunctionV0(f) if f.name.to_utf8_string_lossy() == "__constructor" => Some( + f.inputs + .iter() + .map(|i| (i.name.to_utf8_string_lossy(), i.type_.clone())) + .collect(), + ), + _ => None, + })) +} + +/// A spec type as the Rust type a test would pass, for error messages. +fn type_name(t: &ScSpecTypeDef) -> String { + use ScSpecTypeDef as T; + match t { + T::Bool => "bool".into(), + T::U32 => "u32".into(), + T::I32 => "i32".into(), + T::U64 => "u64".into(), + T::I64 => "i64".into(), + T::U128 => "u128".into(), + T::I128 => "i128".into(), + T::Address => "Address".into(), + T::MuxedAddress => "MuxedAddress".into(), + T::String => "String".into(), + T::Symbol => "Symbol".into(), + T::Bytes => "Bytes".into(), + T::BytesN(b) => format!("BytesN<{}>", b.n), + T::Option(o) => format!("Option<{}>", type_name(&o.value_type)), + T::Vec(v) => format!("Vec<{}>", type_name(&v.element_type)), + T::Map(m) => format!( + "Map<{}, {}>", + type_name(&m.key_type), + type_name(&m.value_type) + ), + T::Udt(u) => u.name.to_utf8_string_lossy(), + other => other.name().to_string(), + } +} + +/// `args = { admin:
, fee: }` — a starting point to paste. +fn args_suggestion(params: &[(String, ScSpecTypeDef)]) -> String { + let fields: Vec = params + .iter() + .map(|(name, t)| format!("{name}: <{}>", type_name(t))) + .collect(); + format!("args = {{ {} }}", fields.join(", ")) +} + +/// Order named `args` by the constructor's parameters, reporting unknown, +/// repeated and missing names. +fn order_named_args<'a>( + fields: &'a [(Ident, Expr)], + params: &[(String, ScSpecTypeDef)], + brace_span: proc_macro2::Span, +) -> syn::Result> { + let expected = || args_suggestion(params); + for (i, (name, _)) in fields.iter().enumerate() { + if !params.iter().any(|(p, _)| name == p) { + return Err(syn::Error::new( + name.span(), + format!( + "the constructor has no parameter `{name}`; expected {}", + expected() + ), + )); + } + if fields[..i].iter().any(|(earlier, _)| earlier == name) { + return Err(syn::Error::new( + name.span(), + format!("`{name}` given more than once"), + )); + } + } + params + .iter() + .map(|(p, _)| { + fields + .iter() + .find(|(name, _)| name == p) + .map(|(_, value)| value) + .ok_or_else(|| { + syn::Error::new( + brace_span, + format!( + "missing constructor argument `{p}`; expected {}", + expected() + ), + ) + }) + }) + .collect() +} + +/// Constructor args for the imported Wasm, as a typed call to the +/// `Args::__constructor` that `contractimport!` generates, so rustc checks +/// each value against the constructor's parameter types. +fn wasm_args( + input: &RegisterInput, + mod_ident: &Ident, + params: Option<&[(String, ScSpecTypeDef)]>, +) -> syn::Result { + let name = input.name.raw(); + let values: Vec<&Expr> = match (&input.args, params) { + (None, None | Some([])) => return Ok(quote!(())), + (None, Some(params)) => { + return Err(syn::Error::new( + input.name.span(), + format!( + "`{name}`'s constructor takes arguments. Pass them with `args`:\n\n \ + register_import!(env, {name}, {})", + args_suggestion(params) + ), + )); + } + (Some(args), None) => { + let span = match args { + Args::Named { brace, .. } => brace.span.join(), + Args::Positional(e) => syn::spanned::Spanned::span(e), + }; + return Err(syn::Error::new( + span, + format!("`{name}` has no constructor, so it takes no `args`"), + )); + } + (Some(Args::Named { brace, fields }), Some(params)) => { + order_named_args(fields, params, brace.span.join())? + } + (Some(Args::Positional(Expr::Tuple(tuple))), Some(_)) => tuple.elems.iter().collect(), + (Some(Args::Positional(other)), Some(_)) => return Ok(quote!(#other)), + }; + // Spanned per value, so a type mismatch points at the value, not the macro. + let values = values + .iter() + .map(|v| quote::quote_spanned!(syn::spanned::Spanned::span(v)=> &(#v))); + Ok(quote!(#mod_ident::Args::__constructor(#(#values),*))) +} + /// Emit a block expression registering a contract at the address /// `import_contract!` baked in: the imported Wasm, or `contract` (a native /// mock) when given. Evaluates to the `Address`. fn expand_register( input: &RegisterInput, resolved: &Resolved, + params: Option<&[(String, ScSpecTypeDef)]>, ) -> syn::Result { - let (wasm_path, address) = match resolved { + let (mod_ident, wasm_path, address) = match resolved { Resolved::Wasm { - wasm_path, address, .. - } => (wasm_path.to_string_lossy(), address), + mod_ident, + wasm_path, + address, + } => (mod_ident, wasm_path, address), Resolved::Sac { .. } => { return Err(syn::Error::new( input.name.span(), @@ -475,16 +666,39 @@ fn expand_register( } }; let env = &input.env; - let contract = input.contract.as_ref().map_or_else( - || quote!(::core::include_bytes!(#wasm_path).as_slice()), - |contract| quote!(#contract), - ); - let args = input - .args - .as_ref() - .map_or_else(|| quote!(()), |args| quote!(#args)); + let (module, contract, args) = if let Some(contract) = &input.contract { + // A native mock has no spec to name its parameters by. + let args = match &input.args { + None => quote!(()), + Some(Args::Positional(args)) => quote!(#args), + Some(Args::Named { brace, .. }) => { + return Err(syn::Error::new( + brace.span.join(), + "named `args` need the imported Wasm's constructor; with `contract = ..`, \ + pass the mock's constructor arguments as a tuple, `args = (..)`", + )); + } + }; + (quote!(), quote!(#contract), args) + } else { + let args = wasm_args(input, mod_ident, params)?; + let wasm_path = wasm_path.to_string_lossy(); + ( + quote! { + #[allow(non_snake_case)] + mod #mod_ident { + #![allow(clippy::ref_option, clippy::too_many_arguments)] + use ::soroban_sdk; + soroban_sdk::contractimport!(file = #wasm_path); + } + }, + quote!(#mod_ident::WASM), + args, + ) + }; Ok(quote! { { + #module let __env: &::soroban_sdk::Env = #env; ::soroban_sdk::Env::register_at( __env, @@ -500,7 +714,18 @@ pub(crate) fn register_import( input: proc_macro::TokenStream, ) -> syn::Result { let input: RegisterInput = syn::parse(input)?; - expand_register(&input, &resolve(&input.name, "register_import")?) + let resolved = resolve(&input.name, "register_import")?; + // Only the imported Wasm's constructor is read; a mock has no spec. + let params = match (&resolved, &input.contract) { + (Resolved::Wasm { wasm_path, .. }, None) => { + let err = |e: String| syn::Error::new(input.name.span(), e); + let wasm = std::fs::read(wasm_path) + .map_err(|e| err(format!("could not read {}: {e}", wasm_path.display())))?; + constructor_params(&wasm).map_err(err)? + } + _ => None, + }; + expand_register(&input, &resolved, params.as_deref()) } #[cfg(test)] @@ -779,9 +1004,29 @@ mod register { } } - fn expanded(tokens: proc_macro2::TokenStream, resolved: &Resolved) -> String { + /// `__constructor(admin: Address, fee: u32)`. + fn params() -> Vec<(String, ScSpecTypeDef)> { + vec![ + ("admin".into(), ScSpecTypeDef::Address), + ("fee".into(), ScSpecTypeDef::U32), + ] + } + + fn expand( + tokens: proc_macro2::TokenStream, + resolved: &Resolved, + params: Option<&[(String, ScSpecTypeDef)]>, + ) -> syn::Result { let input: RegisterInput = parse2(tokens).unwrap(); - expand_register(&input, resolved).unwrap().to_string() + expand_register(&input, resolved, params).map(|out| out.to_string()) + } + + fn expanded(tokens: proc_macro2::TokenStream, resolved: &Resolved) -> String { + expand(tokens, resolved, None).unwrap() + } + + fn with_params(tokens: proc_macro2::TokenStream) -> syn::Result { + expand(tokens, &wasm(), Some(¶ms())) } fn parse_err(tokens: proc_macro2::TokenStream) -> String { @@ -822,26 +1067,106 @@ mod register { fn registers_the_cached_wasm_at_the_baked_address() { let out = expanded(quote!(&env, our_dao), &wasm()); assert!(out.contains("Env :: register_at"), "{out}"); - assert!(out.contains("include_bytes !"), "{out}"); + assert!(out.contains("contractimport !"), "{out}"); assert!(out.contains("our_dao.wasm"), "{out}"); + assert!( + out.contains("our_dao :: WASM , () ,"), + "no constructor: {out}" + ); assert!(out.contains(A), "bakes the resolved id: {out}"); + } + + #[test] + fn named_args_are_ordered_by_the_constructor_and_typed() { + // Given out of order, and `admin` in shorthand. + let out = with_params(quote!(&env, our_dao, args = { fee: 7u32, admin })).unwrap(); + assert!( + out.contains("our_dao :: Args :: __constructor (& (admin) , & (7u32))"), + "{out}" + ); + } + + #[test] + fn positional_tuple_args_are_typed_too() { + let out = with_params(quote!(&env, our_dao, args = (&admin, 7u32))).unwrap(); + assert!( + out.contains("our_dao :: Args :: __constructor (& (& admin) , & (7u32))"), + "{out}" + ); + } + + #[test] + fn missing_args_suggest_the_constructor_parameters() { + let msg = with_params(quote!(&env, our_dao)).unwrap_err().to_string(); + assert!(msg.contains("constructor takes arguments"), "{msg}"); assert!( - out.contains(". as_slice () , () ,"), - "no constructor args by default: {out}" + msg.contains("register_import!(env, our_dao, args = { admin:
, fee: })"), + "{msg}" ); } #[test] - fn passes_constructor_args_through() { - let out = expanded(quote!(&env, our_dao, args = (&admin, 7u32)), &wasm()); - assert!(out.contains("(& admin , 7u32)"), "{out}"); + fn named_args_report_unknown_repeated_and_missing_names() { + let msg = with_params(quote!(&env, our_dao, args = { owner: a, fee: 1u32 })) + .unwrap_err() + .to_string(); + assert!(msg.contains("no parameter `owner`"), "{msg}"); + let msg = with_params(quote!(&env, our_dao, args = { admin, admin, fee: 1u32 })) + .unwrap_err() + .to_string(); + assert!(msg.contains("`admin` given more than once"), "{msg}"); + let msg = with_params(quote!(&env, our_dao, args = { admin })) + .unwrap_err() + .to_string(); + assert!(msg.contains("missing constructor argument `fee`"), "{msg}"); + } + + #[test] + fn args_without_a_constructor_are_rejected() { + let msg = expand(quote!(&env, our_dao, args = { admin }), &wasm(), None) + .unwrap_err() + .to_string(); + assert!(msg.contains("has no constructor"), "{msg}"); + } + + #[test] + fn named_args_are_rejected_for_a_mock() { + let msg = expand( + quote!(&env, our_dao, contract = MockDao, args = { admin }), + &wasm(), + None, + ) + .unwrap_err() + .to_string(); + assert!(msg.contains("as a tuple"), "{msg}"); + let out = expanded( + quote!(&env, our_dao, contract = MockDao, args = (&admin,)), + &wasm(), + ); + assert!(out.contains("MockDao , (& admin ,)"), "{out}"); + } + + #[test] + fn spec_types_are_named_as_rust_types() { + use stellar_xdr::{ScSpecTypeBytesN, ScSpecTypeVec}; + assert_eq!(type_name(&ScSpecTypeDef::I128), "i128"); + assert_eq!( + type_name(&ScSpecTypeDef::BytesN(ScSpecTypeBytesN { n: 32 })), + "BytesN<32>" + ); + assert_eq!( + type_name(&ScSpecTypeDef::Vec(Box::new(ScSpecTypeVec { + element_type: Box::new(ScSpecTypeDef::Address), + }))), + "Vec
" + ); } #[test] fn contract_option_replaces_the_wasm() { let out = expanded(quote!(&env, our_dao, contract = mocks::MockDao), &wasm()); assert!(out.contains("mocks :: MockDao"), "{out}"); - assert!(!out.contains("include_bytes"), "{out}"); + assert!(!out.contains("contractimport"), "{out}"); assert!(out.contains(A), "still at the baked address: {out}"); } @@ -855,7 +1180,7 @@ mod register { quote!(&env, "circle/usdc", contract = MockToken), ] { let input: RegisterInput = parse2(tokens).unwrap(); - let msg = expand_register(&input, &sac).unwrap_err().to_string(); + let msg = expand_register(&input, &sac, None).unwrap_err().to_string(); assert!(msg.contains("Stellar Asset Contracts"), "{msg}"); assert!(msg.contains("SAC support is coming"), "{msg}"); } diff --git a/crates/stellar-registry-macro/src/lib.rs b/crates/stellar-registry-macro/src/lib.rs index ad405e2..f65933e 100644 --- a/crates/stellar-registry-macro/src/lib.rs +++ b/crates/stellar-registry-macro/src/lib.rs @@ -88,8 +88,8 @@ pub fn import_contract(input: TokenStream) -> TokenStream { /// let env = Env::default(); /// // The imported contract's own Wasm, as cached by `import_contract!`: /// register_import!(&env, our_dao); -/// // ...with constructor arguments: -/// register_import!(&env, our_dao, args = (&admin, 7u32)); +/// // ...with constructor arguments, by name: +/// register_import!(&env, our_dao, args = { admin, quorum: 7u32 }); /// // ...or a native mock in place of the Wasm: /// register_import!(&env, our_dao, contract = MockDao); /// ``` @@ -98,11 +98,16 @@ pub fn import_contract(input: TokenStream) -> TokenStream { /// and resolved by the same code, so both macros always agree on the /// address and Wasm (same cache files, same `STELLAR_NO_REGISTRY` /// handling, same flagged-contract check). -/// - `args = (..)` is the constructor-argument tuple, as for `Env::register` -/// (`()` when omitted). +/// - `args = { name: value, .. }` passes constructor arguments by parameter +/// name, read from the imported Wasm's constructor; `{ admin }` is +/// shorthand for `{ admin: admin }`. Values are borrowed, not moved, and +/// type-checked against the constructor. Leaving out `args` when the +/// constructor takes parameters is a compile error that lists them. A +/// positional tuple, `args = (..)`, also works. /// - `contract = ` registers a native `#[contract]` type instead of the -/// Wasm, for when you want to control what the dependency returns. `args` -/// then go to the mock's constructor. +/// Wasm, for when you want to control what the dependency returns. Its +/// constructor arguments are a tuple, `args = (..)`, since a mock has no +/// spec to name them by. /// /// Exported as `stellar_registry::testutils::register_import!`, behind /// `stellar-registry`'s `testutils` feature. The expansion calls diff --git a/crates/stellar-registry-test/fixtures/contracts/hello_caller/Cargo.lock b/crates/stellar-registry-test/fixtures/contracts/hello_caller/Cargo.lock index 4c6ac03..e4305e0 100644 --- a/crates/stellar-registry-test/fixtures/contracts/hello_caller/Cargo.lock +++ b/crates/stellar-registry-test/fixtures/contracts/hello_caller/Cargo.lock @@ -1820,6 +1820,7 @@ dependencies = [ "proc-macro2", "quote", "sha2", + "soroban-spec", "stellar-build", "stellar-registry-name", "stellar-strkey 0.0.18", diff --git a/crates/stellar-registry-test/fixtures/contracts/hello_caller/src/lib.rs b/crates/stellar-registry-test/fixtures/contracts/hello_caller/src/lib.rs index 8402347..b4e977f 100644 --- a/crates/stellar-registry-test/fixtures/contracts/hello_caller/src/lib.rs +++ b/crates/stellar-registry-test/fixtures/contracts/hello_caller/src/lib.rs @@ -47,7 +47,7 @@ mod test { let env = Env::default(); let admin = Address::generate(&env); // `hello_v1`'s constructor takes an admin. - register_import!(&env, hello_v1, args = (&admin,)); + register_import!(&env, hello_v1, args = { admin }); assert_eq!(greet(&env, "world"), String::from_str(&env, "world")); } @@ -64,7 +64,7 @@ mod test { fn registers_at_the_same_address_either_way() { let env = Env::default(); let admin = Address::generate(&env); - let wasm = register_import!(&env, hello_v1, args = (&admin,)); + let wasm = register_import!(&env, hello_v1, args = { admin }); let mock = register_import!(&env, hello_v1, contract = MockHello); assert_eq!(wasm, mock); diff --git a/crates/stellar-registry/README.md b/crates/stellar-registry/README.md index 61d903f..9b242e3 100644 --- a/crates/stellar-registry/README.md +++ b/crates/stellar-registry/README.md @@ -68,8 +68,8 @@ fn proposes() { Two options, in either order: -- `args = (..)` passes constructor arguments, as a tuple like `Env::register` takes: `register_import!(&env, our_dao, args = (&admin, 7u32))`. -- `contract = ..` registers a native contract in place of the Wasm, when you want to control what the dependency returns: `register_import!(&env, our_dao, contract = MockDao)`. `MockDao` is an ordinary `#[contract]` type with the functions your contract calls. +- `args = { .. }` passes constructor arguments by name: `register_import!(&env, our_dao, args = { admin, quorum: 7u32 })`. The names and types come from the imported contract's constructor, so a misspelled name or wrong type is a compile error, and leaving `args` out when the constructor needs some fails with the list to fill in, e.g. `args = { admin:
, quorum: }`. `{ admin }` is shorthand for `{ admin: admin }`; values are borrowed, not moved. A positional tuple, `args = (&admin, 7u32)`, also works. +- `contract = ..` registers a native contract in place of the Wasm, when you want to control what the dependency returns: `register_import!(&env, our_dao, contract = MockDao)`. `MockDao` is an ordinary `#[contract]` type with the functions your contract calls; pass its constructor arguments as a tuple. Running tests is an ordinary build, so there is nothing to build first. Both macros resolve the name at compile time on the network the build uses, and share the cached id and Wasm (`target/stellar//deployed/.id` and `.wasm`). Run tests on a network where the name is registered: From d15e0783970c94bc48d355389cc42e2035697134 Mon Sep 17 00:00:00 2001 From: pselle Date: Fri, 16 Oct 2026 10:58:10 -0400 Subject: [PATCH 06/20] test: add a testnet recipe for import_contract! + register_import! The new `unverified_caller` fixture imports `unverified`, the root registry's subregistry on testnet, and tests itself with `register_import!` and named constructor args. `just test-register-import-testnet` runs it with `STELLAR_NETWORK=testnet`, so the id and Wasm are really fetched. It needs network access, so CI doesn't run it. Co-Authored-By: Claude --- CLAUDE.md | 1 + Cargo.toml | 5 +- .../contracts/unverified_caller/Cargo.lock | 2271 +++++++++++++++++ .../contracts/unverified_caller/Cargo.toml | 26 + .../contracts/unverified_caller/src/lib.rs | 41 + justfile | 4 + 6 files changed, 2347 insertions(+), 1 deletion(-) create mode 100644 crates/stellar-registry-test/fixtures/contracts/unverified_caller/Cargo.lock create mode 100644 crates/stellar-registry-test/fixtures/contracts/unverified_caller/Cargo.toml create mode 100644 crates/stellar-registry-test/fixtures/contracts/unverified_caller/src/lib.rs diff --git a/CLAUDE.md b/CLAUDE.md index 9b711ed..fa90ae3 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -58,6 +58,7 @@ Note: the `justfile` still carries some recipes from the monorepo. Prefer the `c - Unit tests run without external dependencies. - Integration tests require a local Stellar RPC (Docker `stellar/quickstart`) and the `integration-tests` feature flag. - `just test-register-import` runs the `hello_caller` fixture, which is excluded from the workspace because `import_contract!` needs seeded cache files to compile. +- `just test-register-import-testnet` runs the `unverified_caller` fixture online against testnet, where `unverified` is really registered. It needs network access, so CI doesn't run it. - Command tests use `stellar_registry_test::RegistryTest`, defined in local crate `crates/stellar-registry-test` ## Cross-repo dependencies diff --git a/Cargo.toml b/Cargo.toml index 4f36387..84343d6 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -3,7 +3,10 @@ resolver = "2" default-members = ["crates/*"] members = ["crates/*", "crates/stellar-registry-test/fixtures/contracts/*"] # Needs seeded `import_contract!` cache files to compile; see its Cargo.toml. -exclude = ["crates/stellar-registry-test/fixtures/contracts/hello_caller"] +exclude = [ + "crates/stellar-registry-test/fixtures/contracts/hello_caller", + "crates/stellar-registry-test/fixtures/contracts/unverified_caller", +] [workspace.package] repository = "https://github.com/stellar-registry/cli" diff --git a/crates/stellar-registry-test/fixtures/contracts/unverified_caller/Cargo.lock b/crates/stellar-registry-test/fixtures/contracts/unverified_caller/Cargo.lock new file mode 100644 index 0000000..77a5a52 --- /dev/null +++ b/crates/stellar-registry-test/fixtures/contracts/unverified_caller/Cargo.lock @@ -0,0 +1,2271 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 4 + +[[package]] +name = "ahash" +version = "0.8.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a15f179cd60c4584b8a8c596927aadc462e27f2ca70c04e0071964a73ba7a75" +dependencies = [ + "cfg-if", + "once_cell", + "version_check", + "zerocopy", +] + +[[package]] +name = "allocator-api2" +version = "0.2.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "683d7910e743518b0e34f1186f92494becacb047c7b6bf616c96772180fef923" + +[[package]] +name = "android_system_properties" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae221649c9976a6f6c56ae1facf410f3ddb33cc661c4b7b61020a912d4237fbc" +dependencies = [ + "libc", +] + +[[package]] +name = "arbitrary" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7d5a26814d8dcb93b0e5a0ff3c6d80a8843bafb21b39e8e18a6f05471870e110" +dependencies = [ + "derive_arbitrary", +] + +[[package]] +name = "ark-bls12-381" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3df4dcc01ff89867cd86b0da835f23c3f02738353aaee7dde7495af71363b8d5" +dependencies = [ + "ark-ec", + "ark-ff", + "ark-serialize", + "ark-std", +] + +[[package]] +name = "ark-bn254" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d69eab57e8d2663efa5c63135b2af4f396d66424f88954c21104125ab6b3e6bc" +dependencies = [ + "ark-ec", + "ark-ff", + "ark-std", +] + +[[package]] +name = "ark-ec" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "43d68f2d516162846c1238e755a7c4d131b892b70cc70c471a8e3ca3ed818fce" +dependencies = [ + "ahash", + "ark-ff", + "ark-poly", + "ark-serialize", + "ark-std", + "educe", + "fnv", + "hashbrown 0.15.5", + "itertools", + "num-bigint", + "num-integer", + "num-traits", + "zeroize", +] + +[[package]] +name = "ark-ff" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a177aba0ed1e0fbb62aa9f6d0502e9b46dad8c2eab04c14258a1212d2557ea70" +dependencies = [ + "ark-ff-asm", + "ark-ff-macros", + "ark-serialize", + "ark-std", + "arrayvec", + "digest 0.10.7", + "educe", + "itertools", + "num-bigint", + "num-traits", + "paste", + "zeroize", +] + +[[package]] +name = "ark-ff-asm" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62945a2f7e6de02a31fe400aa489f0e0f5b2502e69f95f853adb82a96c7a6b60" +dependencies = [ + "quote", + "syn 2.0.119", +] + +[[package]] +name = "ark-ff-macros" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09be120733ee33f7693ceaa202ca41accd5653b779563608f1234f78ae07c4b3" +dependencies = [ + "num-bigint", + "num-traits", + "proc-macro2", + "quote", + "syn 2.0.119", +] + +[[package]] +name = "ark-poly" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "579305839da207f02b89cd1679e50e67b4331e2f9294a57693e5051b7703fe27" +dependencies = [ + "ahash", + "ark-ff", + "ark-serialize", + "ark-std", + "educe", + "fnv", + "hashbrown 0.15.5", +] + +[[package]] +name = "ark-serialize" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f4d068aaf107ebcd7dfb52bc748f8030e0fc930ac8e360146ca54c1203088f7" +dependencies = [ + "ark-serialize-derive", + "ark-std", + "arrayvec", + "digest 0.10.7", + "num-bigint", +] + +[[package]] +name = "ark-serialize-derive" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "213888f660fddcca0d257e88e54ac05bca01885f258ccdf695bafd77031bb69d" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.119", +] + +[[package]] +name = "ark-std" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "246a225cc6131e9ee4f24619af0f19d67761fff15d7ccc22e42b80846e69449a" +dependencies = [ + "num-traits", + "rand", +] + +[[package]] +name = "arrayvec" +version = "0.7.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3fb67a6e08acf24fdeccbac2cb6ac4305825bd1f117462e0e6f2f193345ad56" + +[[package]] +name = "autocfg" +version = "1.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2032f911046de80f0a198e0901378627c33f59ea0ac00e363d481118bd70a53" + +[[package]] +name = "base16ct" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c7f02d4ea65f2c1853089ffd8d2787bdbc63de2f0d29dedbcf8ccdfa0ccd4cf" + +[[package]] +name = "base64" +version = "0.22.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" + +[[package]] +name = "base64" +version = "0.23.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac07cdecf99051d9a5238b80f35af32cdeba5b336e55d957b318b50137e18da5" + +[[package]] +name = "base64ct" +version = "1.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2af50177e190e07a26ab74f8b1efbfe2ef87da2116221318cb1c2e82baf7de06" + +[[package]] +name = "bitflags" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" + +[[package]] +name = "block-buffer" +version = "0.10.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" +dependencies = [ + "generic-array", +] + +[[package]] +name = "block-buffer" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d2f6c7dbe95a6ed67ad9f18e57daf93a2f034c524b99fd2b76d18fdfeb6660aa" +dependencies = [ + "hybrid-array", +] + +[[package]] +name = "bs58" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bf88ba1141d185c399bee5288d850d63b8369520c1eafc32a0430b5b6c287bf4" +dependencies = [ + "tinyvec", +] + +[[package]] +name = "bumpalo" +version = "3.20.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72f5acc6cb2ba439de613abc23857ec3d78374d8ed5ac84e9d11336e87da8649" + +[[package]] +name = "byteorder" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" + +[[package]] +name = "bytes-lit" +version = "0.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b04f2b1d34cb428043f14aa4c853d14294532e8bbde3b6a3bc2faaaae31a1dd" +dependencies = [ + "num-bigint", + "proc-macro2", + "quote", + "syn 2.0.119", +] + +[[package]] +name = "camino" +version = "1.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbbad30e4b4c14a39e3cc8aed085a12a327257c316619c93581e017bc52be591" +dependencies = [ + "serde_core", +] + +[[package]] +name = "cargo-platform" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e35af189006b9c0f00a064685c727031e3ed2d8020f7ba284d78cc2671bd36ea" +dependencies = [ + "serde", +] + +[[package]] +name = "cargo_metadata" +version = "0.18.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2d886547e41f740c616ae73108f6eb70afe6d940c7bc697cb30f13daec073037" +dependencies = [ + "camino", + "cargo-platform", + "semver", + "serde", + "serde_json", + "thiserror 1.0.69", +] + +[[package]] +name = "cc" +version = "1.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6651c9ed80effdc7db0ff72512157f901af5e3549e341e24b1dd4887d836d838" +dependencies = [ + "find-msvc-tools", + "shlex", +] + +[[package]] +name = "cfg-if" +version = "1.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4e7648175b45a9a48536d676f68d918270699102aa8dab5496df06904c914600" + +[[package]] +name = "cfg_eval" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "45565fc9416b9896014f5732ac776f810ee53a66730c17e4020c3ec064a8f88f" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.119", +] + +[[package]] +name = "chrono" +version = "0.4.45" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1aa79e62e7697b8e29b513a68abacf485adcd1fe8284a4316c5ae868e6633327" +dependencies = [ + "iana-time-zone", + "num-traits", + "serde", + "windows-link", +] + +[[package]] +name = "const-oid" +version = "0.9.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c2459377285ad874054d797f3ccebf984978aa39129f6eafde5cdc8315b612f8" + +[[package]] +name = "const-oid" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a6ef517f0926dd24a1582492c791b6a4818a4d94e789a334894aa15b0d12f55c" + +[[package]] +name = "core-foundation-sys" +version = "0.8.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" + +[[package]] +name = "cpufeatures" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "59ed5838eebb26a2bb2e58f6d5b5316989ae9d08bab10e0e6d103e656d1b0280" +dependencies = [ + "libc", +] + +[[package]] +name = "cpufeatures" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5ca28b0ae3115b884660db4118d803791fd6756b6e88f39c0f3f7859060d7566" +dependencies = [ + "libc", +] + +[[package]] +name = "crate-git-revision" +version = "0.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c521bf1f43d31ed2f73441775ed31935d77901cb3451e44b38a1c1612fcbaf98" +dependencies = [ + "serde", + "serde_derive", + "serde_json", +] + +[[package]] +name = "crate-git-revision" +version = "0.0.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "54851b5b3f24621804b1cded2820975623c205e3055d2d44031cdb1237339ac8" +dependencies = [ + "serde", + "serde_derive", + "serde_json", +] + +[[package]] +name = "crypto-bigint" +version = "0.5.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0dc92fb57ca44df6db8059111ab3af99a63d5d0f8375d9972e319a379c6bab76" +dependencies = [ + "generic-array", + "rand_core", + "subtle", + "zeroize", +] + +[[package]] +name = "crypto-common" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" +dependencies = [ + "generic-array", + "typenum", +] + +[[package]] +name = "crypto-common" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ce6e4c961d6cd6c9a86db418387425e8bdeaf05b3c8bc1411e6dca4c252f1453" +dependencies = [ + "hybrid-array", +] + +[[package]] +name = "ctor" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67773048316103656a637612c4a62477603b777d91d9c62ff2290f9cde178fdb" +dependencies = [ + "ctor-proc-macro", + "dtor", +] + +[[package]] +name = "ctor-proc-macro" +version = "0.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2931af7e13dc045d8e9d26afccc6fa115d64e115c9c84b1166288b46f6782c2" + +[[package]] +name = "curve25519-dalek" +version = "4.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97fb8b7c4503de7d6ae7b42ab72a5a59857b4c937ec27a3d4539dba95b5ab2be" +dependencies = [ + "cfg-if", + "cpufeatures 0.2.17", + "curve25519-dalek-derive", + "digest 0.10.7", + "fiat-crypto 0.2.9", + "rustc_version", + "subtle", + "zeroize", +] + +[[package]] +name = "curve25519-dalek" +version = "5.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b5eed333089e2e1c1ac8c6c0398e5e2497b4c9926ca6d0365ed1e099afa5bc23" +dependencies = [ + "cfg-if", + "cpufeatures 0.3.1", + "curve25519-dalek-derive", + "digest 0.11.3", + "fiat-crypto 0.3.0", + "rustc_version", + "subtle", +] + +[[package]] +name = "curve25519-dalek-derive" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f46882e17999c6cc590af592290432be3bce0428cb0d5f8b6715e4dc7b383eb3" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.119", +] + +[[package]] +name = "darling" +version = "0.20.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc7f46116c46ff9ab3eb1597a45688b6715c6e628b5c133e288e709a29bcb4ee" +dependencies = [ + "darling_core 0.20.11", + "darling_macro 0.20.11", +] + +[[package]] +name = "darling" +version = "0.24.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed17f5901b6630b993ca003def43f2f8ef4014fc13b047b57aad617ff32bc2ec" +dependencies = [ + "darling_core 0.24.1", + "darling_macro 0.24.1", +] + +[[package]] +name = "darling_core" +version = "0.20.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0d00b9596d185e565c2207a0b01f8bd1a135483d02d9b7b0a54b11da8d53412e" +dependencies = [ + "fnv", + "ident_case", + "proc-macro2", + "quote", + "strsim", + "syn 2.0.119", +] + +[[package]] +name = "darling_core" +version = "0.24.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6837e2cf7485aaae18f86181d2f0e9a7ed297a025e220aeabf63fdebd3a2ddff" +dependencies = [ + "ident_case", + "proc-macro2", + "quote", + "strsim", + "syn 3.0.9", +] + +[[package]] +name = "darling_macro" +version = "0.20.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc34b93ccb385b40dc71c6fceac4b2ad23662c7eeb248cf10d529b7e055b6ead" +dependencies = [ + "darling_core 0.20.11", + "quote", + "syn 2.0.119", +] + +[[package]] +name = "darling_macro" +version = "0.24.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2ac7135c3ef02b2f7833bbeb1be5ba7f966dcde8a87c6b87f65a778d71a02785" +dependencies = [ + "darling_core 0.24.1", + "quote", + "syn 3.0.9", +] + +[[package]] +name = "data-encoding" +version = "2.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4583a4551df46e2792f82ceeac45e850d2e2d5debba0b91f102385cda5b11f06" + +[[package]] +name = "defmt" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2953bfe4f93bbd20cc71198842756f77d161884c99ebbabc41d80231ded88d1" +dependencies = [ + "bitflags", + "defmt-macros", +] + +[[package]] +name = "defmt-macros" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bad9c72e7ca2137e0dc3813245a0d282fd6daad32fd800af018306a9169b5fe8" +dependencies = [ + "defmt-parser", + "proc-macro2", + "quote", + "syn 2.0.119", +] + +[[package]] +name = "defmt-parser" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "10d60334b3b2e7c9d91ef8150abfb6fa4c1c39ebbcf4a81c2e346aad939fee3e" +dependencies = [ + "thiserror 2.0.21", +] + +[[package]] +name = "der" +version = "0.7.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e7c1832837b905bbfb5101e07cc24c8deddf52f93225eee6ead5f4d63d53ddcb" +dependencies = [ + "const-oid 0.9.6", + "zeroize", +] + +[[package]] +name = "deranged" +version = "0.5.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e9de72ce2ad1f90dc62fa25f0f430ef85eb4b0d8fa0be4f30373bc40a21d28e" +dependencies = [ + "serde_core", +] + +[[package]] +name = "derive_arbitrary" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67e77553c4162a157adbf834ebae5b415acbecbeafc7a74b0e886657506a7611" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.119", +] + +[[package]] +name = "digest" +version = "0.10.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" +dependencies = [ + "block-buffer 0.10.4", + "const-oid 0.9.6", + "crypto-common 0.1.6", + "subtle", +] + +[[package]] +name = "digest" +version = "0.11.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1dd6dbb5841937940781866fa1281a1ff7bd3bf827091440879f9994983d5c2" +dependencies = [ + "block-buffer 0.12.1", + "const-oid 0.10.2", + "crypto-common 0.2.2", +] + +[[package]] +name = "downcast-rs" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75b325c5dbd37f80359721ad39aca5a29fb04c89279657cffdda8736d0c0b9d2" + +[[package]] +name = "dtor" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "404d02eeb088a82cfd873006cb713fe411306c7d182c344905e101fb1167d301" +dependencies = [ + "dtor-proc-macro", +] + +[[package]] +name = "dtor-proc-macro" +version = "0.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f678cf4a922c215c63e0de95eb1ff08a958a81d47e485cf9da1e27bf6305cfa5" + +[[package]] +name = "dyn-clone" +version = "1.0.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d0881ea181b1df73ff77ffaaf9c7544ecc11e82fba9b5f27b262a3c73a332555" + +[[package]] +name = "ecdsa" +version = "0.16.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee27f32b5c5292967d2d4a9d7f1e0b0aed2c15daded5a60300e4abb9d8020bca" +dependencies = [ + "der", + "digest 0.10.7", + "elliptic-curve", + "rfc6979", + "signature", +] + +[[package]] +name = "ed25519" +version = "2.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "115531babc129696a58c64a4fef0a8bf9e9698629fb97e9e40767d235cfbcd53" +dependencies = [ + "pkcs8", + "signature", +] + +[[package]] +name = "ed25519-dalek" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "70e796c081cee67dc755e1a36a0a172b897fab85fc3f6bc48307991f64e4eca9" +dependencies = [ + "curve25519-dalek 4.1.3", + "ed25519", + "rand_core", + "serde", + "sha2 0.10.9", + "subtle", + "zeroize", +] + +[[package]] +name = "educe" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d7bc049e1bd8cdeb31b68bbd586a9464ecf9f3944af3958a7a9d0f8b9799417" +dependencies = [ + "enum-ordinalize", + "proc-macro2", + "quote", + "syn 2.0.119", +] + +[[package]] +name = "either" +version = "1.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0e9c71c2167ca323c882b99918929403426e2373ea17242ff5653e0d5e1058be" + +[[package]] +name = "elliptic-curve" +version = "0.13.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b5e6043086bf7973472e0c7dff2142ea0b680d30e18d9cc40f267efbf222bd47" +dependencies = [ + "base16ct", + "crypto-bigint", + "digest 0.10.7", + "ff", + "generic-array", + "group", + "rand_core", + "sec1", + "subtle", + "zeroize", +] + +[[package]] +name = "enum-ordinalize" +version = "4.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "89dd01549b09589510cf0647475075d12071456586d70f5c75c98ae2a5537677" +dependencies = [ + "enum-ordinalize-derive", +] + +[[package]] +name = "enum-ordinalize-derive" +version = "4.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a65863d15a4ce2888bd2f0f543cc963d3879c3a022c8ee43f6141d479a3ac815" +dependencies = [ + "proc-macro2", + "quote", + "syn 3.0.9", +] + +[[package]] +name = "equivalent" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" + +[[package]] +name = "escape-bytes" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2bfcf67fea2815c2fc3b90873fae90957be12ff417335dfadc7f52927feb03b2" + +[[package]] +name = "ethnum" +version = "1.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "40404c3f5f511ec4da6fe866ddf6a717c309fdbb69fbbad7b0f3edab8f2e835f" + +[[package]] +name = "ff" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c0b50bfb653653f9ca9095b427bed08ab8d75a137839d9ad64eb11810d5b6393" +dependencies = [ + "rand_core", + "subtle", +] + +[[package]] +name = "fiat-crypto" +version = "0.2.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "28dea519a9695b9977216879a3ebfddf92f1c08c05d984f8996aecd6ecdc811d" + +[[package]] +name = "fiat-crypto" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "64cd1e32ddd350061ae6edb1b082d7c54915b5c672c389143b9a63403a109f24" + +[[package]] +name = "find-msvc-tools" +version = "0.1.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aedcfb3409746eddb02b9e19ebda1c3394f759a152e48ee875a0844d1b955484" + +[[package]] +name = "fnv" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" + +[[package]] +name = "futures-core" +version = "0.3.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "92d699e522242e69e3003b94ecc1f960f3a5e015aa7c5d7486e65ad01dd94f5e" + +[[package]] +name = "futures-task" +version = "0.3.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cd417de3d1d015fc3bfd2b1ea46dfc7bab72ef86f1cc7cc9c78e728b34a6d1fd" + +[[package]] +name = "futures-util" +version = "0.3.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0d50a92467f8ba5dd6e3ee5d4bd04d73ab2e4e1c44474a0674821dfce14b79bc" +dependencies = [ + "futures-core", + "futures-task", + "pin-project-lite", + "slab", +] + +[[package]] +name = "generic-array" +version = "0.14.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4bb6743198531e02858aeaea5398fcc883e71851fcbcb5a2f773e2fb6cb1edf2" +dependencies = [ + "typenum", + "version_check", + "zeroize", +] + +[[package]] +name = "getrandom" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff2abc00be7fca6ebc474524697ae276ad847ad0a6b3faa4bcb027e9a4614ad0" +dependencies = [ + "cfg-if", + "js-sys", + "libc", + "wasi", + "wasm-bindgen", +] + +[[package]] +name = "group" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0f9ef7462f7c099f518d754361858f86d8a07af53ba9af0fe635bbccb151a63" +dependencies = [ + "ff", + "rand_core", + "subtle", +] + +[[package]] +name = "hash32" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "47d60b12902ba28e2730cd37e95b8c9223af2808df9e902d4df49588d1470606" +dependencies = [ + "byteorder", +] + +[[package]] +name = "hashbrown" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" + +[[package]] +name = "hashbrown" +version = "0.15.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9229cfe53dfd69f0609a49f65461bd93001ea1ef889cd5529dd176593f5338a1" +dependencies = [ + "allocator-api2", +] + +[[package]] +name = "hashbrown" +version = "0.17.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed5909b6e89a2db4456e54cd5f673791d7eca6732202bbf2a9cc504fe2f9b84a" + +[[package]] +name = "heapless" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0bfb9eb618601c89945a70e254898da93b13be0388091d42117462b265bb3fad" +dependencies = [ + "hash32", + "stable_deref_trait", +] + +[[package]] +name = "heapless" +version = "0.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "25ba4bd83f9415b58b4ed8dc5714c76e626a105be4646c02630ad730ad3b5aa4" +dependencies = [ + "hash32", + "stable_deref_trait", + "zeroize", +] + +[[package]] +name = "heck" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" + +[[package]] +name = "hex" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" +dependencies = [ + "serde", +] + +[[package]] +name = "hex-literal" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6fe2267d4ed49bc07b63801559be28c718ea06c4738b7a03c94df7386d2cde46" + +[[package]] +name = "hmac" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c49c37c09c17a53d937dfbb742eb3a961d65a994e6bcdcf37e7399d0cc8ab5e" +dependencies = [ + "digest 0.10.7", +] + +[[package]] +name = "hybrid-array" +version = "0.4.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "27f864f10dfb56725ce5ce5472bc52252c8f93a4ab86327122cebf62c5f59a17" +dependencies = [ + "typenum", +] + +[[package]] +name = "iana-time-zone" +version = "0.1.65" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e31bc9ad994ba00e440a8aa5c9ef0ec67d5cb5e5cb0cc7f8b744a35b389cc470" +dependencies = [ + "android_system_properties", + "core-foundation-sys", + "iana-time-zone-haiku", + "js-sys", + "log", + "wasm-bindgen", + "windows-core", +] + +[[package]] +name = "iana-time-zone-haiku" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f" +dependencies = [ + "cc", +] + +[[package]] +name = "ident_case" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" + +[[package]] +name = "indexmap" +version = "1.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bd070e393353796e801d209ad339e89596eb4c8d430d18ede6a1cced8fafbd99" +dependencies = [ + "autocfg", + "hashbrown 0.12.3", + "serde", +] + +[[package]] +name = "indexmap" +version = "2.14.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cc4e190f5d26ca7051642629da2c52fc03bde85a03197c99408dcd291734c855" +dependencies = [ + "equivalent", + "hashbrown 0.17.1", + "serde", + "serde_core", +] + +[[package]] +name = "indexmap-nostd" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e04e2fd2b8188ea827b32ef11de88377086d690286ab35747ef7f9bf3ccb590" + +[[package]] +name = "itertools" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "413ee7dfc52ee1a4949ceeb7dbc8a33f2d6c088194d9f922fb8318faf1f01186" +dependencies = [ + "either", +] + +[[package]] +name = "itoa" +version = "1.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f42a60cbdf9a97f5d2305f08a87dc4e09308d1276d28c869c684d7777685682" + +[[package]] +name = "jiff" +version = "0.2.38" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b2b005715dcbeb0089a3c0dab99f2ff1cc3b2525323552703d648585d342a383" +dependencies = [ + "defmt", + "jiff-core", + "jiff-static", + "jiff-tzdb-platform", + "log", + "portable-atomic", + "portable-atomic-util", + "serde_core", + "windows-link", +] + +[[package]] +name = "jiff-core" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e52fe76043ccecc9005d2305ebaadf7d7fc0cc89ca6baa10a94d6bc68c7128c" +dependencies = [ + "defmt", + "log", +] + +[[package]] +name = "jiff-static" +version = "0.2.38" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2cc9817253cf7c7ee4684451bd327e88d6f3658014e54a29198625590650695c" +dependencies = [ + "jiff-core", + "proc-macro2", + "quote", + "syn 2.0.119", +] + +[[package]] +name = "jiff-tzdb" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fa8377070c6bae868759445e5a77f66d84f0b72f3a054bfb00e6d038b8282da7" + +[[package]] +name = "jiff-tzdb-platform" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "875a5a69ac2bab1a891711cf5eccbec1ce0341ea805560dcd90b7a2e925132e8" +dependencies = [ + "jiff-tzdb", +] + +[[package]] +name = "js-sys" +version = "0.3.106" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7883d941dae510fb2d978fc3fe018c71c9e2892fd38854de3e8b92c2e5ad9cc5" +dependencies = [ + "cfg-if", + "futures-util", + "wasm-bindgen", +] + +[[package]] +name = "k256" +version = "0.13.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f6e3919bbaa2945715f0bb6d3934a173d1e9a59ac23767fbaaef277265a7411b" +dependencies = [ + "cfg-if", + "ecdsa", + "elliptic-curve", + "sha2 0.10.9", +] + +[[package]] +name = "keccak" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cb26cec98cce3a3d96cbb7bced3c4b16e3d13f27ec56dbd62cbc8f39cfb9d653" +dependencies = [ + "cpufeatures 0.2.17", +] + +[[package]] +name = "libc" +version = "0.2.190" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ce5d3ddc6d3fa000eb1536d85e147bfe31aacaba692ed6a876f95cb7c855be78" + +[[package]] +name = "libm" +version = "0.2.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6d2cec3eae94f9f509c767b45932f1ada8350c4bdb85af2fcab4a3c14807981" + +[[package]] +name = "log" +version = "0.4.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f9f8bd3e56ce4dfc153cf470fffbfa98c7620958b312ca5c3a4b8d5181fd13c6" + +[[package]] +name = "macro-string" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b27834086c65ec3f9387b096d66e99f221cf081c2b738042aa252bcd41204e3" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.119", +] + +[[package]] +name = "memchr" +version = "2.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf8baf1c55e62ffcace7a9f06f4bd9cd3f0c4beb022d3b367256b91b87513d98" + +[[package]] +name = "num-bigint" +version = "0.4.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c89e69e7e0f03bea5ef08013795c25018e101932225a656383bd384495ecc367" +dependencies = [ + "num-integer", + "num-traits", +] + +[[package]] +name = "num-conv" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "521739c6d2bac4aa25192232afe6841231376b2b26d4d9fae5ecf8ca5772e441" + +[[package]] +name = "num-derive" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed3955f1a9c7c0c15e092f9c887db08b1fc683305fdf6eb6684f22555355e202" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.119", +] + +[[package]] +name = "num-integer" +version = "0.1.47" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ce2d95d4b3734dc35aa2f45e1aa22cd416814592a4f9d9205e11affd5b8e10b" +dependencies = [ + "num-traits", +] + +[[package]] +name = "num-traits" +version = "0.2.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" +dependencies = [ + "autocfg", +] + +[[package]] +name = "once_cell" +version = "1.21.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9f7c3e4beb33f85d45ae3e3a1792185706c8e16d043238c593331cc7cd313b50" + +[[package]] +name = "p256" +version = "0.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c9863ad85fa8f4460f9c48cb909d38a0d689dba1f6f6988a5e3e0d31071bcd4b" +dependencies = [ + "ecdsa", + "elliptic-curve", + "primeorder", + "sha2 0.10.9", +] + +[[package]] +name = "paste" +version = "1.0.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a" + +[[package]] +name = "pin-project-lite" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a89322df9ebe1c1578d689c92318e070967d1042b512afbe49518723f4e6d5cd" + +[[package]] +name = "pkcs8" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f950b2377845cebe5cf8b5165cb3cc1a5e0fa5cfa3e1f7f55707d8fd82e0a7b7" +dependencies = [ + "der", + "spki", +] + +[[package]] +name = "portable-atomic" +version = "1.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05c8b63e8d9609db387f0324918f81d68fe27748f084ef092fb35954d0539a85" + +[[package]] +name = "portable-atomic-util" +version = "0.2.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "10ab3eb7f3becc3a1cbc4f2c6f20267996cfc1a6467a873763411b136a122715" +dependencies = [ + "portable-atomic", +] + +[[package]] +name = "powerfmt" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4a6394b9e965e73d0a289ee54f589087e2c676aedf60885baf52c76b771e4958" + +[[package]] +name = "ppv-lite86" +version = "0.2.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85eae3c4ed2f50dcfe72643da4befc30deadb458a9b590d720cde2f2b1e97da9" +dependencies = [ + "zerocopy", +] + +[[package]] +name = "prettyplease" +version = "0.2.37" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "479ca8adacdd7ce8f1fb39ce9ecccbfe93a3f1344b3d0d97f20bc0196208f62b" +dependencies = [ + "proc-macro2", + "syn 2.0.119", +] + +[[package]] +name = "primeorder" +version = "0.13.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "353e1ca18966c16d9deb1c69278edbc5f194139612772bd9537af60ac231e1e6" +dependencies = [ + "elliptic-curve", +] + +[[package]] +name = "proc-macro2" +version = "1.0.107" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "985e7ec9bb745e6ce6535b544d84d6cd6f7ad8bd711c398938ae983b91a766d9" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.47" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fbf4db142a473a8d80c26bbf18454ed458bf8d26c8219c331daecfdbd079001" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "rand" +version = "0.8.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e058c7de0b26af77780c769414d6257830bb240f3c38477dbc2c16e5f54d6d4c" +dependencies = [ + "libc", + "rand_chacha", + "rand_core", +] + +[[package]] +name = "rand_chacha" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" +dependencies = [ + "ppv-lite86", + "rand_core", +] + +[[package]] +name = "rand_core" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" +dependencies = [ + "getrandom", +] + +[[package]] +name = "ref-cast" +version = "1.0.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7e440fb4e4b4147295338efb76001ab9e4efc0e5839df2c47fc5ac2381d365c3" +dependencies = [ + "ref-cast-impl", +] + +[[package]] +name = "ref-cast-impl" +version = "1.0.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "92ecd8964f8453721699a1ed72037b0db49ce2f5a5138486ee89bed6f67cdf3a" +dependencies = [ + "proc-macro2", + "quote", + "syn 3.0.9", +] + +[[package]] +name = "rfc6979" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8dd2a808d456c4a54e300a23e9f5a67e122c3024119acbfd73e3bf664491cb2" +dependencies = [ + "hmac", + "subtle", +] + +[[package]] +name = "rustc_version" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cfcb3a22ef46e85b45de6ee7e79d063319ebb6594faafcf1c225ea92ab6e9b92" +dependencies = [ + "semver", +] + +[[package]] +name = "rustversion" +version = "1.0.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf54715a573b99ac80df0bc206da022bcd442c974952c7b9720069370852e21f" + +[[package]] +name = "schemars" +version = "0.8.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3fbf2ae1b8bc8e02df939598064d22402220cd5bbcca1c76f7d6a310974d5615" +dependencies = [ + "dyn-clone", + "serde", + "serde_json", +] + +[[package]] +name = "schemars" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4cd191f9397d57d581cddd31014772520aa448f65ef991055d7f61582c65165f" +dependencies = [ + "dyn-clone", + "ref-cast", + "serde", + "serde_json", +] + +[[package]] +name = "schemars" +version = "1.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ab508826f74a77ca9d5aba6ff19b522583ee3eaf28a19384ff3d0e5835fadf6e" +dependencies = [ + "dyn-clone", + "ref-cast", + "serde", + "serde_json", +] + +[[package]] +name = "sec1" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3e97a565f76233a6003f9f5c54be1d9c5bdfa3eccfb189469f11ec4901c47dc" +dependencies = [ + "base16ct", + "der", + "generic-array", + "subtle", + "zeroize", +] + +[[package]] +name = "semver" +version = "1.0.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a7852d02fc848982e0c167ef163aaff9cd91dc640ba85e263cb1ce46fae51cd" +dependencies = [ + "serde", + "serde_core", +] + +[[package]] +name = "serde" +version = "1.0.229" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4148590afebada386688f18773da617792bf2ef03ffc1e4cbd2b1d45b023e0ba" +dependencies = [ + "serde_core", + "serde_derive", +] + +[[package]] +name = "serde_core" +version = "1.0.229" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67dca2c9c51e58a4791a4b1ed58308b39c64224d349a935ab5039aa360942a48" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.229" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e7a5d71263a5a7d47b41f6b3f06ba276f10cc18b0931f1799f710578e2309348" +dependencies = [ + "proc-macro2", + "quote", + "syn 3.0.9", +] + +[[package]] +name = "serde_json" +version = "1.0.154" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e7e9cc8b1b85264074fbcc02a88680c4096b1e47df8f739dceb03bf482f04bd6" +dependencies = [ + "itoa", + "memchr", + "serde", + "serde_core", + "zmij", +] + +[[package]] +name = "serde_with" +version = "3.24.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df9adc193c780ef8f159aee8b61e2d5801aaa555e6eb0947fe45530ec506296f" +dependencies = [ + "base64 0.23.1", + "bs58", + "chrono", + "hex", + "indexmap 1.9.3", + "indexmap 2.14.2", + "jiff", + "schemars 0.8.22", + "schemars 0.9.0", + "schemars 1.2.3", + "serde_core", + "serde_json", + "serde_with_macros", + "time", +] + +[[package]] +name = "serde_with_macros" +version = "3.24.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3e17bbc68e28663bbbb90df47e058aa7eda4fb445b89fe70457bb94fbccf6e49" +dependencies = [ + "darling 0.24.1", + "proc-macro2", + "quote", + "syn 3.0.9", +] + +[[package]] +name = "sha2" +version = "0.10.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a7507d819769d01a365ab707794a4084392c824f54a7a6a7862f8c3d0892b283" +dependencies = [ + "cfg-if", + "cpufeatures 0.2.17", + "digest 0.10.7", +] + +[[package]] +name = "sha2" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "47d7069beb7d6ac7b9acd1039986e73443f24234f41074da099d6f994ac9ad19" +dependencies = [ + "cfg-if", + "cpufeatures 0.3.1", + "digest 0.11.3", +] + +[[package]] +name = "sha3" +version = "0.10.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77fd7028345d415a4034cf8777cd4f8ab1851274233b45f84e3d955502d93874" +dependencies = [ + "digest 0.10.7", + "keccak", +] + +[[package]] +name = "shlex" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8fadd59c855ef2080decdef8ff161eb6661b86933c9d82e5ba29dc602a55aba" + +[[package]] +name = "signature" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77549399552de45a898a580c1b41d445bf730df867cc44e6c0233bbc4b8329de" +dependencies = [ + "digest 0.10.7", + "rand_core", +] + +[[package]] +name = "slab" +version = "0.4.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c790de23124f9ab44544d7ac05d60440adc586479ce501c1d6d7da3cd8c9cf5" + +[[package]] +name = "smallvec" +version = "1.16.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b3dc8af474f516a851ff4bd12db780f948b9250ad37211e4eec0bccea54e01b" + +[[package]] +name = "soroban-builtin-sdk-macros" +version = "28.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "86ffd329211a50add3965a8f4a8f70e24fc5c8268c945566ab963029b12dce40" +dependencies = [ + "itertools", + "proc-macro2", + "quote", + "syn 2.0.119", +] + +[[package]] +name = "soroban-env-common" +version = "28.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "610bb12ee937a56c2b629f8eb16efb7e47607cba4e79ab0c24132b9a53dd19a0" +dependencies = [ + "arbitrary", + "crate-git-revision 0.0.9", + "ethnum", + "num-derive", + "num-traits", + "serde", + "soroban-env-macros", + "soroban-wasmi", + "static_assertions", + "stellar-xdr 28.0.0", + "wasmparser", +] + +[[package]] +name = "soroban-env-guest" +version = "28.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7d383176b27bb286f718ab0ff5713bd9b01132089151266359bd9b20f38684cf" +dependencies = [ + "soroban-env-common", + "static_assertions", +] + +[[package]] +name = "soroban-env-host" +version = "28.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "36d7e5920c200939164cc58038224e2d90d7e2fabbfa505d6e2129d648dce7e2" +dependencies = [ + "ark-bls12-381", + "ark-bn254", + "ark-ec", + "ark-ff", + "ark-serialize", + "curve25519-dalek 5.0.0", + "ecdsa", + "ed25519-dalek", + "elliptic-curve", + "generic-array", + "getrandom", + "hex-literal", + "hmac", + "k256", + "num-derive", + "num-integer", + "num-traits", + "p256", + "rand", + "rand_chacha", + "sec1", + "sha2 0.10.9", + "sha3", + "soroban-builtin-sdk-macros", + "soroban-env-common", + "soroban-wasmi", + "static_assertions", + "stellar-strkey 0.0.13", + "wasmparser", +] + +[[package]] +name = "soroban-env-macros" +version = "28.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "077600bb175ea54592c1ac5983b8fb34ec9ab7a65f298af11d687b61e9d646a4" +dependencies = [ + "itertools", + "proc-macro2", + "quote", + "serde", + "serde_json", + "stellar-xdr 28.0.0", + "syn 2.0.119", +] + +[[package]] +name = "soroban-ledger-snapshot" +version = "28.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b5caeb0a4c8af6d04dc1dfbf96199e0d50ae086c7d54c9001abe78bb891fc44f" +dependencies = [ + "serde", + "serde_json", + "serde_with", + "soroban-env-common", + "soroban-env-host", + "thiserror 1.0.69", +] + +[[package]] +name = "soroban-sdk" +version = "28.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e4946477e75667215a25b652b0febb446485686c944d364c117c903e3fb98bab" +dependencies = [ + "arbitrary", + "bytes-lit", + "crate-git-revision 0.0.9", + "ctor", + "derive_arbitrary", + "ed25519-dalek", + "rand", + "rustc_version", + "serde", + "serde_json", + "soroban-env-guest", + "soroban-env-host", + "soroban-ledger-snapshot", + "soroban-sdk-macros", + "stellar-strkey 0.0.16", + "visibility", +] + +[[package]] +name = "soroban-sdk-macros" +version = "28.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "168f2d2c3fd4da57584c71e0a8315a8e656486495bc53e18c93b8ec4db085438" +dependencies = [ + "darling 0.20.11", + "heck", + "itertools", + "macro-string", + "proc-macro2", + "quote", + "sha2 0.10.9", + "soroban-env-common", + "soroban-spec", + "soroban-spec-rust", + "stellar-xdr 28.0.0", + "syn 2.0.119", +] + +[[package]] +name = "soroban-spec" +version = "28.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2f48c8e183fc140ccd89e1fe1663cc605fb18641967c0bb591f64887599fde59" +dependencies = [ + "base64 0.22.1", + "sha2 0.10.9", + "stellar-xdr 28.0.0", + "thiserror 1.0.69", + "wasmparser", +] + +[[package]] +name = "soroban-spec-rust" +version = "28.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "40cd0021887fd047e9f63cea36da35b202881bc0e0dbcc067a146137ca46f192" +dependencies = [ + "prettyplease", + "proc-macro2", + "quote", + "sha2 0.10.9", + "soroban-spec", + "stellar-xdr 28.0.0", + "syn 2.0.119", + "thiserror 1.0.69", +] + +[[package]] +name = "soroban-wasmi" +version = "0.31.1-soroban.20.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "710403de32d0e0c35375518cb995d4fc056d0d48966f2e56ea471b8cb8fc9719" +dependencies = [ + "smallvec", + "spin", + "wasmi_arena", + "wasmi_core", + "wasmparser-nostd", +] + +[[package]] +name = "spin" +version = "0.9.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3763264f6b73151db08c50ff20d7d8a0b8796e021cdea7ceedad07b80155fa0e" + +[[package]] +name = "spki" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d91ed6c858b01f942cd56b37a94b3e0a1798290327d1236e4d9cf4eaca44d29d" +dependencies = [ + "base64ct", + "der", +] + +[[package]] +name = "stable_deref_trait" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ce2be8dc25455e1f91df71bfa12ad37d7af1092ae736f3a6cd0e37bc7810596" + +[[package]] +name = "static_assertions" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" + +[[package]] +name = "stellar-build" +version = "0.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97fd5e3be902a9658fb9f642d4eef7837ab1d51e52dde9be1d9b218326499d97" +dependencies = [ + "cargo_metadata", + "sha2 0.10.9", + "thiserror 2.0.21", + "topological-sort", +] + +[[package]] +name = "stellar-registry" +version = "0.1.1" +dependencies = [ + "stellar-registry-macro", +] + +[[package]] +name = "stellar-registry-macro" +version = "0.1.1" +dependencies = [ + "proc-macro2", + "quote", + "sha2 0.11.1", + "soroban-spec", + "stellar-build", + "stellar-registry-name", + "stellar-strkey 0.0.18", + "stellar-xdr 30.0.0", + "syn 2.0.119", +] + +[[package]] +name = "stellar-registry-name" +version = "0.1.0" +dependencies = [ + "semver", + "thiserror 2.0.21", +] + +[[package]] +name = "stellar-strkey" +version = "0.0.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee1832fb50c651ad10f734aaf5d31ca5acdfb197a6ecda64d93fcdb8885af913" +dependencies = [ + "crate-git-revision 0.0.6", + "data-encoding", +] + +[[package]] +name = "stellar-strkey" +version = "0.0.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "084afcb0d458c3d5d5baa2d294b18f881e62cc258ef539d8fdf68be7dbe45520" +dependencies = [ + "crate-git-revision 0.0.6", + "data-encoding", + "heapless 0.8.0", +] + +[[package]] +name = "stellar-strkey" +version = "0.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0f34ff61c0ca6f1c2b4169e8d1633417bd9225f58b6175e4e317204565d16277" +dependencies = [ + "crate-git-revision 0.0.9", + "data-encoding", + "heapless 0.9.3", + "zeroize", +] + +[[package]] +name = "stellar-strkey" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5038ed2bf4e11842210d118f65791d8ca4747622daddd222338b3d06b6051bc1" +dependencies = [ + "crate-git-revision 0.0.9", + "data-encoding", + "heapless 0.9.3", + "zeroize", +] + +[[package]] +name = "stellar-xdr" +version = "28.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f93d09ff8b9f919b084f664003c4c546ac66a76affd5429460dbe29f4b326f8e" +dependencies = [ + "arbitrary", + "base64 0.22.1", + "cfg_eval", + "crate-git-revision 0.0.9", + "escape-bytes", + "ethnum", + "hex", + "serde", + "serde_with", + "sha2 0.10.9", + "stellar-strkey 0.0.13", +] + +[[package]] +name = "stellar-xdr" +version = "30.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56f2fb2008b42fc746c0f6b46e375600068dd91f07748e9f1737c6341a2255f2" +dependencies = [ + "crate-git-revision 0.0.9", + "escape-bytes", + "ethnum", + "hex", + "sha2 0.10.9", + "stellar-strkey 1.0.0", +] + +[[package]] +name = "strsim" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" + +[[package]] +name = "subtle" +version = "2.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" + +[[package]] +name = "syn" +version = "2.0.119" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "872831b642d1a07999a962a351ed35b955ea2cfc8f3862091e2a240a84f17297" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "syn" +version = "3.0.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d78c8dee4c7bf0e14673097256fed6142ce9d3b85a408189d07482442145823b" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "thiserror" +version = "1.0.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6aaf5339b578ea85b50e080feb250a3e8ae8cfcdff9a461c9ec2904bc923f52" +dependencies = [ + "thiserror-impl 1.0.69", +] + +[[package]] +name = "thiserror" +version = "2.0.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09e52cb86a36cede5cb101bf8908837b3e4c6e5e59fe7fd85c23fb56200d189e" +dependencies = [ + "thiserror-impl 2.0.21", +] + +[[package]] +name = "thiserror-impl" +version = "1.0.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.119", +] + +[[package]] +name = "thiserror-impl" +version = "2.0.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fe5197923287db20a58125f0bc85c062f7f2c892de97b18c356f9efb14b28524" +dependencies = [ + "proc-macro2", + "quote", + "syn 3.0.9", +] + +[[package]] +name = "time" +version = "0.3.55" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cdb87b95ec50ddfa440816d227a17b2ccbdda963a316a727fda0fc4334f7d134" +dependencies = [ + "deranged", + "num-conv", + "powerfmt", + "serde_core", + "time-core", + "time-macros", +] + +[[package]] +name = "time-core" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e1c906769ad99c88eaa54e728060edef082f8e358ff32030cb7c7d315e81109" + +[[package]] +name = "time-macros" +version = "0.2.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7e689342a48d2ea927c87ea50cabf8594854bf940e9310208848d680d668ed85" +dependencies = [ + "num-conv", + "time-core", +] + +[[package]] +name = "tinyvec" +version = "1.13.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fd3ca314f692efd6c868f8408f53fe444634a845f96c028b97d35f6a1f79f0ee" + +[[package]] +name = "topological-sort" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ea68304e134ecd095ac6c3574494fc62b909f416c4fca77e440530221e549d3d" + +[[package]] +name = "typenum" +version = "1.20.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6f5e870be6c3b371b77fe0ee0bafb859fa4964b4404c27de1d380043c4dda20" + +[[package]] +name = "unicode-ident" +version = "1.0.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a2c754d6c33795a1c324727428e5a7dedb5b06195f9890bdbcba760d3e246563" + +[[package]] +name = "unverified_caller" +version = "0.0.0" +dependencies = [ + "soroban-sdk", + "stellar-registry", +] + +[[package]] +name = "version_check" +version = "0.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" + +[[package]] +name = "visibility" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d674d135b4a8c1d7e813e2f8d1c9a58308aee4a680323066025e53132218bd91" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.119", +] + +[[package]] +name = "wasi" +version = "0.11.1+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b" + +[[package]] +name = "wasm-bindgen" +version = "0.2.129" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9bb54f33acc68fd454578d9820b0bde1a1a3d17aa17bb7b6595806d02886d409" +dependencies = [ + "cfg-if", + "once_cell", + "rustversion", + "wasm-bindgen-macro", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-macro" +version = "0.2.129" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2e29d0c35b16e224a7eeb5cd2d25e3e1968fbd65604117b44d3b789d00ee8535" +dependencies = [ + "quote", + "wasm-bindgen-macro-support", +] + +[[package]] +name = "wasm-bindgen-macro-support" +version = "0.2.129" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6f501a8bc3719dba86ef8ae4728879c08001bea749eb1333ac5b91e040e2a6b7" +dependencies = [ + "bumpalo", + "proc-macro2", + "quote", + "syn 3.0.9", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-shared" +version = "0.2.129" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "23f0c9c52aa7cd7d77769a4cfe2a9adb1b331f489a41d912ce14513d5ab995c6" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "wasmi_arena" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "104a7f73be44570cac297b3035d76b169d6599637631cf37a1703326a0727073" + +[[package]] +name = "wasmi_core" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dcf1a7db34bff95b85c261002720c00c3a6168256dcb93041d3fa2054d19856a" +dependencies = [ + "downcast-rs", + "libm", + "num-traits", + "paste", +] + +[[package]] +name = "wasmparser" +version = "0.116.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a58e28b80dd8340cb07b8242ae654756161f6fc8d0038123d679b7b99964fa50" +dependencies = [ + "indexmap 2.14.2", + "semver", +] + +[[package]] +name = "wasmparser-nostd" +version = "0.100.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d5a015fe95f3504a94bb1462c717aae75253e39b9dd6c3fb1062c934535c64aa" +dependencies = [ + "indexmap-nostd", +] + +[[package]] +name = "windows-core" +version = "0.62.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8e83a14d34d0623b51dce9581199302a221863196a1dde71a7663a4c2be9deb" +dependencies = [ + "windows-implement", + "windows-interface", + "windows-link", + "windows-result", + "windows-strings", +] + +[[package]] +name = "windows-implement" +version = "0.60.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "053e2e040ab57b9dc951b72c264860db7eb3b0200ba345b4e4c3b14f67855ddf" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.119", +] + +[[package]] +name = "windows-interface" +version = "0.59.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f316c4a2570ba26bbec722032c4099d8c8bc095efccdc15688708623367e358" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.119", +] + +[[package]] +name = "windows-link" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5" + +[[package]] +name = "windows-result" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7781fa89eaf60850ac3d2da7af8e5242a5ea78d1a11c49bf2910bb5a73853eb5" +dependencies = [ + "windows-link", +] + +[[package]] +name = "windows-strings" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7837d08f69c77cf6b07689544538e017c1bfcf57e34b4c0ff58e6c2cd3b37091" +dependencies = [ + "windows-link", +] + +[[package]] +name = "zerocopy" +version = "0.8.62" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "86502bf56ac7c77571a32e2647bb2a15894565e981fb2a48d7bde2d91c965a9d" +dependencies = [ + "zerocopy-derive", +] + +[[package]] +name = "zerocopy-derive" +version = "0.8.62" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5457206954b06561e2608c7e19cf58b1926586d999c246eebe4502f7e2039d1a" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.119", +] + +[[package]] +name = "zeroize" +version = "1.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e13084392c5e4bc371903e2935a5eaeed24905a7511356b883835e18a78f6879" +dependencies = [ + "zeroize_derive", +] + +[[package]] +name = "zeroize_derive" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c50655cbb0fe3fc43170059e702f1ce5e19b84cec58dc87b037a09935c2f328" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.119", +] + +[[package]] +name = "zmij" +version = "1.0.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "29666d0abbfad1e3dc4dcf6144730dd3a3ab225bbbdac83319345b1b44ccfc1b" diff --git a/crates/stellar-registry-test/fixtures/contracts/unverified_caller/Cargo.toml b/crates/stellar-registry-test/fixtures/contracts/unverified_caller/Cargo.toml new file mode 100644 index 0000000..02b6f0f --- /dev/null +++ b/crates/stellar-registry-test/fixtures/contracts/unverified_caller/Cargo.toml @@ -0,0 +1,26 @@ +[package] +name = "unverified_caller" +version = "0.0.0" +edition = "2024" +publish = false + +# Tests `import_contract!` + `register_import!` against a contract that is +# really registered on testnet: the root registry's `unverified` subregistry. +# +# Excluded from the repo workspace: `import_contract!(env, unverified)` only +# resolves on testnet, so a default (`local`) workspace build could not +# compile it. `just test-register-import-testnet` runs it online. +[workspace] + +[lib] +crate-type = ["cdylib"] +doctest = false + +[dependencies] +# Same requirement as the root workspace's soroban-sdk. +soroban-sdk = "28.0.0" +stellar-registry = { path = "../../../../stellar-registry" } + +[dev-dependencies] +soroban-sdk = { version = "28.0.0", features = ["testutils"] } +stellar-registry = { path = "../../../../stellar-registry", features = ["testutils"] } diff --git a/crates/stellar-registry-test/fixtures/contracts/unverified_caller/src/lib.rs b/crates/stellar-registry-test/fixtures/contracts/unverified_caller/src/lib.rs new file mode 100644 index 0000000..03bee61 --- /dev/null +++ b/crates/stellar-registry-test/fixtures/contracts/unverified_caller/src/lib.rs @@ -0,0 +1,41 @@ +#![no_std] +//! Tests a contract that imports a name registered on testnet, the +//! `unverified` subregistry, with `register_import!` and named constructor +//! args. Built with `STELLAR_NETWORK=testnet`, so both macros resolve the +//! name, id and Wasm on testnet. +use soroban_sdk::{Address, Env, contract, contractimpl}; + +#[contract] +pub struct Contract; + +#[contractimpl] +impl Contract { + /// The `unverified` registry's manager, read through `import_contract!`. + pub fn unverified_manager(env: &Env) -> Option
{ + stellar_registry::import_contract!(env, unverified).manager() + } +} + +#[cfg(test)] +mod test { + use super::*; + use soroban_sdk::testutils::Address as _; + use stellar_registry::testutils::register_import; + + #[test] + fn reads_the_imported_registrys_manager() { + let env = Env::default(); + let admin = Address::generate(&env); + let manager = Address::generate(&env); + let root = Address::generate(&env); + // A subregistry, so the constructor doesn't deploy anything. + register_import!( + &env, + unverified, + args = { admin, manager: Some(manager.clone()), root: Some(root) } + ); + + let client = ContractClient::new(&env, &env.register(Contract, ())); + assert_eq!(client.unverified_manager(), Some(manager)); + } +} diff --git a/justfile b/justfile index f0017b6..10dce4f 100644 --- a/justfile +++ b/justfile @@ -62,6 +62,10 @@ test-register-import: echo CBESJIMX7J53SWJGJ7WQ6QTLJI4S5LPPJNC2BNVD63GIKAYCDTDOO322 > $deployed/hello_v1.id STELLAR_NO_REGISTRY=1 STELLAR_NETWORK=local cargo test --manifest-path $fixture/Cargo.toml +# Test the unverified_caller fixture online against testnet, where `unverified` is really registered +test-register-import-testnet: build + STELLAR_NETWORK=testnet cargo test --manifest-path crates/stellar-registry-test/fixtures/contracts/unverified_caller/Cargo.toml + clippy *args: cargo clippy --all {{ args }} \ -- -Dclippy::pedantic -Aclippy::must_use_candidate -Aclippy::missing_errors_doc -Aclippy::missing_panics_doc From 9f64340b4c35f0418f0f8fdee412453227d07092 Mon Sep 17 00:00:00 2001 From: pselle Date: Fri, 16 Oct 2026 11:07:57 -0400 Subject: [PATCH 07/20] test: slim down the testnet fixture Drop its committed Cargo.lock (it only runs manually, online) and trim its comments. Co-Authored-By: Claude --- CLAUDE.md | 2 +- .../contracts/unverified_caller/.gitignore | 1 + .../contracts/unverified_caller/Cargo.lock | 2271 ----------------- .../contracts/unverified_caller/Cargo.toml | 9 +- .../contracts/unverified_caller/src/lib.rs | 21 +- justfile | 2 +- 6 files changed, 13 insertions(+), 2293 deletions(-) create mode 100644 crates/stellar-registry-test/fixtures/contracts/unverified_caller/.gitignore delete mode 100644 crates/stellar-registry-test/fixtures/contracts/unverified_caller/Cargo.lock diff --git a/CLAUDE.md b/CLAUDE.md index fa90ae3..971e887 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -58,7 +58,7 @@ Note: the `justfile` still carries some recipes from the monorepo. Prefer the `c - Unit tests run without external dependencies. - Integration tests require a local Stellar RPC (Docker `stellar/quickstart`) and the `integration-tests` feature flag. - `just test-register-import` runs the `hello_caller` fixture, which is excluded from the workspace because `import_contract!` needs seeded cache files to compile. -- `just test-register-import-testnet` runs the `unverified_caller` fixture online against testnet, where `unverified` is really registered. It needs network access, so CI doesn't run it. +- `just test-register-import-testnet` runs the `unverified_caller` fixture against testnet; it needs network access, so CI doesn't run it. - Command tests use `stellar_registry_test::RegistryTest`, defined in local crate `crates/stellar-registry-test` ## Cross-repo dependencies diff --git a/crates/stellar-registry-test/fixtures/contracts/unverified_caller/.gitignore b/crates/stellar-registry-test/fixtures/contracts/unverified_caller/.gitignore new file mode 100644 index 0000000..03314f7 --- /dev/null +++ b/crates/stellar-registry-test/fixtures/contracts/unverified_caller/.gitignore @@ -0,0 +1 @@ +Cargo.lock diff --git a/crates/stellar-registry-test/fixtures/contracts/unverified_caller/Cargo.lock b/crates/stellar-registry-test/fixtures/contracts/unverified_caller/Cargo.lock deleted file mode 100644 index 77a5a52..0000000 --- a/crates/stellar-registry-test/fixtures/contracts/unverified_caller/Cargo.lock +++ /dev/null @@ -1,2271 +0,0 @@ -# This file is automatically @generated by Cargo. -# It is not intended for manual editing. -version = 4 - -[[package]] -name = "ahash" -version = "0.8.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5a15f179cd60c4584b8a8c596927aadc462e27f2ca70c04e0071964a73ba7a75" -dependencies = [ - "cfg-if", - "once_cell", - "version_check", - "zerocopy", -] - -[[package]] -name = "allocator-api2" -version = "0.2.21" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "683d7910e743518b0e34f1186f92494becacb047c7b6bf616c96772180fef923" - -[[package]] -name = "android_system_properties" -version = "0.1.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ae221649c9976a6f6c56ae1facf410f3ddb33cc661c4b7b61020a912d4237fbc" -dependencies = [ - "libc", -] - -[[package]] -name = "arbitrary" -version = "1.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7d5a26814d8dcb93b0e5a0ff3c6d80a8843bafb21b39e8e18a6f05471870e110" -dependencies = [ - "derive_arbitrary", -] - -[[package]] -name = "ark-bls12-381" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3df4dcc01ff89867cd86b0da835f23c3f02738353aaee7dde7495af71363b8d5" -dependencies = [ - "ark-ec", - "ark-ff", - "ark-serialize", - "ark-std", -] - -[[package]] -name = "ark-bn254" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d69eab57e8d2663efa5c63135b2af4f396d66424f88954c21104125ab6b3e6bc" -dependencies = [ - "ark-ec", - "ark-ff", - "ark-std", -] - -[[package]] -name = "ark-ec" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "43d68f2d516162846c1238e755a7c4d131b892b70cc70c471a8e3ca3ed818fce" -dependencies = [ - "ahash", - "ark-ff", - "ark-poly", - "ark-serialize", - "ark-std", - "educe", - "fnv", - "hashbrown 0.15.5", - "itertools", - "num-bigint", - "num-integer", - "num-traits", - "zeroize", -] - -[[package]] -name = "ark-ff" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a177aba0ed1e0fbb62aa9f6d0502e9b46dad8c2eab04c14258a1212d2557ea70" -dependencies = [ - "ark-ff-asm", - "ark-ff-macros", - "ark-serialize", - "ark-std", - "arrayvec", - "digest 0.10.7", - "educe", - "itertools", - "num-bigint", - "num-traits", - "paste", - "zeroize", -] - -[[package]] -name = "ark-ff-asm" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "62945a2f7e6de02a31fe400aa489f0e0f5b2502e69f95f853adb82a96c7a6b60" -dependencies = [ - "quote", - "syn 2.0.119", -] - -[[package]] -name = "ark-ff-macros" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09be120733ee33f7693ceaa202ca41accd5653b779563608f1234f78ae07c4b3" -dependencies = [ - "num-bigint", - "num-traits", - "proc-macro2", - "quote", - "syn 2.0.119", -] - -[[package]] -name = "ark-poly" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "579305839da207f02b89cd1679e50e67b4331e2f9294a57693e5051b7703fe27" -dependencies = [ - "ahash", - "ark-ff", - "ark-serialize", - "ark-std", - "educe", - "fnv", - "hashbrown 0.15.5", -] - -[[package]] -name = "ark-serialize" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f4d068aaf107ebcd7dfb52bc748f8030e0fc930ac8e360146ca54c1203088f7" -dependencies = [ - "ark-serialize-derive", - "ark-std", - "arrayvec", - "digest 0.10.7", - "num-bigint", -] - -[[package]] -name = "ark-serialize-derive" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "213888f660fddcca0d257e88e54ac05bca01885f258ccdf695bafd77031bb69d" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.119", -] - -[[package]] -name = "ark-std" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "246a225cc6131e9ee4f24619af0f19d67761fff15d7ccc22e42b80846e69449a" -dependencies = [ - "num-traits", - "rand", -] - -[[package]] -name = "arrayvec" -version = "0.7.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d3fb67a6e08acf24fdeccbac2cb6ac4305825bd1f117462e0e6f2f193345ad56" - -[[package]] -name = "autocfg" -version = "1.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f2032f911046de80f0a198e0901378627c33f59ea0ac00e363d481118bd70a53" - -[[package]] -name = "base16ct" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c7f02d4ea65f2c1853089ffd8d2787bdbc63de2f0d29dedbcf8ccdfa0ccd4cf" - -[[package]] -name = "base64" -version = "0.22.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" - -[[package]] -name = "base64" -version = "0.23.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ac07cdecf99051d9a5238b80f35af32cdeba5b336e55d957b318b50137e18da5" - -[[package]] -name = "base64ct" -version = "1.8.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2af50177e190e07a26ab74f8b1efbfe2ef87da2116221318cb1c2e82baf7de06" - -[[package]] -name = "bitflags" -version = "1.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" - -[[package]] -name = "block-buffer" -version = "0.10.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" -dependencies = [ - "generic-array", -] - -[[package]] -name = "block-buffer" -version = "0.12.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d2f6c7dbe95a6ed67ad9f18e57daf93a2f034c524b99fd2b76d18fdfeb6660aa" -dependencies = [ - "hybrid-array", -] - -[[package]] -name = "bs58" -version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bf88ba1141d185c399bee5288d850d63b8369520c1eafc32a0430b5b6c287bf4" -dependencies = [ - "tinyvec", -] - -[[package]] -name = "bumpalo" -version = "3.20.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "72f5acc6cb2ba439de613abc23857ec3d78374d8ed5ac84e9d11336e87da8649" - -[[package]] -name = "byteorder" -version = "1.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" - -[[package]] -name = "bytes-lit" -version = "0.0.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b04f2b1d34cb428043f14aa4c853d14294532e8bbde3b6a3bc2faaaae31a1dd" -dependencies = [ - "num-bigint", - "proc-macro2", - "quote", - "syn 2.0.119", -] - -[[package]] -name = "camino" -version = "1.2.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bbbad30e4b4c14a39e3cc8aed085a12a327257c316619c93581e017bc52be591" -dependencies = [ - "serde_core", -] - -[[package]] -name = "cargo-platform" -version = "0.1.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e35af189006b9c0f00a064685c727031e3ed2d8020f7ba284d78cc2671bd36ea" -dependencies = [ - "serde", -] - -[[package]] -name = "cargo_metadata" -version = "0.18.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2d886547e41f740c616ae73108f6eb70afe6d940c7bc697cb30f13daec073037" -dependencies = [ - "camino", - "cargo-platform", - "semver", - "serde", - "serde_json", - "thiserror 1.0.69", -] - -[[package]] -name = "cc" -version = "1.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6651c9ed80effdc7db0ff72512157f901af5e3549e341e24b1dd4887d836d838" -dependencies = [ - "find-msvc-tools", - "shlex", -] - -[[package]] -name = "cfg-if" -version = "1.0.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4e7648175b45a9a48536d676f68d918270699102aa8dab5496df06904c914600" - -[[package]] -name = "cfg_eval" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "45565fc9416b9896014f5732ac776f810ee53a66730c17e4020c3ec064a8f88f" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.119", -] - -[[package]] -name = "chrono" -version = "0.4.45" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1aa79e62e7697b8e29b513a68abacf485adcd1fe8284a4316c5ae868e6633327" -dependencies = [ - "iana-time-zone", - "num-traits", - "serde", - "windows-link", -] - -[[package]] -name = "const-oid" -version = "0.9.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c2459377285ad874054d797f3ccebf984978aa39129f6eafde5cdc8315b612f8" - -[[package]] -name = "const-oid" -version = "0.10.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a6ef517f0926dd24a1582492c791b6a4818a4d94e789a334894aa15b0d12f55c" - -[[package]] -name = "core-foundation-sys" -version = "0.8.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" - -[[package]] -name = "cpufeatures" -version = "0.2.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "59ed5838eebb26a2bb2e58f6d5b5316989ae9d08bab10e0e6d103e656d1b0280" -dependencies = [ - "libc", -] - -[[package]] -name = "cpufeatures" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5ca28b0ae3115b884660db4118d803791fd6756b6e88f39c0f3f7859060d7566" -dependencies = [ - "libc", -] - -[[package]] -name = "crate-git-revision" -version = "0.0.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c521bf1f43d31ed2f73441775ed31935d77901cb3451e44b38a1c1612fcbaf98" -dependencies = [ - "serde", - "serde_derive", - "serde_json", -] - -[[package]] -name = "crate-git-revision" -version = "0.0.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "54851b5b3f24621804b1cded2820975623c205e3055d2d44031cdb1237339ac8" -dependencies = [ - "serde", - "serde_derive", - "serde_json", -] - -[[package]] -name = "crypto-bigint" -version = "0.5.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0dc92fb57ca44df6db8059111ab3af99a63d5d0f8375d9972e319a379c6bab76" -dependencies = [ - "generic-array", - "rand_core", - "subtle", - "zeroize", -] - -[[package]] -name = "crypto-common" -version = "0.1.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" -dependencies = [ - "generic-array", - "typenum", -] - -[[package]] -name = "crypto-common" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ce6e4c961d6cd6c9a86db418387425e8bdeaf05b3c8bc1411e6dca4c252f1453" -dependencies = [ - "hybrid-array", -] - -[[package]] -name = "ctor" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "67773048316103656a637612c4a62477603b777d91d9c62ff2290f9cde178fdb" -dependencies = [ - "ctor-proc-macro", - "dtor", -] - -[[package]] -name = "ctor-proc-macro" -version = "0.0.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2931af7e13dc045d8e9d26afccc6fa115d64e115c9c84b1166288b46f6782c2" - -[[package]] -name = "curve25519-dalek" -version = "4.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "97fb8b7c4503de7d6ae7b42ab72a5a59857b4c937ec27a3d4539dba95b5ab2be" -dependencies = [ - "cfg-if", - "cpufeatures 0.2.17", - "curve25519-dalek-derive", - "digest 0.10.7", - "fiat-crypto 0.2.9", - "rustc_version", - "subtle", - "zeroize", -] - -[[package]] -name = "curve25519-dalek" -version = "5.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b5eed333089e2e1c1ac8c6c0398e5e2497b4c9926ca6d0365ed1e099afa5bc23" -dependencies = [ - "cfg-if", - "cpufeatures 0.3.1", - "curve25519-dalek-derive", - "digest 0.11.3", - "fiat-crypto 0.3.0", - "rustc_version", - "subtle", -] - -[[package]] -name = "curve25519-dalek-derive" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f46882e17999c6cc590af592290432be3bce0428cb0d5f8b6715e4dc7b383eb3" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.119", -] - -[[package]] -name = "darling" -version = "0.20.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fc7f46116c46ff9ab3eb1597a45688b6715c6e628b5c133e288e709a29bcb4ee" -dependencies = [ - "darling_core 0.20.11", - "darling_macro 0.20.11", -] - -[[package]] -name = "darling" -version = "0.24.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ed17f5901b6630b993ca003def43f2f8ef4014fc13b047b57aad617ff32bc2ec" -dependencies = [ - "darling_core 0.24.1", - "darling_macro 0.24.1", -] - -[[package]] -name = "darling_core" -version = "0.20.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0d00b9596d185e565c2207a0b01f8bd1a135483d02d9b7b0a54b11da8d53412e" -dependencies = [ - "fnv", - "ident_case", - "proc-macro2", - "quote", - "strsim", - "syn 2.0.119", -] - -[[package]] -name = "darling_core" -version = "0.24.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6837e2cf7485aaae18f86181d2f0e9a7ed297a025e220aeabf63fdebd3a2ddff" -dependencies = [ - "ident_case", - "proc-macro2", - "quote", - "strsim", - "syn 3.0.9", -] - -[[package]] -name = "darling_macro" -version = "0.20.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fc34b93ccb385b40dc71c6fceac4b2ad23662c7eeb248cf10d529b7e055b6ead" -dependencies = [ - "darling_core 0.20.11", - "quote", - "syn 2.0.119", -] - -[[package]] -name = "darling_macro" -version = "0.24.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2ac7135c3ef02b2f7833bbeb1be5ba7f966dcde8a87c6b87f65a778d71a02785" -dependencies = [ - "darling_core 0.24.1", - "quote", - "syn 3.0.9", -] - -[[package]] -name = "data-encoding" -version = "2.11.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4583a4551df46e2792f82ceeac45e850d2e2d5debba0b91f102385cda5b11f06" - -[[package]] -name = "defmt" -version = "1.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2953bfe4f93bbd20cc71198842756f77d161884c99ebbabc41d80231ded88d1" -dependencies = [ - "bitflags", - "defmt-macros", -] - -[[package]] -name = "defmt-macros" -version = "1.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bad9c72e7ca2137e0dc3813245a0d282fd6daad32fd800af018306a9169b5fe8" -dependencies = [ - "defmt-parser", - "proc-macro2", - "quote", - "syn 2.0.119", -] - -[[package]] -name = "defmt-parser" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "10d60334b3b2e7c9d91ef8150abfb6fa4c1c39ebbcf4a81c2e346aad939fee3e" -dependencies = [ - "thiserror 2.0.21", -] - -[[package]] -name = "der" -version = "0.7.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e7c1832837b905bbfb5101e07cc24c8deddf52f93225eee6ead5f4d63d53ddcb" -dependencies = [ - "const-oid 0.9.6", - "zeroize", -] - -[[package]] -name = "deranged" -version = "0.5.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9e9de72ce2ad1f90dc62fa25f0f430ef85eb4b0d8fa0be4f30373bc40a21d28e" -dependencies = [ - "serde_core", -] - -[[package]] -name = "derive_arbitrary" -version = "1.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "67e77553c4162a157adbf834ebae5b415acbecbeafc7a74b0e886657506a7611" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.119", -] - -[[package]] -name = "digest" -version = "0.10.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" -dependencies = [ - "block-buffer 0.10.4", - "const-oid 0.9.6", - "crypto-common 0.1.6", - "subtle", -] - -[[package]] -name = "digest" -version = "0.11.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f1dd6dbb5841937940781866fa1281a1ff7bd3bf827091440879f9994983d5c2" -dependencies = [ - "block-buffer 0.12.1", - "const-oid 0.10.2", - "crypto-common 0.2.2", -] - -[[package]] -name = "downcast-rs" -version = "1.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "75b325c5dbd37f80359721ad39aca5a29fb04c89279657cffdda8736d0c0b9d2" - -[[package]] -name = "dtor" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "404d02eeb088a82cfd873006cb713fe411306c7d182c344905e101fb1167d301" -dependencies = [ - "dtor-proc-macro", -] - -[[package]] -name = "dtor-proc-macro" -version = "0.0.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f678cf4a922c215c63e0de95eb1ff08a958a81d47e485cf9da1e27bf6305cfa5" - -[[package]] -name = "dyn-clone" -version = "1.0.20" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d0881ea181b1df73ff77ffaaf9c7544ecc11e82fba9b5f27b262a3c73a332555" - -[[package]] -name = "ecdsa" -version = "0.16.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ee27f32b5c5292967d2d4a9d7f1e0b0aed2c15daded5a60300e4abb9d8020bca" -dependencies = [ - "der", - "digest 0.10.7", - "elliptic-curve", - "rfc6979", - "signature", -] - -[[package]] -name = "ed25519" -version = "2.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "115531babc129696a58c64a4fef0a8bf9e9698629fb97e9e40767d235cfbcd53" -dependencies = [ - "pkcs8", - "signature", -] - -[[package]] -name = "ed25519-dalek" -version = "2.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "70e796c081cee67dc755e1a36a0a172b897fab85fc3f6bc48307991f64e4eca9" -dependencies = [ - "curve25519-dalek 4.1.3", - "ed25519", - "rand_core", - "serde", - "sha2 0.10.9", - "subtle", - "zeroize", -] - -[[package]] -name = "educe" -version = "0.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1d7bc049e1bd8cdeb31b68bbd586a9464ecf9f3944af3958a7a9d0f8b9799417" -dependencies = [ - "enum-ordinalize", - "proc-macro2", - "quote", - "syn 2.0.119", -] - -[[package]] -name = "either" -version = "1.19.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0e9c71c2167ca323c882b99918929403426e2373ea17242ff5653e0d5e1058be" - -[[package]] -name = "elliptic-curve" -version = "0.13.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b5e6043086bf7973472e0c7dff2142ea0b680d30e18d9cc40f267efbf222bd47" -dependencies = [ - "base16ct", - "crypto-bigint", - "digest 0.10.7", - "ff", - "generic-array", - "group", - "rand_core", - "sec1", - "subtle", - "zeroize", -] - -[[package]] -name = "enum-ordinalize" -version = "4.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "89dd01549b09589510cf0647475075d12071456586d70f5c75c98ae2a5537677" -dependencies = [ - "enum-ordinalize-derive", -] - -[[package]] -name = "enum-ordinalize-derive" -version = "4.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a65863d15a4ce2888bd2f0f543cc963d3879c3a022c8ee43f6141d479a3ac815" -dependencies = [ - "proc-macro2", - "quote", - "syn 3.0.9", -] - -[[package]] -name = "equivalent" -version = "1.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" - -[[package]] -name = "escape-bytes" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2bfcf67fea2815c2fc3b90873fae90957be12ff417335dfadc7f52927feb03b2" - -[[package]] -name = "ethnum" -version = "1.5.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "40404c3f5f511ec4da6fe866ddf6a717c309fdbb69fbbad7b0f3edab8f2e835f" - -[[package]] -name = "ff" -version = "0.13.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c0b50bfb653653f9ca9095b427bed08ab8d75a137839d9ad64eb11810d5b6393" -dependencies = [ - "rand_core", - "subtle", -] - -[[package]] -name = "fiat-crypto" -version = "0.2.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "28dea519a9695b9977216879a3ebfddf92f1c08c05d984f8996aecd6ecdc811d" - -[[package]] -name = "fiat-crypto" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "64cd1e32ddd350061ae6edb1b082d7c54915b5c672c389143b9a63403a109f24" - -[[package]] -name = "find-msvc-tools" -version = "0.1.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aedcfb3409746eddb02b9e19ebda1c3394f759a152e48ee875a0844d1b955484" - -[[package]] -name = "fnv" -version = "1.0.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" - -[[package]] -name = "futures-core" -version = "0.3.34" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "92d699e522242e69e3003b94ecc1f960f3a5e015aa7c5d7486e65ad01dd94f5e" - -[[package]] -name = "futures-task" -version = "0.3.34" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cd417de3d1d015fc3bfd2b1ea46dfc7bab72ef86f1cc7cc9c78e728b34a6d1fd" - -[[package]] -name = "futures-util" -version = "0.3.34" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0d50a92467f8ba5dd6e3ee5d4bd04d73ab2e4e1c44474a0674821dfce14b79bc" -dependencies = [ - "futures-core", - "futures-task", - "pin-project-lite", - "slab", -] - -[[package]] -name = "generic-array" -version = "0.14.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4bb6743198531e02858aeaea5398fcc883e71851fcbcb5a2f773e2fb6cb1edf2" -dependencies = [ - "typenum", - "version_check", - "zeroize", -] - -[[package]] -name = "getrandom" -version = "0.2.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ff2abc00be7fca6ebc474524697ae276ad847ad0a6b3faa4bcb027e9a4614ad0" -dependencies = [ - "cfg-if", - "js-sys", - "libc", - "wasi", - "wasm-bindgen", -] - -[[package]] -name = "group" -version = "0.13.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f0f9ef7462f7c099f518d754361858f86d8a07af53ba9af0fe635bbccb151a63" -dependencies = [ - "ff", - "rand_core", - "subtle", -] - -[[package]] -name = "hash32" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "47d60b12902ba28e2730cd37e95b8c9223af2808df9e902d4df49588d1470606" -dependencies = [ - "byteorder", -] - -[[package]] -name = "hashbrown" -version = "0.12.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" - -[[package]] -name = "hashbrown" -version = "0.15.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9229cfe53dfd69f0609a49f65461bd93001ea1ef889cd5529dd176593f5338a1" -dependencies = [ - "allocator-api2", -] - -[[package]] -name = "hashbrown" -version = "0.17.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ed5909b6e89a2db4456e54cd5f673791d7eca6732202bbf2a9cc504fe2f9b84a" - -[[package]] -name = "heapless" -version = "0.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0bfb9eb618601c89945a70e254898da93b13be0388091d42117462b265bb3fad" -dependencies = [ - "hash32", - "stable_deref_trait", -] - -[[package]] -name = "heapless" -version = "0.9.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "25ba4bd83f9415b58b4ed8dc5714c76e626a105be4646c02630ad730ad3b5aa4" -dependencies = [ - "hash32", - "stable_deref_trait", - "zeroize", -] - -[[package]] -name = "heck" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" - -[[package]] -name = "hex" -version = "0.4.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" -dependencies = [ - "serde", -] - -[[package]] -name = "hex-literal" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6fe2267d4ed49bc07b63801559be28c718ea06c4738b7a03c94df7386d2cde46" - -[[package]] -name = "hmac" -version = "0.12.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c49c37c09c17a53d937dfbb742eb3a961d65a994e6bcdcf37e7399d0cc8ab5e" -dependencies = [ - "digest 0.10.7", -] - -[[package]] -name = "hybrid-array" -version = "0.4.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "27f864f10dfb56725ce5ce5472bc52252c8f93a4ab86327122cebf62c5f59a17" -dependencies = [ - "typenum", -] - -[[package]] -name = "iana-time-zone" -version = "0.1.65" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e31bc9ad994ba00e440a8aa5c9ef0ec67d5cb5e5cb0cc7f8b744a35b389cc470" -dependencies = [ - "android_system_properties", - "core-foundation-sys", - "iana-time-zone-haiku", - "js-sys", - "log", - "wasm-bindgen", - "windows-core", -] - -[[package]] -name = "iana-time-zone-haiku" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f" -dependencies = [ - "cc", -] - -[[package]] -name = "ident_case" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" - -[[package]] -name = "indexmap" -version = "1.9.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bd070e393353796e801d209ad339e89596eb4c8d430d18ede6a1cced8fafbd99" -dependencies = [ - "autocfg", - "hashbrown 0.12.3", - "serde", -] - -[[package]] -name = "indexmap" -version = "2.14.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cc4e190f5d26ca7051642629da2c52fc03bde85a03197c99408dcd291734c855" -dependencies = [ - "equivalent", - "hashbrown 0.17.1", - "serde", - "serde_core", -] - -[[package]] -name = "indexmap-nostd" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e04e2fd2b8188ea827b32ef11de88377086d690286ab35747ef7f9bf3ccb590" - -[[package]] -name = "itertools" -version = "0.13.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "413ee7dfc52ee1a4949ceeb7dbc8a33f2d6c088194d9f922fb8318faf1f01186" -dependencies = [ - "either", -] - -[[package]] -name = "itoa" -version = "1.0.18" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f42a60cbdf9a97f5d2305f08a87dc4e09308d1276d28c869c684d7777685682" - -[[package]] -name = "jiff" -version = "0.2.38" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b2b005715dcbeb0089a3c0dab99f2ff1cc3b2525323552703d648585d342a383" -dependencies = [ - "defmt", - "jiff-core", - "jiff-static", - "jiff-tzdb-platform", - "log", - "portable-atomic", - "portable-atomic-util", - "serde_core", - "windows-link", -] - -[[package]] -name = "jiff-core" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5e52fe76043ccecc9005d2305ebaadf7d7fc0cc89ca6baa10a94d6bc68c7128c" -dependencies = [ - "defmt", - "log", -] - -[[package]] -name = "jiff-static" -version = "0.2.38" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2cc9817253cf7c7ee4684451bd327e88d6f3658014e54a29198625590650695c" -dependencies = [ - "jiff-core", - "proc-macro2", - "quote", - "syn 2.0.119", -] - -[[package]] -name = "jiff-tzdb" -version = "0.1.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fa8377070c6bae868759445e5a77f66d84f0b72f3a054bfb00e6d038b8282da7" - -[[package]] -name = "jiff-tzdb-platform" -version = "0.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "875a5a69ac2bab1a891711cf5eccbec1ce0341ea805560dcd90b7a2e925132e8" -dependencies = [ - "jiff-tzdb", -] - -[[package]] -name = "js-sys" -version = "0.3.106" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7883d941dae510fb2d978fc3fe018c71c9e2892fd38854de3e8b92c2e5ad9cc5" -dependencies = [ - "cfg-if", - "futures-util", - "wasm-bindgen", -] - -[[package]] -name = "k256" -version = "0.13.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f6e3919bbaa2945715f0bb6d3934a173d1e9a59ac23767fbaaef277265a7411b" -dependencies = [ - "cfg-if", - "ecdsa", - "elliptic-curve", - "sha2 0.10.9", -] - -[[package]] -name = "keccak" -version = "0.1.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cb26cec98cce3a3d96cbb7bced3c4b16e3d13f27ec56dbd62cbc8f39cfb9d653" -dependencies = [ - "cpufeatures 0.2.17", -] - -[[package]] -name = "libc" -version = "0.2.190" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ce5d3ddc6d3fa000eb1536d85e147bfe31aacaba692ed6a876f95cb7c855be78" - -[[package]] -name = "libm" -version = "0.2.16" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6d2cec3eae94f9f509c767b45932f1ada8350c4bdb85af2fcab4a3c14807981" - -[[package]] -name = "log" -version = "0.4.34" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f9f8bd3e56ce4dfc153cf470fffbfa98c7620958b312ca5c3a4b8d5181fd13c6" - -[[package]] -name = "macro-string" -version = "0.1.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1b27834086c65ec3f9387b096d66e99f221cf081c2b738042aa252bcd41204e3" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.119", -] - -[[package]] -name = "memchr" -version = "2.8.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cf8baf1c55e62ffcace7a9f06f4bd9cd3f0c4beb022d3b367256b91b87513d98" - -[[package]] -name = "num-bigint" -version = "0.4.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c89e69e7e0f03bea5ef08013795c25018e101932225a656383bd384495ecc367" -dependencies = [ - "num-integer", - "num-traits", -] - -[[package]] -name = "num-conv" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "521739c6d2bac4aa25192232afe6841231376b2b26d4d9fae5ecf8ca5772e441" - -[[package]] -name = "num-derive" -version = "0.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ed3955f1a9c7c0c15e092f9c887db08b1fc683305fdf6eb6684f22555355e202" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.119", -] - -[[package]] -name = "num-integer" -version = "0.1.47" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ce2d95d4b3734dc35aa2f45e1aa22cd416814592a4f9d9205e11affd5b8e10b" -dependencies = [ - "num-traits", -] - -[[package]] -name = "num-traits" -version = "0.2.19" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" -dependencies = [ - "autocfg", -] - -[[package]] -name = "once_cell" -version = "1.21.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9f7c3e4beb33f85d45ae3e3a1792185706c8e16d043238c593331cc7cd313b50" - -[[package]] -name = "p256" -version = "0.13.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c9863ad85fa8f4460f9c48cb909d38a0d689dba1f6f6988a5e3e0d31071bcd4b" -dependencies = [ - "ecdsa", - "elliptic-curve", - "primeorder", - "sha2 0.10.9", -] - -[[package]] -name = "paste" -version = "1.0.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a" - -[[package]] -name = "pin-project-lite" -version = "0.2.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a89322df9ebe1c1578d689c92318e070967d1042b512afbe49518723f4e6d5cd" - -[[package]] -name = "pkcs8" -version = "0.10.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f950b2377845cebe5cf8b5165cb3cc1a5e0fa5cfa3e1f7f55707d8fd82e0a7b7" -dependencies = [ - "der", - "spki", -] - -[[package]] -name = "portable-atomic" -version = "1.15.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "05c8b63e8d9609db387f0324918f81d68fe27748f084ef092fb35954d0539a85" - -[[package]] -name = "portable-atomic-util" -version = "0.2.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "10ab3eb7f3becc3a1cbc4f2c6f20267996cfc1a6467a873763411b136a122715" -dependencies = [ - "portable-atomic", -] - -[[package]] -name = "powerfmt" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4a6394b9e965e73d0a289ee54f589087e2c676aedf60885baf52c76b771e4958" - -[[package]] -name = "ppv-lite86" -version = "0.2.21" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "85eae3c4ed2f50dcfe72643da4befc30deadb458a9b590d720cde2f2b1e97da9" -dependencies = [ - "zerocopy", -] - -[[package]] -name = "prettyplease" -version = "0.2.37" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "479ca8adacdd7ce8f1fb39ce9ecccbfe93a3f1344b3d0d97f20bc0196208f62b" -dependencies = [ - "proc-macro2", - "syn 2.0.119", -] - -[[package]] -name = "primeorder" -version = "0.13.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "353e1ca18966c16d9deb1c69278edbc5f194139612772bd9537af60ac231e1e6" -dependencies = [ - "elliptic-curve", -] - -[[package]] -name = "proc-macro2" -version = "1.0.107" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "985e7ec9bb745e6ce6535b544d84d6cd6f7ad8bd711c398938ae983b91a766d9" -dependencies = [ - "unicode-ident", -] - -[[package]] -name = "quote" -version = "1.0.47" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1fbf4db142a473a8d80c26bbf18454ed458bf8d26c8219c331daecfdbd079001" -dependencies = [ - "proc-macro2", -] - -[[package]] -name = "rand" -version = "0.8.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e058c7de0b26af77780c769414d6257830bb240f3c38477dbc2c16e5f54d6d4c" -dependencies = [ - "libc", - "rand_chacha", - "rand_core", -] - -[[package]] -name = "rand_chacha" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" -dependencies = [ - "ppv-lite86", - "rand_core", -] - -[[package]] -name = "rand_core" -version = "0.6.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" -dependencies = [ - "getrandom", -] - -[[package]] -name = "ref-cast" -version = "1.0.27" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7e440fb4e4b4147295338efb76001ab9e4efc0e5839df2c47fc5ac2381d365c3" -dependencies = [ - "ref-cast-impl", -] - -[[package]] -name = "ref-cast-impl" -version = "1.0.27" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "92ecd8964f8453721699a1ed72037b0db49ce2f5a5138486ee89bed6f67cdf3a" -dependencies = [ - "proc-macro2", - "quote", - "syn 3.0.9", -] - -[[package]] -name = "rfc6979" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f8dd2a808d456c4a54e300a23e9f5a67e122c3024119acbfd73e3bf664491cb2" -dependencies = [ - "hmac", - "subtle", -] - -[[package]] -name = "rustc_version" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cfcb3a22ef46e85b45de6ee7e79d063319ebb6594faafcf1c225ea92ab6e9b92" -dependencies = [ - "semver", -] - -[[package]] -name = "rustversion" -version = "1.0.23" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cf54715a573b99ac80df0bc206da022bcd442c974952c7b9720069370852e21f" - -[[package]] -name = "schemars" -version = "0.8.22" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3fbf2ae1b8bc8e02df939598064d22402220cd5bbcca1c76f7d6a310974d5615" -dependencies = [ - "dyn-clone", - "serde", - "serde_json", -] - -[[package]] -name = "schemars" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4cd191f9397d57d581cddd31014772520aa448f65ef991055d7f61582c65165f" -dependencies = [ - "dyn-clone", - "ref-cast", - "serde", - "serde_json", -] - -[[package]] -name = "schemars" -version = "1.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ab508826f74a77ca9d5aba6ff19b522583ee3eaf28a19384ff3d0e5835fadf6e" -dependencies = [ - "dyn-clone", - "ref-cast", - "serde", - "serde_json", -] - -[[package]] -name = "sec1" -version = "0.7.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d3e97a565f76233a6003f9f5c54be1d9c5bdfa3eccfb189469f11ec4901c47dc" -dependencies = [ - "base16ct", - "der", - "generic-array", - "subtle", - "zeroize", -] - -[[package]] -name = "semver" -version = "1.0.28" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a7852d02fc848982e0c167ef163aaff9cd91dc640ba85e263cb1ce46fae51cd" -dependencies = [ - "serde", - "serde_core", -] - -[[package]] -name = "serde" -version = "1.0.229" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4148590afebada386688f18773da617792bf2ef03ffc1e4cbd2b1d45b023e0ba" -dependencies = [ - "serde_core", - "serde_derive", -] - -[[package]] -name = "serde_core" -version = "1.0.229" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "67dca2c9c51e58a4791a4b1ed58308b39c64224d349a935ab5039aa360942a48" -dependencies = [ - "serde_derive", -] - -[[package]] -name = "serde_derive" -version = "1.0.229" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e7a5d71263a5a7d47b41f6b3f06ba276f10cc18b0931f1799f710578e2309348" -dependencies = [ - "proc-macro2", - "quote", - "syn 3.0.9", -] - -[[package]] -name = "serde_json" -version = "1.0.154" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e7e9cc8b1b85264074fbcc02a88680c4096b1e47df8f739dceb03bf482f04bd6" -dependencies = [ - "itoa", - "memchr", - "serde", - "serde_core", - "zmij", -] - -[[package]] -name = "serde_with" -version = "3.24.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "df9adc193c780ef8f159aee8b61e2d5801aaa555e6eb0947fe45530ec506296f" -dependencies = [ - "base64 0.23.1", - "bs58", - "chrono", - "hex", - "indexmap 1.9.3", - "indexmap 2.14.2", - "jiff", - "schemars 0.8.22", - "schemars 0.9.0", - "schemars 1.2.3", - "serde_core", - "serde_json", - "serde_with_macros", - "time", -] - -[[package]] -name = "serde_with_macros" -version = "3.24.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3e17bbc68e28663bbbb90df47e058aa7eda4fb445b89fe70457bb94fbccf6e49" -dependencies = [ - "darling 0.24.1", - "proc-macro2", - "quote", - "syn 3.0.9", -] - -[[package]] -name = "sha2" -version = "0.10.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a7507d819769d01a365ab707794a4084392c824f54a7a6a7862f8c3d0892b283" -dependencies = [ - "cfg-if", - "cpufeatures 0.2.17", - "digest 0.10.7", -] - -[[package]] -name = "sha2" -version = "0.11.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "47d7069beb7d6ac7b9acd1039986e73443f24234f41074da099d6f994ac9ad19" -dependencies = [ - "cfg-if", - "cpufeatures 0.3.1", - "digest 0.11.3", -] - -[[package]] -name = "sha3" -version = "0.10.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "77fd7028345d415a4034cf8777cd4f8ab1851274233b45f84e3d955502d93874" -dependencies = [ - "digest 0.10.7", - "keccak", -] - -[[package]] -name = "shlex" -version = "2.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f8fadd59c855ef2080decdef8ff161eb6661b86933c9d82e5ba29dc602a55aba" - -[[package]] -name = "signature" -version = "2.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "77549399552de45a898a580c1b41d445bf730df867cc44e6c0233bbc4b8329de" -dependencies = [ - "digest 0.10.7", - "rand_core", -] - -[[package]] -name = "slab" -version = "0.4.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0c790de23124f9ab44544d7ac05d60440adc586479ce501c1d6d7da3cd8c9cf5" - -[[package]] -name = "smallvec" -version = "1.16.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b3dc8af474f516a851ff4bd12db780f948b9250ad37211e4eec0bccea54e01b" - -[[package]] -name = "soroban-builtin-sdk-macros" -version = "28.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "86ffd329211a50add3965a8f4a8f70e24fc5c8268c945566ab963029b12dce40" -dependencies = [ - "itertools", - "proc-macro2", - "quote", - "syn 2.0.119", -] - -[[package]] -name = "soroban-env-common" -version = "28.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "610bb12ee937a56c2b629f8eb16efb7e47607cba4e79ab0c24132b9a53dd19a0" -dependencies = [ - "arbitrary", - "crate-git-revision 0.0.9", - "ethnum", - "num-derive", - "num-traits", - "serde", - "soroban-env-macros", - "soroban-wasmi", - "static_assertions", - "stellar-xdr 28.0.0", - "wasmparser", -] - -[[package]] -name = "soroban-env-guest" -version = "28.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7d383176b27bb286f718ab0ff5713bd9b01132089151266359bd9b20f38684cf" -dependencies = [ - "soroban-env-common", - "static_assertions", -] - -[[package]] -name = "soroban-env-host" -version = "28.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "36d7e5920c200939164cc58038224e2d90d7e2fabbfa505d6e2129d648dce7e2" -dependencies = [ - "ark-bls12-381", - "ark-bn254", - "ark-ec", - "ark-ff", - "ark-serialize", - "curve25519-dalek 5.0.0", - "ecdsa", - "ed25519-dalek", - "elliptic-curve", - "generic-array", - "getrandom", - "hex-literal", - "hmac", - "k256", - "num-derive", - "num-integer", - "num-traits", - "p256", - "rand", - "rand_chacha", - "sec1", - "sha2 0.10.9", - "sha3", - "soroban-builtin-sdk-macros", - "soroban-env-common", - "soroban-wasmi", - "static_assertions", - "stellar-strkey 0.0.13", - "wasmparser", -] - -[[package]] -name = "soroban-env-macros" -version = "28.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "077600bb175ea54592c1ac5983b8fb34ec9ab7a65f298af11d687b61e9d646a4" -dependencies = [ - "itertools", - "proc-macro2", - "quote", - "serde", - "serde_json", - "stellar-xdr 28.0.0", - "syn 2.0.119", -] - -[[package]] -name = "soroban-ledger-snapshot" -version = "28.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b5caeb0a4c8af6d04dc1dfbf96199e0d50ae086c7d54c9001abe78bb891fc44f" -dependencies = [ - "serde", - "serde_json", - "serde_with", - "soroban-env-common", - "soroban-env-host", - "thiserror 1.0.69", -] - -[[package]] -name = "soroban-sdk" -version = "28.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e4946477e75667215a25b652b0febb446485686c944d364c117c903e3fb98bab" -dependencies = [ - "arbitrary", - "bytes-lit", - "crate-git-revision 0.0.9", - "ctor", - "derive_arbitrary", - "ed25519-dalek", - "rand", - "rustc_version", - "serde", - "serde_json", - "soroban-env-guest", - "soroban-env-host", - "soroban-ledger-snapshot", - "soroban-sdk-macros", - "stellar-strkey 0.0.16", - "visibility", -] - -[[package]] -name = "soroban-sdk-macros" -version = "28.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "168f2d2c3fd4da57584c71e0a8315a8e656486495bc53e18c93b8ec4db085438" -dependencies = [ - "darling 0.20.11", - "heck", - "itertools", - "macro-string", - "proc-macro2", - "quote", - "sha2 0.10.9", - "soroban-env-common", - "soroban-spec", - "soroban-spec-rust", - "stellar-xdr 28.0.0", - "syn 2.0.119", -] - -[[package]] -name = "soroban-spec" -version = "28.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2f48c8e183fc140ccd89e1fe1663cc605fb18641967c0bb591f64887599fde59" -dependencies = [ - "base64 0.22.1", - "sha2 0.10.9", - "stellar-xdr 28.0.0", - "thiserror 1.0.69", - "wasmparser", -] - -[[package]] -name = "soroban-spec-rust" -version = "28.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "40cd0021887fd047e9f63cea36da35b202881bc0e0dbcc067a146137ca46f192" -dependencies = [ - "prettyplease", - "proc-macro2", - "quote", - "sha2 0.10.9", - "soroban-spec", - "stellar-xdr 28.0.0", - "syn 2.0.119", - "thiserror 1.0.69", -] - -[[package]] -name = "soroban-wasmi" -version = "0.31.1-soroban.20.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "710403de32d0e0c35375518cb995d4fc056d0d48966f2e56ea471b8cb8fc9719" -dependencies = [ - "smallvec", - "spin", - "wasmi_arena", - "wasmi_core", - "wasmparser-nostd", -] - -[[package]] -name = "spin" -version = "0.9.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3763264f6b73151db08c50ff20d7d8a0b8796e021cdea7ceedad07b80155fa0e" - -[[package]] -name = "spki" -version = "0.7.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d91ed6c858b01f942cd56b37a94b3e0a1798290327d1236e4d9cf4eaca44d29d" -dependencies = [ - "base64ct", - "der", -] - -[[package]] -name = "stable_deref_trait" -version = "1.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6ce2be8dc25455e1f91df71bfa12ad37d7af1092ae736f3a6cd0e37bc7810596" - -[[package]] -name = "static_assertions" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" - -[[package]] -name = "stellar-build" -version = "0.0.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "97fd5e3be902a9658fb9f642d4eef7837ab1d51e52dde9be1d9b218326499d97" -dependencies = [ - "cargo_metadata", - "sha2 0.10.9", - "thiserror 2.0.21", - "topological-sort", -] - -[[package]] -name = "stellar-registry" -version = "0.1.1" -dependencies = [ - "stellar-registry-macro", -] - -[[package]] -name = "stellar-registry-macro" -version = "0.1.1" -dependencies = [ - "proc-macro2", - "quote", - "sha2 0.11.1", - "soroban-spec", - "stellar-build", - "stellar-registry-name", - "stellar-strkey 0.0.18", - "stellar-xdr 30.0.0", - "syn 2.0.119", -] - -[[package]] -name = "stellar-registry-name" -version = "0.1.0" -dependencies = [ - "semver", - "thiserror 2.0.21", -] - -[[package]] -name = "stellar-strkey" -version = "0.0.13" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ee1832fb50c651ad10f734aaf5d31ca5acdfb197a6ecda64d93fcdb8885af913" -dependencies = [ - "crate-git-revision 0.0.6", - "data-encoding", -] - -[[package]] -name = "stellar-strkey" -version = "0.0.16" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "084afcb0d458c3d5d5baa2d294b18f881e62cc258ef539d8fdf68be7dbe45520" -dependencies = [ - "crate-git-revision 0.0.6", - "data-encoding", - "heapless 0.8.0", -] - -[[package]] -name = "stellar-strkey" -version = "0.0.18" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0f34ff61c0ca6f1c2b4169e8d1633417bd9225f58b6175e4e317204565d16277" -dependencies = [ - "crate-git-revision 0.0.9", - "data-encoding", - "heapless 0.9.3", - "zeroize", -] - -[[package]] -name = "stellar-strkey" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5038ed2bf4e11842210d118f65791d8ca4747622daddd222338b3d06b6051bc1" -dependencies = [ - "crate-git-revision 0.0.9", - "data-encoding", - "heapless 0.9.3", - "zeroize", -] - -[[package]] -name = "stellar-xdr" -version = "28.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f93d09ff8b9f919b084f664003c4c546ac66a76affd5429460dbe29f4b326f8e" -dependencies = [ - "arbitrary", - "base64 0.22.1", - "cfg_eval", - "crate-git-revision 0.0.9", - "escape-bytes", - "ethnum", - "hex", - "serde", - "serde_with", - "sha2 0.10.9", - "stellar-strkey 0.0.13", -] - -[[package]] -name = "stellar-xdr" -version = "30.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "56f2fb2008b42fc746c0f6b46e375600068dd91f07748e9f1737c6341a2255f2" -dependencies = [ - "crate-git-revision 0.0.9", - "escape-bytes", - "ethnum", - "hex", - "sha2 0.10.9", - "stellar-strkey 1.0.0", -] - -[[package]] -name = "strsim" -version = "0.11.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" - -[[package]] -name = "subtle" -version = "2.6.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" - -[[package]] -name = "syn" -version = "2.0.119" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "872831b642d1a07999a962a351ed35b955ea2cfc8f3862091e2a240a84f17297" -dependencies = [ - "proc-macro2", - "quote", - "unicode-ident", -] - -[[package]] -name = "syn" -version = "3.0.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d78c8dee4c7bf0e14673097256fed6142ce9d3b85a408189d07482442145823b" -dependencies = [ - "proc-macro2", - "quote", - "unicode-ident", -] - -[[package]] -name = "thiserror" -version = "1.0.69" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6aaf5339b578ea85b50e080feb250a3e8ae8cfcdff9a461c9ec2904bc923f52" -dependencies = [ - "thiserror-impl 1.0.69", -] - -[[package]] -name = "thiserror" -version = "2.0.21" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09e52cb86a36cede5cb101bf8908837b3e4c6e5e59fe7fd85c23fb56200d189e" -dependencies = [ - "thiserror-impl 2.0.21", -] - -[[package]] -name = "thiserror-impl" -version = "1.0.69" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.119", -] - -[[package]] -name = "thiserror-impl" -version = "2.0.21" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fe5197923287db20a58125f0bc85c062f7f2c892de97b18c356f9efb14b28524" -dependencies = [ - "proc-macro2", - "quote", - "syn 3.0.9", -] - -[[package]] -name = "time" -version = "0.3.55" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cdb87b95ec50ddfa440816d227a17b2ccbdda963a316a727fda0fc4334f7d134" -dependencies = [ - "deranged", - "num-conv", - "powerfmt", - "serde_core", - "time-core", - "time-macros", -] - -[[package]] -name = "time-core" -version = "0.1.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9e1c906769ad99c88eaa54e728060edef082f8e358ff32030cb7c7d315e81109" - -[[package]] -name = "time-macros" -version = "0.2.32" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7e689342a48d2ea927c87ea50cabf8594854bf940e9310208848d680d668ed85" -dependencies = [ - "num-conv", - "time-core", -] - -[[package]] -name = "tinyvec" -version = "1.13.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fd3ca314f692efd6c868f8408f53fe444634a845f96c028b97d35f6a1f79f0ee" - -[[package]] -name = "topological-sort" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ea68304e134ecd095ac6c3574494fc62b909f416c4fca77e440530221e549d3d" - -[[package]] -name = "typenum" -version = "1.20.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6f5e870be6c3b371b77fe0ee0bafb859fa4964b4404c27de1d380043c4dda20" - -[[package]] -name = "unicode-ident" -version = "1.0.27" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a2c754d6c33795a1c324727428e5a7dedb5b06195f9890bdbcba760d3e246563" - -[[package]] -name = "unverified_caller" -version = "0.0.0" -dependencies = [ - "soroban-sdk", - "stellar-registry", -] - -[[package]] -name = "version_check" -version = "0.9.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" - -[[package]] -name = "visibility" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d674d135b4a8c1d7e813e2f8d1c9a58308aee4a680323066025e53132218bd91" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.119", -] - -[[package]] -name = "wasi" -version = "0.11.1+wasi-snapshot-preview1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b" - -[[package]] -name = "wasm-bindgen" -version = "0.2.129" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9bb54f33acc68fd454578d9820b0bde1a1a3d17aa17bb7b6595806d02886d409" -dependencies = [ - "cfg-if", - "once_cell", - "rustversion", - "wasm-bindgen-macro", - "wasm-bindgen-shared", -] - -[[package]] -name = "wasm-bindgen-macro" -version = "0.2.129" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2e29d0c35b16e224a7eeb5cd2d25e3e1968fbd65604117b44d3b789d00ee8535" -dependencies = [ - "quote", - "wasm-bindgen-macro-support", -] - -[[package]] -name = "wasm-bindgen-macro-support" -version = "0.2.129" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6f501a8bc3719dba86ef8ae4728879c08001bea749eb1333ac5b91e040e2a6b7" -dependencies = [ - "bumpalo", - "proc-macro2", - "quote", - "syn 3.0.9", - "wasm-bindgen-shared", -] - -[[package]] -name = "wasm-bindgen-shared" -version = "0.2.129" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "23f0c9c52aa7cd7d77769a4cfe2a9adb1b331f489a41d912ce14513d5ab995c6" -dependencies = [ - "unicode-ident", -] - -[[package]] -name = "wasmi_arena" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "104a7f73be44570cac297b3035d76b169d6599637631cf37a1703326a0727073" - -[[package]] -name = "wasmi_core" -version = "0.13.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dcf1a7db34bff95b85c261002720c00c3a6168256dcb93041d3fa2054d19856a" -dependencies = [ - "downcast-rs", - "libm", - "num-traits", - "paste", -] - -[[package]] -name = "wasmparser" -version = "0.116.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a58e28b80dd8340cb07b8242ae654756161f6fc8d0038123d679b7b99964fa50" -dependencies = [ - "indexmap 2.14.2", - "semver", -] - -[[package]] -name = "wasmparser-nostd" -version = "0.100.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d5a015fe95f3504a94bb1462c717aae75253e39b9dd6c3fb1062c934535c64aa" -dependencies = [ - "indexmap-nostd", -] - -[[package]] -name = "windows-core" -version = "0.62.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b8e83a14d34d0623b51dce9581199302a221863196a1dde71a7663a4c2be9deb" -dependencies = [ - "windows-implement", - "windows-interface", - "windows-link", - "windows-result", - "windows-strings", -] - -[[package]] -name = "windows-implement" -version = "0.60.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "053e2e040ab57b9dc951b72c264860db7eb3b0200ba345b4e4c3b14f67855ddf" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.119", -] - -[[package]] -name = "windows-interface" -version = "0.59.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f316c4a2570ba26bbec722032c4099d8c8bc095efccdc15688708623367e358" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.119", -] - -[[package]] -name = "windows-link" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5" - -[[package]] -name = "windows-result" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7781fa89eaf60850ac3d2da7af8e5242a5ea78d1a11c49bf2910bb5a73853eb5" -dependencies = [ - "windows-link", -] - -[[package]] -name = "windows-strings" -version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7837d08f69c77cf6b07689544538e017c1bfcf57e34b4c0ff58e6c2cd3b37091" -dependencies = [ - "windows-link", -] - -[[package]] -name = "zerocopy" -version = "0.8.62" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "86502bf56ac7c77571a32e2647bb2a15894565e981fb2a48d7bde2d91c965a9d" -dependencies = [ - "zerocopy-derive", -] - -[[package]] -name = "zerocopy-derive" -version = "0.8.62" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5457206954b06561e2608c7e19cf58b1926586d999c246eebe4502f7e2039d1a" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.119", -] - -[[package]] -name = "zeroize" -version = "1.9.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e13084392c5e4bc371903e2935a5eaeed24905a7511356b883835e18a78f6879" -dependencies = [ - "zeroize_derive", -] - -[[package]] -name = "zeroize_derive" -version = "1.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3c50655cbb0fe3fc43170059e702f1ce5e19b84cec58dc87b037a09935c2f328" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.119", -] - -[[package]] -name = "zmij" -version = "1.0.23" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "29666d0abbfad1e3dc4dcf6144730dd3a3ab225bbbdac83319345b1b44ccfc1b" diff --git a/crates/stellar-registry-test/fixtures/contracts/unverified_caller/Cargo.toml b/crates/stellar-registry-test/fixtures/contracts/unverified_caller/Cargo.toml index 02b6f0f..ca2d47e 100644 --- a/crates/stellar-registry-test/fixtures/contracts/unverified_caller/Cargo.toml +++ b/crates/stellar-registry-test/fixtures/contracts/unverified_caller/Cargo.toml @@ -4,12 +4,8 @@ version = "0.0.0" edition = "2024" publish = false -# Tests `import_contract!` + `register_import!` against a contract that is -# really registered on testnet: the root registry's `unverified` subregistry. -# -# Excluded from the repo workspace: `import_contract!(env, unverified)` only -# resolves on testnet, so a default (`local`) workspace build could not -# compile it. `just test-register-import-testnet` runs it online. +# Imports `unverified`, which only resolves on testnet, so it can't be a +# workspace member. Run with `just test-register-import-testnet`. [workspace] [lib] @@ -17,7 +13,6 @@ crate-type = ["cdylib"] doctest = false [dependencies] -# Same requirement as the root workspace's soroban-sdk. soroban-sdk = "28.0.0" stellar-registry = { path = "../../../../stellar-registry" } diff --git a/crates/stellar-registry-test/fixtures/contracts/unverified_caller/src/lib.rs b/crates/stellar-registry-test/fixtures/contracts/unverified_caller/src/lib.rs index 03bee61..e60f5a3 100644 --- a/crates/stellar-registry-test/fixtures/contracts/unverified_caller/src/lib.rs +++ b/crates/stellar-registry-test/fixtures/contracts/unverified_caller/src/lib.rs @@ -1,8 +1,6 @@ #![no_std] -//! Tests a contract that imports a name registered on testnet, the -//! `unverified` subregistry, with `register_import!` and named constructor -//! args. Built with `STELLAR_NETWORK=testnet`, so both macros resolve the -//! name, id and Wasm on testnet. +//! Imports `unverified`, the testnet registry's subregistry, so both macros +//! fetch its id and Wasm from testnet. use soroban_sdk::{Address, Env, contract, contractimpl}; #[contract] @@ -10,7 +8,6 @@ pub struct Contract; #[contractimpl] impl Contract { - /// The `unverified` registry's manager, read through `import_contract!`. pub fn unverified_manager(env: &Env) -> Option
{ stellar_registry::import_contract!(env, unverified).manager() } @@ -25,15 +22,13 @@ mod test { #[test] fn reads_the_imported_registrys_manager() { let env = Env::default(); - let admin = Address::generate(&env); - let manager = Address::generate(&env); - let root = Address::generate(&env); - // A subregistry, so the constructor doesn't deploy anything. - register_import!( - &env, - unverified, - args = { admin, manager: Some(manager.clone()), root: Some(root) } + let (admin, manager, root) = ( + Address::generate(&env), + Address::generate(&env), + Address::generate(&env), ); + // Passing `root` makes it a subregistry, whose constructor deploys nothing. + register_import!(&env, unverified, args = { admin, manager: Some(manager.clone()), root: Some(root) }); let client = ContractClient::new(&env, &env.register(Contract, ())); assert_eq!(client.unverified_manager(), Some(manager)); diff --git a/justfile b/justfile index 10dce4f..33729cb 100644 --- a/justfile +++ b/justfile @@ -62,7 +62,7 @@ test-register-import: echo CBESJIMX7J53SWJGJ7WQ6QTLJI4S5LPPJNC2BNVD63GIKAYCDTDOO322 > $deployed/hello_v1.id STELLAR_NO_REGISTRY=1 STELLAR_NETWORK=local cargo test --manifest-path $fixture/Cargo.toml -# Test the unverified_caller fixture online against testnet, where `unverified` is really registered +# Test the unverified_caller fixture against testnet test-register-import-testnet: build STELLAR_NETWORK=testnet cargo test --manifest-path crates/stellar-registry-test/fixtures/contracts/unverified_caller/Cargo.toml From 1369dfdd15c45807d693492e8d522bddf6535ae0 Mon Sep 17 00:00:00 2001 From: pselle Date: Fri, 16 Oct 2026 11:09:19 -0400 Subject: [PATCH 08/20] test: point the testnet fixture at unverified/my-token `unverified` is the subregistry itself; `unverified/my-token` is an ordinary registered contract, and its five-argument constructor exercises named args more fully. Co-Authored-By: Claude --- CLAUDE.md | 2 +- Cargo.toml | 2 +- .../.gitignore | 0 .../Cargo.toml | 6 ++-- .../contracts/token_caller/src/lib.rs | 36 +++++++++++++++++++ .../contracts/unverified_caller/src/lib.rs | 36 ------------------- justfile | 4 +-- 7 files changed, 43 insertions(+), 43 deletions(-) rename crates/stellar-registry-test/fixtures/contracts/{unverified_caller => token_caller}/.gitignore (100%) rename crates/stellar-registry-test/fixtures/contracts/{unverified_caller => token_caller}/Cargo.toml (69%) create mode 100644 crates/stellar-registry-test/fixtures/contracts/token_caller/src/lib.rs delete mode 100644 crates/stellar-registry-test/fixtures/contracts/unverified_caller/src/lib.rs diff --git a/CLAUDE.md b/CLAUDE.md index 971e887..da49e36 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -58,7 +58,7 @@ Note: the `justfile` still carries some recipes from the monorepo. Prefer the `c - Unit tests run without external dependencies. - Integration tests require a local Stellar RPC (Docker `stellar/quickstart`) and the `integration-tests` feature flag. - `just test-register-import` runs the `hello_caller` fixture, which is excluded from the workspace because `import_contract!` needs seeded cache files to compile. -- `just test-register-import-testnet` runs the `unverified_caller` fixture against testnet; it needs network access, so CI doesn't run it. +- `just test-register-import-testnet` runs the `token_caller` fixture against testnet; it needs network access, so CI doesn't run it. - Command tests use `stellar_registry_test::RegistryTest`, defined in local crate `crates/stellar-registry-test` ## Cross-repo dependencies diff --git a/Cargo.toml b/Cargo.toml index 84343d6..2a34c5e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -5,7 +5,7 @@ members = ["crates/*", "crates/stellar-registry-test/fixtures/contracts/*"] # Needs seeded `import_contract!` cache files to compile; see its Cargo.toml. exclude = [ "crates/stellar-registry-test/fixtures/contracts/hello_caller", - "crates/stellar-registry-test/fixtures/contracts/unverified_caller", + "crates/stellar-registry-test/fixtures/contracts/token_caller", ] [workspace.package] diff --git a/crates/stellar-registry-test/fixtures/contracts/unverified_caller/.gitignore b/crates/stellar-registry-test/fixtures/contracts/token_caller/.gitignore similarity index 100% rename from crates/stellar-registry-test/fixtures/contracts/unverified_caller/.gitignore rename to crates/stellar-registry-test/fixtures/contracts/token_caller/.gitignore diff --git a/crates/stellar-registry-test/fixtures/contracts/unverified_caller/Cargo.toml b/crates/stellar-registry-test/fixtures/contracts/token_caller/Cargo.toml similarity index 69% rename from crates/stellar-registry-test/fixtures/contracts/unverified_caller/Cargo.toml rename to crates/stellar-registry-test/fixtures/contracts/token_caller/Cargo.toml index ca2d47e..70dfc2b 100644 --- a/crates/stellar-registry-test/fixtures/contracts/unverified_caller/Cargo.toml +++ b/crates/stellar-registry-test/fixtures/contracts/token_caller/Cargo.toml @@ -1,11 +1,11 @@ [package] -name = "unverified_caller" +name = "token_caller" version = "0.0.0" edition = "2024" publish = false -# Imports `unverified`, which only resolves on testnet, so it can't be a -# workspace member. Run with `just test-register-import-testnet`. +# Imports `unverified/my-token`, which only resolves on testnet, so it can't +# be a workspace member. Run with `just test-register-import-testnet`. [workspace] [lib] diff --git a/crates/stellar-registry-test/fixtures/contracts/token_caller/src/lib.rs b/crates/stellar-registry-test/fixtures/contracts/token_caller/src/lib.rs new file mode 100644 index 0000000..acf83ef --- /dev/null +++ b/crates/stellar-registry-test/fixtures/contracts/token_caller/src/lib.rs @@ -0,0 +1,36 @@ +#![no_std] +//! Imports `unverified/my-token`, a token registered on testnet, so both +//! macros fetch its id and Wasm from testnet. +use soroban_sdk::{Env, String, contract, contractimpl}; + +#[contract] +pub struct Contract; + +#[contractimpl] +impl Contract { + pub fn token_symbol(env: &Env) -> String { + stellar_registry::import_contract!(env, "unverified/my-token").symbol() + } +} + +#[cfg(test)] +mod test { + use super::*; + use soroban_sdk::{Address, testutils::Address as _}; + use stellar_registry::testutils::register_import; + + #[test] + fn reads_the_imported_tokens_symbol() { + let env = Env::default(); + register_import!(&env, "unverified/my-token", args = { + name: String::from_str(&env, "Test Token"), + symbol: String::from_str(&env, "TT"), + decimals: 7u32, + owner: Address::generate(&env), + initial_supply: 1_000i128, + }); + + let client = ContractClient::new(&env, &env.register(Contract, ())); + assert_eq!(client.token_symbol(), String::from_str(&env, "TT")); + } +} diff --git a/crates/stellar-registry-test/fixtures/contracts/unverified_caller/src/lib.rs b/crates/stellar-registry-test/fixtures/contracts/unverified_caller/src/lib.rs deleted file mode 100644 index e60f5a3..0000000 --- a/crates/stellar-registry-test/fixtures/contracts/unverified_caller/src/lib.rs +++ /dev/null @@ -1,36 +0,0 @@ -#![no_std] -//! Imports `unverified`, the testnet registry's subregistry, so both macros -//! fetch its id and Wasm from testnet. -use soroban_sdk::{Address, Env, contract, contractimpl}; - -#[contract] -pub struct Contract; - -#[contractimpl] -impl Contract { - pub fn unverified_manager(env: &Env) -> Option
{ - stellar_registry::import_contract!(env, unverified).manager() - } -} - -#[cfg(test)] -mod test { - use super::*; - use soroban_sdk::testutils::Address as _; - use stellar_registry::testutils::register_import; - - #[test] - fn reads_the_imported_registrys_manager() { - let env = Env::default(); - let (admin, manager, root) = ( - Address::generate(&env), - Address::generate(&env), - Address::generate(&env), - ); - // Passing `root` makes it a subregistry, whose constructor deploys nothing. - register_import!(&env, unverified, args = { admin, manager: Some(manager.clone()), root: Some(root) }); - - let client = ContractClient::new(&env, &env.register(Contract, ())); - assert_eq!(client.unverified_manager(), Some(manager)); - } -} diff --git a/justfile b/justfile index 33729cb..3fe32f8 100644 --- a/justfile +++ b/justfile @@ -62,9 +62,9 @@ test-register-import: echo CBESJIMX7J53SWJGJ7WQ6QTLJI4S5LPPJNC2BNVD63GIKAYCDTDOO322 > $deployed/hello_v1.id STELLAR_NO_REGISTRY=1 STELLAR_NETWORK=local cargo test --manifest-path $fixture/Cargo.toml -# Test the unverified_caller fixture against testnet +# Test the token_caller fixture against testnet test-register-import-testnet: build - STELLAR_NETWORK=testnet cargo test --manifest-path crates/stellar-registry-test/fixtures/contracts/unverified_caller/Cargo.toml + STELLAR_NETWORK=testnet cargo test --manifest-path crates/stellar-registry-test/fixtures/contracts/token_caller/Cargo.toml clippy *args: cargo clippy --all {{ args }} \ From 2e49a32254e9c0f9978142670b3b3744afdde651 Mon Sep 17 00:00:00 2001 From: pselle Date: Tue, 3 Nov 2026 04:27:40 -0500 Subject: [PATCH 09/20] docs: drop a stray doc comment on constructor_params Co-Authored-By: Claude --- crates/stellar-registry-macro/src/contract.rs | 3 --- 1 file changed, 3 deletions(-) diff --git a/crates/stellar-registry-macro/src/contract.rs b/crates/stellar-registry-macro/src/contract.rs index 3145aef..de0f8bf 100644 --- a/crates/stellar-registry-macro/src/contract.rs +++ b/crates/stellar-registry-macro/src/contract.rs @@ -508,9 +508,6 @@ pub(crate) fn import_contract( }) } -/// Emit a block expression registering a contract at the address -/// `import_contract!` baked in: the imported Wasm, or `contract` (a native -/// mock) when given. Evaluates to the `Address`. /// The constructor's parameters (name and type), read from the contract spec /// embedded in the Wasm; `None` if the contract has no constructor. fn constructor_params(wasm: &[u8]) -> Result>, String> { From 9cc0e578e66da0582bbd35da80ba0ef389329b97 Mon Sep 17 00:00:00 2001 From: pselle Date: Tue, 3 Nov 2026 04:33:16 -0500 Subject: [PATCH 10/20] feat: add stellar registry clone-contract Copies a registered contract from another network: look the name up on `--from-network` (refusing flagged contracts unless `--force`), fetch the deployed contract's Wasm, deploy it on the target network with the given constructor args, and register it there under the same name or `--as`. This lets a local network hold a real copy of a contract that `import_contract!` can then resolve. Co-Authored-By: Claude --- CLAUDE.md | 2 +- .../src/commands/clone_contract.rs | 210 ++++++++++++++++++ .../stellar-registry-cli/src/commands/mod.rs | 6 + 3 files changed, 217 insertions(+), 1 deletion(-) create mode 100644 crates/stellar-registry-cli/src/commands/clone_contract.rs diff --git a/CLAUDE.md b/CLAUDE.md index 72307f6..fe7576d 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -39,7 +39,7 @@ Note: the `justfile` still carries some recipes from the monorepo. Prefer the `c | Crate | Purpose | |-------|---------| -| `stellar-registry-cli` | The `stellar registry` CLI: `publish`, `deploy`, `download`, `install`/`create-alias`, `upgrade`, `register-contract`, and `account` (`account register`, `account preauthorize`, ...) | +| `stellar-registry-cli` | The `stellar registry` CLI: `publish`, `deploy`, `download`, `install`/`create-alias`, `upgrade`, `register-contract`, `clone-contract` (copy a registered contract from another network), and `account` (`account register`, `account preauthorize`, ...) | | `stellar-registry-build` | Library for interacting with the registry at build time | | `stellar-registry-macro` | Macro crate defining procedural macros `import_contract!`, `import_contract_client!`, and `import_asset`. Proc-macro crates are special and need to only export proc-macros. | | `stellar-registry` | Re-exports the `import_contract!`, `import_contract_client!`, and `import_asset!` macros from `stellar-registry-macro`. Its `testutils` feature adds `testutils::register_import!` for testing contracts that use `import_contract!`. Might export more behavior later. (published to crates.io; dev-dependency of `stellar-registry/contracts`) | diff --git a/crates/stellar-registry-cli/src/commands/clone_contract.rs b/crates/stellar-registry-cli/src/commands/clone_contract.rs new file mode 100644 index 0000000..1cd3d32 --- /dev/null +++ b/crates/stellar-registry-cli/src/commands/clone_contract.rs @@ -0,0 +1,210 @@ +use std::ffi::OsString; + +use clap::Parser; +use stellar_cli::{ + commands::contract::{deploy::wasm, fetch}, + config::{self, UnresolvedContract, network}, +}; +use stellar_registry_build::name::Prefixed; + +use crate::CommandParser; +use crate::commands::{fetch_contract_id, global, register_contract}; + +/// Copy a registered contract from one network to another: look it up by +/// name on `--from-network`, deploy its Wasm on the target network with the +/// given constructor arguments, and register it there under the same name. +#[derive(Parser, Debug, Clone)] +pub struct Cmd { + /// Name of the contract on `--from-network`. Can use prefix if not using + /// verified registry. E.g. `unverified/` + pub contract_name: Prefixed, + + /// Network to copy the contract from, e.g. `testnet` + #[arg(long)] + pub from_network: String, + + /// Name to register the copy under on the target network; defaults to + /// `contract_name` + #[arg(long = "as")] + pub as_name: Option, + + /// Copy the contract even if it is flagged as compromised on + /// `--from-network` + #[arg(long)] + pub force: bool, + + /// Owner of the registration on the target network; defaults to the + /// source account + #[arg(long)] + pub owner: Option, + + /// Arguments for the contract's constructor + #[arg(last = true, id = "CONSTRUCTOR_ARGS")] + pub slop: Vec, + + #[command(flatten)] + pub config: global::Args, +} + +#[derive(thiserror::Error, Debug)] +pub enum Error { + #[error(transparent)] + FetchContractId(#[from] fetch_contract_id::Error), + #[error(transparent)] + Fetch(#[from] fetch::Error), + #[error(transparent)] + Deploy(#[from] wasm::Error), + #[error(transparent)] + RegisterContract(#[from] register_contract::Error), + #[error(transparent)] + Config(#[from] config::Error), + #[error(transparent)] + Io(#[from] std::io::Error), + #[error(transparent)] + Clap(#[from] clap::Error), + #[error("deploying returned a transaction instead of a contract id")] + NoContractId, +} + +impl Cmd { + pub async fn run(&self) -> Result<(), Error> { + let contract_id = self.clone_contract().await?; + println!("{contract_id}"); + Ok(()) + } + + /// The source network's view of the config: same locator, different + /// network. Env-provided RPC settings belong to the target, so they are + /// not carried over. + fn source_config(&self) -> global::Args { + let mut source = self.config.clone(); + source.config.network = network::Args { + network: Some(self.from_network.clone()), + ..Default::default() + }; + source + } + + pub async fn clone_contract(&self) -> Result { + let source = self.source_config(); + + // 1. Resolve the name on the source network, refusing flagged + // contracts unless `--force`. + let source_id = fetch_contract_id::Cmd { + contract_name: self.contract_name.clone(), + force: self.force, + config: source.clone(), + } + .fetch_contract_id() + .await?; + + // 2. Fetch the deployed contract's own Wasm from the source network. + let wasm = fetch::Cmd { + contract_id: Some(UnresolvedContract::Resolved(source_id)), + wasm_hash: None, + out_file: None, + locator: source.locator.clone(), + network: source.network.clone(), + } + .get_bytes() + .await?; + let wasm_path = std::env::temp_dir().join(format!("{source_id}.wasm")); + std::fs::write(&wasm_path, wasm)?; + + // 3. Deploy it on the target network with the constructor arguments. + let wasm_path = wasm_path.to_string_lossy(); + let slop: Vec = self + .slop + .iter() + .map(|a| a.to_string_lossy().into_owned()) + .collect(); + let mut args = vec!["--wasm", wasm_path.as_ref()]; + if !slop.is_empty() { + args.push("--"); + args.extend(slop.iter().map(String::as_str)); + } + let mut deploy = wasm::Cmd::parse_arg_vec(&args)?; + deploy.config = self.config.config.clone(); + let contract_id = deploy + .execute(&deploy.config, false, false) + .await? + .into_result() + .ok_or(Error::NoContractId)?; + + // 4. Register the new contract under the same name. + register_contract::Cmd { + contract_name: self.as_name.clone().unwrap_or(self.contract_name.clone()), + contract_address: format!("{contract_id}"), + owner: self.owner.clone(), + dry_run: false, + config: self.config.clone(), + } + .run() + .await?; + + Ok(contract_id) + } +} + +#[cfg(feature = "integration-tests")] +#[cfg(test)] +mod tests { + use stellar_registry_test::{AssertExt, RegistryTest}; + + #[tokio::test] + async fn copies_a_contract_under_a_new_name() { + let registry = RegistryTest::new().await; + let v1 = registry.hello_wasm_v1(); + + registry + .registry_cli("publish") + .arg("--wasm") + .arg(v1.to_str().unwrap()) + .arg("--binver") + .arg("0.0.1") + .arg("--wasm-name") + .arg("hello") + .assert() + .success(); + registry + .registry_cli("deploy") + .arg("--contract-name") + .arg("hello") + .arg("--wasm-name") + .arg("hello") + .arg("--") + .arg("--admin=alice") + .assert() + .success(); + + // Source and target are both the local network here, so the copy + // needs a different name. + let copied = registry + .registry_cli("clone-contract") + .arg("hello") + .arg("--from-network") + .arg("local") + .arg("--as") + .arg("hello-copy") + .arg("--") + .arg("--admin=alice") + .assert() + .success() + .stdout_as_str(); + + let original = registry + .parse_cmd::(&["hello"]) + .unwrap() + .fetch_contract_id() + .await + .unwrap(); + let fetched = registry + .parse_cmd::(&["hello-copy"]) + .unwrap() + .fetch_contract_id() + .await + .unwrap(); + assert_eq!(copied.trim(), fetched.to_string()); + assert_ne!(original, fetched, "a new deployment, not the same contract"); + } +} diff --git a/crates/stellar-registry-cli/src/commands/mod.rs b/crates/stellar-registry-cli/src/commands/mod.rs index 6a68d4f..8a34c43 100644 --- a/crates/stellar-registry-cli/src/commands/mod.rs +++ b/crates/stellar-registry-cli/src/commands/mod.rs @@ -4,6 +4,7 @@ use clap::{CommandFactory, FromArgMatches, Parser}; pub mod account; pub mod account_cmd; +pub mod clone_contract; pub mod create_alias; pub mod current_version; pub mod deploy; @@ -60,6 +61,7 @@ impl Root { pub async fn run(&mut self) -> Result<(), Error> { match &mut self.cmd { Cmd::Account(cmd) => cmd.run().await?, + Cmd::CloneContract(cmd) => cmd.run().await?, Cmd::CurrentVersion(cmd) => cmd.run().await?, Cmd::Deploy(deploy) => deploy.run().await?, Cmd::DeployUnnamed(cmd) => cmd.run().await?, @@ -93,6 +95,8 @@ pub enum Cmd { /// Name and manage accounts (G-addresses and smart accounts) #[command(subcommand)] Account(account_cmd::Cmd), + /// Copy a registered contract from another network: deploy its Wasm here and register it under the same name + CloneContract(Box), /// Create a local `stellar contract alias` from a named registry contract CreateAlias(Box), /// Get the current (latest) version of a published Wasm @@ -130,6 +134,8 @@ pub enum Error { #[error(transparent)] Account(#[from] account_cmd::Error), #[error(transparent)] + CloneContract(#[from] clone_contract::Error), + #[error(transparent)] CreateAlias(#[from] create_alias::Error), #[error(transparent)] CurrentVersion(#[from] current_version::Error), From 380f17164a0322adedc9efcabed670cae4be7559 Mon Sep 17 00:00:00 2001 From: pselle Date: Tue, 3 Nov 2026 04:48:33 -0500 Subject: [PATCH 11/20] fix: look up clone-contract's source on the canonical registry `STELLAR_REGISTRY_CONTRACT_ID` points at one registry deployment, usually a local one, so applying it to the source network made a testnet-to-local copy look the name up at the local registry's id on testnet. Use the source network's canonical root registry unless source and target are the same network. Co-Authored-By: Claude --- crates/stellar-registry-build/src/registry.rs | 20 ++++++++- .../src/commands/clone_contract.rs | 43 ++++++++++++++----- 2 files changed, 51 insertions(+), 12 deletions(-) diff --git a/crates/stellar-registry-build/src/registry.rs b/crates/stellar-registry-build/src/registry.rs index 216704c..de3861b 100644 --- a/crates/stellar-registry-build/src/registry.rs +++ b/crates/stellar-registry-build/src/registry.rs @@ -17,7 +17,25 @@ impl Registry { } pub async fn new(config: &config::Args, name: Option<&str>) -> Result { - let contract = Self::verified(config)?; + Self::from_root(Self::verified(config)?, config, name).await + } + + /// Like [`Self::new`], but always from the network's canonical root + /// registry, ignoring `STELLAR_REGISTRY_CONTRACT_ID`: that override names + /// one deployment (usually a local one), not a registry on every network. + pub async fn new_canonical(config: &config::Args, name: Option<&str>) -> Result { + let root = Registry(Contract::new( + verified_contract_id(&config.get_network()?.network_passphrase), + config, + )); + Self::from_root(root, config, name).await + } + + async fn from_root( + contract: Self, + config: &config::Args, + name: Option<&str>, + ) -> Result { Ok(if let Some(name) = name { if let Ok(contract_id) = name.parse() { Registry(Contract::new(contract_id, config)) diff --git a/crates/stellar-registry-cli/src/commands/clone_contract.rs b/crates/stellar-registry-cli/src/commands/clone_contract.rs index 1cd3d32..94dabe4 100644 --- a/crates/stellar-registry-cli/src/commands/clone_contract.rs +++ b/crates/stellar-registry-cli/src/commands/clone_contract.rs @@ -5,10 +5,10 @@ use stellar_cli::{ commands::contract::{deploy::wasm, fetch}, config::{self, UnresolvedContract, network}, }; -use stellar_registry_build::name::Prefixed; +use stellar_registry_build::{name::Prefixed, registry::Registry}; use crate::CommandParser; -use crate::commands::{fetch_contract_id, global, register_contract}; +use crate::commands::{global, register_contract}; /// Copy a registered contract from one network to another: look it up by /// name on `--from-network`, deploy its Wasm on the target network with the @@ -49,7 +49,11 @@ pub struct Cmd { #[derive(thiserror::Error, Debug)] pub enum Error { #[error(transparent)] - FetchContractId(#[from] fetch_contract_id::Error), + Registry(#[from] stellar_registry_build::Error), + #[error( + "contract `{0}` is flagged as compromised on the source network; pass --force to copy it anyway" + )] + ContractFlagged(String), #[error(transparent)] Fetch(#[from] fetch::Error), #[error(transparent)] @@ -89,14 +93,31 @@ impl Cmd { let source = self.source_config(); // 1. Resolve the name on the source network, refusing flagged - // contracts unless `--force`. - let source_id = fetch_contract_id::Cmd { - contract_name: self.contract_name.clone(), - force: self.force, - config: source.clone(), - } - .fetch_contract_id() - .await?; + // contracts unless `--force`. `STELLAR_REGISTRY_CONTRACT_ID` points + // at the target's registry, so it only applies to the source when + // both are the same network. + let same_network = source.get_network()?.network_passphrase + == self.config.get_network()?.network_passphrase; + let channel = self.contract_name.channel(); + let registry = if same_network { + Registry::new(&source, channel).await? + } else { + Registry::new_canonical(&source, channel).await? + }; + let name = self.contract_name.name(); + let source_id = if self.force { + registry.fetch_contract_id_unchecked(name).await? + } else { + registry + .fetch_contract_id(name) + .await + .map_err(|e| match e { + stellar_registry_build::Error::ContractFlagged(_) => { + Error::ContractFlagged(self.contract_name.to_string()) + } + other => other.into(), + })? + }; // 2. Fetch the deployed contract's own Wasm from the source network. let wasm = fetch::Cmd { From cc009b487c6d99ff6049822917398e210a467729 Mon Sep 17 00:00:00 2001 From: pselle Date: Tue, 3 Nov 2026 04:58:29 -0500 Subject: [PATCH 12/20] refactor: fold clone-contract into deploy --from-network `stellar registry deploy --contract-name --from-network testnet -- ` copies a contract registered on another network: it fetches the deployed Wasm there, deploys it here from the source account (not through the registry's `deploy`) and registers it as `--contract-name`. `--from-contract-name` names it on the source network when different; `--force` copies a flagged contract. This replaces the separate `clone-contract` command. Co-Authored-By: Claude --- CLAUDE.md | 4 +- .../src/commands/clone_contract.rs | 231 ------------------ .../src/commands/deploy.rs | 86 ++++++- .../stellar-registry-cli/src/commands/mod.rs | 8 +- 4 files changed, 82 insertions(+), 247 deletions(-) delete mode 100644 crates/stellar-registry-cli/src/commands/clone_contract.rs diff --git a/CLAUDE.md b/CLAUDE.md index fe7576d..196cfd8 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -39,7 +39,7 @@ Note: the `justfile` still carries some recipes from the monorepo. Prefer the `c | Crate | Purpose | |-------|---------| -| `stellar-registry-cli` | The `stellar registry` CLI: `publish`, `deploy`, `download`, `install`/`create-alias`, `upgrade`, `register-contract`, `clone-contract` (copy a registered contract from another network), and `account` (`account register`, `account preauthorize`, ...) | +| `stellar-registry-cli` | The `stellar registry` CLI: `publish`, `deploy`, `download`, `install`/`create-alias`, `upgrade`, `register-contract`, and `account` (`account register`, `account preauthorize`, ...) | | `stellar-registry-build` | Library for interacting with the registry at build time | | `stellar-registry-macro` | Macro crate defining procedural macros `import_contract!`, `import_contract_client!`, and `import_asset`. Proc-macro crates are special and need to only export proc-macros. | | `stellar-registry` | Re-exports the `import_contract!`, `import_contract_client!`, and `import_asset!` macros from `stellar-registry-macro`. Its `testutils` feature adds `testutils::register_import!` for testing contracts that use `import_contract!`. Might export more behavior later. (published to crates.io; dev-dependency of `stellar-registry/contracts`) | @@ -50,7 +50,7 @@ Note: the `justfile` still carries some recipes from the monorepo. Prefer the `c `publish` → `deploy` → `install` / `create-alias` - `publish` — uploads wasm to the registry with semantic versioning -- `deploy` — instantiates a published wasm as a named contract +- `deploy` — instantiates a published wasm as a named contract, or with `--from-network` copies a contract registered on another network - `install` / `create-alias` — creates a local stellar-cli alias from the registry ## Testing diff --git a/crates/stellar-registry-cli/src/commands/clone_contract.rs b/crates/stellar-registry-cli/src/commands/clone_contract.rs deleted file mode 100644 index 94dabe4..0000000 --- a/crates/stellar-registry-cli/src/commands/clone_contract.rs +++ /dev/null @@ -1,231 +0,0 @@ -use std::ffi::OsString; - -use clap::Parser; -use stellar_cli::{ - commands::contract::{deploy::wasm, fetch}, - config::{self, UnresolvedContract, network}, -}; -use stellar_registry_build::{name::Prefixed, registry::Registry}; - -use crate::CommandParser; -use crate::commands::{global, register_contract}; - -/// Copy a registered contract from one network to another: look it up by -/// name on `--from-network`, deploy its Wasm on the target network with the -/// given constructor arguments, and register it there under the same name. -#[derive(Parser, Debug, Clone)] -pub struct Cmd { - /// Name of the contract on `--from-network`. Can use prefix if not using - /// verified registry. E.g. `unverified/` - pub contract_name: Prefixed, - - /// Network to copy the contract from, e.g. `testnet` - #[arg(long)] - pub from_network: String, - - /// Name to register the copy under on the target network; defaults to - /// `contract_name` - #[arg(long = "as")] - pub as_name: Option, - - /// Copy the contract even if it is flagged as compromised on - /// `--from-network` - #[arg(long)] - pub force: bool, - - /// Owner of the registration on the target network; defaults to the - /// source account - #[arg(long)] - pub owner: Option, - - /// Arguments for the contract's constructor - #[arg(last = true, id = "CONSTRUCTOR_ARGS")] - pub slop: Vec, - - #[command(flatten)] - pub config: global::Args, -} - -#[derive(thiserror::Error, Debug)] -pub enum Error { - #[error(transparent)] - Registry(#[from] stellar_registry_build::Error), - #[error( - "contract `{0}` is flagged as compromised on the source network; pass --force to copy it anyway" - )] - ContractFlagged(String), - #[error(transparent)] - Fetch(#[from] fetch::Error), - #[error(transparent)] - Deploy(#[from] wasm::Error), - #[error(transparent)] - RegisterContract(#[from] register_contract::Error), - #[error(transparent)] - Config(#[from] config::Error), - #[error(transparent)] - Io(#[from] std::io::Error), - #[error(transparent)] - Clap(#[from] clap::Error), - #[error("deploying returned a transaction instead of a contract id")] - NoContractId, -} - -impl Cmd { - pub async fn run(&self) -> Result<(), Error> { - let contract_id = self.clone_contract().await?; - println!("{contract_id}"); - Ok(()) - } - - /// The source network's view of the config: same locator, different - /// network. Env-provided RPC settings belong to the target, so they are - /// not carried over. - fn source_config(&self) -> global::Args { - let mut source = self.config.clone(); - source.config.network = network::Args { - network: Some(self.from_network.clone()), - ..Default::default() - }; - source - } - - pub async fn clone_contract(&self) -> Result { - let source = self.source_config(); - - // 1. Resolve the name on the source network, refusing flagged - // contracts unless `--force`. `STELLAR_REGISTRY_CONTRACT_ID` points - // at the target's registry, so it only applies to the source when - // both are the same network. - let same_network = source.get_network()?.network_passphrase - == self.config.get_network()?.network_passphrase; - let channel = self.contract_name.channel(); - let registry = if same_network { - Registry::new(&source, channel).await? - } else { - Registry::new_canonical(&source, channel).await? - }; - let name = self.contract_name.name(); - let source_id = if self.force { - registry.fetch_contract_id_unchecked(name).await? - } else { - registry - .fetch_contract_id(name) - .await - .map_err(|e| match e { - stellar_registry_build::Error::ContractFlagged(_) => { - Error::ContractFlagged(self.contract_name.to_string()) - } - other => other.into(), - })? - }; - - // 2. Fetch the deployed contract's own Wasm from the source network. - let wasm = fetch::Cmd { - contract_id: Some(UnresolvedContract::Resolved(source_id)), - wasm_hash: None, - out_file: None, - locator: source.locator.clone(), - network: source.network.clone(), - } - .get_bytes() - .await?; - let wasm_path = std::env::temp_dir().join(format!("{source_id}.wasm")); - std::fs::write(&wasm_path, wasm)?; - - // 3. Deploy it on the target network with the constructor arguments. - let wasm_path = wasm_path.to_string_lossy(); - let slop: Vec = self - .slop - .iter() - .map(|a| a.to_string_lossy().into_owned()) - .collect(); - let mut args = vec!["--wasm", wasm_path.as_ref()]; - if !slop.is_empty() { - args.push("--"); - args.extend(slop.iter().map(String::as_str)); - } - let mut deploy = wasm::Cmd::parse_arg_vec(&args)?; - deploy.config = self.config.config.clone(); - let contract_id = deploy - .execute(&deploy.config, false, false) - .await? - .into_result() - .ok_or(Error::NoContractId)?; - - // 4. Register the new contract under the same name. - register_contract::Cmd { - contract_name: self.as_name.clone().unwrap_or(self.contract_name.clone()), - contract_address: format!("{contract_id}"), - owner: self.owner.clone(), - dry_run: false, - config: self.config.clone(), - } - .run() - .await?; - - Ok(contract_id) - } -} - -#[cfg(feature = "integration-tests")] -#[cfg(test)] -mod tests { - use stellar_registry_test::{AssertExt, RegistryTest}; - - #[tokio::test] - async fn copies_a_contract_under_a_new_name() { - let registry = RegistryTest::new().await; - let v1 = registry.hello_wasm_v1(); - - registry - .registry_cli("publish") - .arg("--wasm") - .arg(v1.to_str().unwrap()) - .arg("--binver") - .arg("0.0.1") - .arg("--wasm-name") - .arg("hello") - .assert() - .success(); - registry - .registry_cli("deploy") - .arg("--contract-name") - .arg("hello") - .arg("--wasm-name") - .arg("hello") - .arg("--") - .arg("--admin=alice") - .assert() - .success(); - - // Source and target are both the local network here, so the copy - // needs a different name. - let copied = registry - .registry_cli("clone-contract") - .arg("hello") - .arg("--from-network") - .arg("local") - .arg("--as") - .arg("hello-copy") - .arg("--") - .arg("--admin=alice") - .assert() - .success() - .stdout_as_str(); - - let original = registry - .parse_cmd::(&["hello"]) - .unwrap() - .fetch_contract_id() - .await - .unwrap(); - let fetched = registry - .parse_cmd::(&["hello-copy"]) - .unwrap() - .fetch_contract_id() - .await - .unwrap(); - assert_eq!(copied.trim(), fetched.to_string()); - assert_ne!(original, fetched, "a new deployment, not the same contract"); - } -} diff --git a/crates/stellar-registry-cli/src/commands/deploy.rs b/crates/stellar-registry-cli/src/commands/deploy.rs index 78c24b7..f2a4cae 100644 --- a/crates/stellar-registry-cli/src/commands/deploy.rs +++ b/crates/stellar-registry-cli/src/commands/deploy.rs @@ -19,6 +19,7 @@ use stellar_registry_build::{ use crate::commands::global; +pub mod from_network; pub mod util; #[derive(Parser, Debug, Clone)] @@ -29,8 +30,21 @@ pub struct Cmd { pub contract_name: Prefixed, /// Name of published contract to deploy from. Can use prefix of not using verified registry. /// E.g. `unverified/` - #[arg(long)] - pub wasm_name: Prefixed, + #[arg(long, required_unless_present = "from_network")] + pub wasm_name: Option, + /// Copy a contract registered on another network (e.g. `testnet`) instead + /// of deploying a published Wasm: its Wasm is deployed here from your + /// account, not through the registry, then registered as `--contract-name` + #[arg(long, conflicts_with_all = ["wasm_name", "version", "deployer"])] + pub from_network: Option, + /// Name of the contract on `--from-network`, if different from + /// `--contract-name` + #[arg(long, requires = "from_network")] + pub from_contract_name: Option, + /// With `--from-network`, copy the contract even if it is flagged as + /// compromised there + #[arg(long, requires = "from_network")] + pub force: bool, /// Arguments for constructor #[arg(last = true, id = "CONSTRUCTOR_ARGS")] pub slop: Vec, @@ -85,6 +99,8 @@ pub enum Error { InvalidReturnValue(String), #[error(transparent)] Registry(#[from] stellar_registry_build::Error), + #[error(transparent)] + FromNetwork(#[from] from_network::Error), #[error( "simulated fee of ~{} XLM is too large for a transaction; the call is likely paying unexpected storage rent", .0 / 10_000_000 @@ -94,7 +110,13 @@ pub enum Error { impl Cmd { pub async fn run(&self) -> Result<(), Error> { - match self.invoke().await { + let result = match &self.from_network { + Some(from_network) => from_network::deploy(self, from_network) + .await + .map_err(Error::from), + None => self.invoke().await, + }; + match result { Ok(contract_id) => { println!( "Contract {} deployed successfully to {contract_id}", @@ -110,10 +132,20 @@ impl Cmd { } } + /// `--wasm-name`, which clap requires unless `--from-network` is given. + fn wasm_name(&self) -> &Prefixed { + self.wasm_name + .as_ref() + .expect("--wasm-name is required without --from-network") + } + pub async fn hash(&self, registry: &Registry) -> Result { let res = registry .as_contract() - .invoke_with_result(&["fetch_hash", "--wasm_name", self.wasm_name.name()], true) + .invoke_with_result( + &["fetch_hash", "--wasm_name", self.wasm_name().name()], + true, + ) .await?; let res = res.trim_matches('"'); Ok(res.parse().unwrap()) @@ -134,7 +166,7 @@ impl Cmd { async fn invoke(&self) -> Result { let target_registry = self.contract_name.registry(&self.config).await?; - let wasm_registry = self.wasm_name.registry(&self.config).await?; + let wasm_registry = self.wasm_name().registry(&self.config).await?; let cross_registry = target_registry.as_contract().id() != wasm_registry.as_contract().id(); let client = self.config.rpc_client()?; let key = self.config.key_pair()?; @@ -151,7 +183,7 @@ impl Cmd { None }; let mut call_args: Vec = vec![ - ScVal::String(ScString(self.wasm_name.name().try_into().unwrap())), + ScVal::String(ScString(self.wasm_name().name().try_into().unwrap())), self.version.clone().map_or(ScVal::Void, |s| { ScVal::String(ScString(s.try_into().unwrap())) }), @@ -169,7 +201,7 @@ impl Cmd { // the trusted root pinned at construction, so we pass the name // rather than an address. Root has no prefix — its own name in // root storage is "registry". - let subregistry_name = self.wasm_name.channel().unwrap_or("registry"); + let subregistry_name = self.wasm_name().channel().unwrap_or("registry"); call_args.push(ScVal::String(ScString( subregistry_name.try_into().unwrap(), ))); @@ -318,4 +350,44 @@ mod tests { .assert() .failure(); } + + #[tokio::test] + async fn from_network_copies_a_registered_contract() { + let registry = RegistryTest::new().await; + publish(®istry, "hello", "0.0.1"); + registry + .registry_cli("deploy") + .arg("--contract-name") + .arg("hello") + .arg("--wasm-name") + .arg("hello") + .arg("--") + .arg("--admin=alice") + .assert() + .success(); + + // Source and target are both the local network here, so the copy + // needs a different name. + registry + .registry_cli("deploy") + .arg("--contract-name") + .arg("hello-copy") + .arg("--from-network") + .arg("local") + .arg("--from-contract-name") + .arg("hello") + .arg("--") + .arg("--admin=alice") + .assert() + .success(); + + let fetch = |name: &str| { + registry + .parse_cmd::(&[name]) + .unwrap() + }; + let original = fetch("hello").fetch_contract_id().await.unwrap(); + let copy = fetch("hello-copy").fetch_contract_id().await.unwrap(); + assert_ne!(original, copy, "a new deployment, not the same contract"); + } } diff --git a/crates/stellar-registry-cli/src/commands/mod.rs b/crates/stellar-registry-cli/src/commands/mod.rs index 8a34c43..c5cf722 100644 --- a/crates/stellar-registry-cli/src/commands/mod.rs +++ b/crates/stellar-registry-cli/src/commands/mod.rs @@ -4,7 +4,6 @@ use clap::{CommandFactory, FromArgMatches, Parser}; pub mod account; pub mod account_cmd; -pub mod clone_contract; pub mod create_alias; pub mod current_version; pub mod deploy; @@ -61,7 +60,6 @@ impl Root { pub async fn run(&mut self) -> Result<(), Error> { match &mut self.cmd { Cmd::Account(cmd) => cmd.run().await?, - Cmd::CloneContract(cmd) => cmd.run().await?, Cmd::CurrentVersion(cmd) => cmd.run().await?, Cmd::Deploy(deploy) => deploy.run().await?, Cmd::DeployUnnamed(cmd) => cmd.run().await?, @@ -95,13 +93,11 @@ pub enum Cmd { /// Name and manage accounts (G-addresses and smart accounts) #[command(subcommand)] Account(account_cmd::Cmd), - /// Copy a registered contract from another network: deploy its Wasm here and register it under the same name - CloneContract(Box), /// Create a local `stellar contract alias` from a named registry contract CreateAlias(Box), /// Get the current (latest) version of a published Wasm CurrentVersion(Box), - /// Deploy a named contract from a published Wasm + /// Deploy a named contract from a published Wasm, or copy one from another network Deploy(Box), /// Deploy a contract from a published Wasm without registering a name DeployUnnamed(Box), @@ -134,8 +130,6 @@ pub enum Error { #[error(transparent)] Account(#[from] account_cmd::Error), #[error(transparent)] - CloneContract(#[from] clone_contract::Error), - #[error(transparent)] CreateAlias(#[from] create_alias::Error), #[error(transparent)] CurrentVersion(#[from] current_version::Error), From 7edea53cb7a6e07d900450d4561c52e81b35b081 Mon Sep 17 00:00:00 2001 From: pselle Date: Tue, 3 Nov 2026 04:58:38 -0500 Subject: [PATCH 13/20] fix: add the deploy from_network module missed from the last commit Co-Authored-By: Claude --- .../src/commands/deploy/from_network.rs | 129 ++++++++++++++++++ 1 file changed, 129 insertions(+) create mode 100644 crates/stellar-registry-cli/src/commands/deploy/from_network.rs diff --git a/crates/stellar-registry-cli/src/commands/deploy/from_network.rs b/crates/stellar-registry-cli/src/commands/deploy/from_network.rs new file mode 100644 index 0000000..6fb3ad2 --- /dev/null +++ b/crates/stellar-registry-cli/src/commands/deploy/from_network.rs @@ -0,0 +1,129 @@ +use stellar_cli::{ + commands::contract::{deploy::wasm, fetch}, + config::{self, UnresolvedContract, network}, +}; +use stellar_registry_build::registry::Registry; + +use super::Cmd; +use crate::CommandParser; +use crate::commands::{global, register_contract}; + +#[derive(thiserror::Error, Debug)] +pub enum Error { + #[error(transparent)] + Registry(#[from] stellar_registry_build::Error), + #[error( + "contract `{0}` is flagged as compromised on the source network; pass --force to deploy it anyway" + )] + ContractFlagged(String), + #[error(transparent)] + Fetch(#[from] fetch::Error), + #[error(transparent)] + Deploy(#[from] wasm::Error), + #[error(transparent)] + RegisterContract(#[from] register_contract::Error), + #[error(transparent)] + Config(#[from] config::Error), + #[error(transparent)] + Io(#[from] std::io::Error), + #[error(transparent)] + Clap(#[from] clap::Error), + #[error("deploying returned a transaction instead of a contract id")] + NoContractId, +} + +/// `deploy --from-network`: look the contract up by name on `from_network`, +/// deploy its Wasm on the target network directly from the source account +/// (not through the registry's `deploy`), then register it under +/// `--contract-name`. +pub async fn deploy(cmd: &Cmd, from_network: &str) -> Result { + let source = source_config(cmd, from_network); + let source_name = cmd + .from_contract_name + .as_ref() + .unwrap_or(&cmd.contract_name); + + // 1. Resolve the name on the source network, refusing flagged contracts + // unless `--force`. `STELLAR_REGISTRY_CONTRACT_ID` points at the + // target's registry, so it only applies when both are the same network. + let same_network = + source.get_network()?.network_passphrase == cmd.config.get_network()?.network_passphrase; + let channel = source_name.channel(); + let registry = if same_network { + Registry::new(&source, channel).await? + } else { + Registry::new_canonical(&source, channel).await? + }; + let name = source_name.name(); + let source_id = if cmd.force { + registry.fetch_contract_id_unchecked(name).await? + } else { + registry + .fetch_contract_id(name) + .await + .map_err(|e| match e { + stellar_registry_build::Error::ContractFlagged(_) => { + Error::ContractFlagged(source_name.to_string()) + } + other => other.into(), + })? + }; + + // 2. Fetch the deployed contract's own Wasm from the source network. + let wasm = fetch::Cmd { + contract_id: Some(UnresolvedContract::Resolved(source_id)), + wasm_hash: None, + out_file: None, + locator: source.locator.clone(), + network: source.network.clone(), + } + .get_bytes() + .await?; + let wasm_path = std::env::temp_dir().join(format!("{source_id}.wasm")); + std::fs::write(&wasm_path, wasm)?; + + // 3. Deploy it on the target network with the constructor arguments. + let wasm_path = wasm_path.to_string_lossy(); + let slop: Vec = cmd + .slop + .iter() + .map(|a| a.to_string_lossy().into_owned()) + .collect(); + let mut args = vec!["--wasm", wasm_path.as_ref()]; + if !slop.is_empty() { + args.push("--"); + args.extend(slop.iter().map(String::as_str)); + } + let mut deploy = wasm::Cmd::parse_arg_vec(&args)?; + deploy.config = cmd.config.config.clone(); + let contract_id = deploy + .execute(&deploy.config, false, false) + .await? + .into_result() + .ok_or(Error::NoContractId)?; + + // 4. Register it under `--contract-name`. + register_contract::Cmd { + contract_name: cmd.contract_name.clone(), + contract_address: format!("{contract_id}"), + owner: None, + dry_run: false, + config: cmd.config.clone(), + } + .run() + .await?; + + Ok(contract_id) +} + +/// The source network's view of the config: same locator, different network. +/// Env-provided RPC settings belong to the target, so they are not carried +/// over. +fn source_config(cmd: &Cmd, from_network: &str) -> global::Args { + let mut source = cmd.config.clone(); + source.config.network = network::Args { + network: Some(from_network.to_string()), + ..Default::default() + }; + source +} From 089409e72e61b0ef44612a7b18866d19694835ae Mon Sep 17 00:00:00 2001 From: pselle Date: Tue, 3 Nov 2026 05:08:00 -0500 Subject: [PATCH 14/20] feat: explain a missing registry on the local network A fresh local network has nothing at the root registry address, so every registry command failed with a bare "contract not found". On local, check the root first and say how to fix it: deploy the registry contract and set STELLAR_REGISTRY_CONTRACT_ID, or use Stellar Scaffold. Co-Authored-By: Claude --- crates/stellar-registry-build/src/error.rs | 8 ++++++ crates/stellar-registry-build/src/registry.rs | 26 ++++++++++++++++++- .../src/commands/fetch_contract_id.rs | 22 ++++++++++++++++ 3 files changed, 55 insertions(+), 1 deletion(-) diff --git a/crates/stellar-registry-build/src/error.rs b/crates/stellar-registry-build/src/error.rs index f038b57..2b46ae2 100644 --- a/crates/stellar-registry-build/src/error.rs +++ b/crates/stellar-registry-build/src/error.rs @@ -25,4 +25,12 @@ pub enum Error { Rpc(#[from] soroban_rpc::Error), #[error(transparent)] Xdr(#[from] stellar_cli::xdr::Error), + #[error( + "no Stellar Registry is deployed on the local network (looked for one at \ + {id}{from_env}). Deploy the registry contract on local and set \ + STELLAR_REGISTRY_CONTRACT_ID to its id, or set up local development with Stellar \ + Scaffold.", + from_env = if *.from_env { " (from STELLAR_REGISTRY_CONTRACT_ID)" } else { "" } + )] + NoLocalRegistry { id: String, from_env: bool }, } diff --git a/crates/stellar-registry-build/src/registry.rs b/crates/stellar-registry-build/src/registry.rs index de3861b..e6c37ac 100644 --- a/crates/stellar-registry-build/src/registry.rs +++ b/crates/stellar-registry-build/src/registry.rs @@ -1,4 +1,4 @@ -use stellar_cli::config; +use stellar_cli::config::{self, network::passphrase}; use crate::{ Error, @@ -36,6 +36,7 @@ impl Registry { config: &config::Args, name: Option<&str>, ) -> Result { + contract.ensure_local_root_exists(config).await?; Ok(if let Some(name) = name { if let Ok(contract_id) = name.parse() { Registry(Contract::new(contract_id, config)) @@ -208,6 +209,29 @@ impl Registry { ) } + /// On a local network nothing is at the canonical root address unless + /// someone deployed a registry there, and every command would otherwise + /// fail with a bare "contract not found". Elsewhere the root exists. + async fn ensure_local_root_exists(&self, config: &config::Args) -> Result<(), Error> { + use stellar_cli::xdr; + if config.get_network()?.network_passphrase != passphrase::LOCAL { + return Ok(()); + } + let instance = self + .contract_data( + xdr::ScVal::LedgerKeyContractInstance, + xdr::ContractDataDurability::Persistent, + ) + .await?; + if instance.is_some() { + return Ok(()); + } + Err(Error::NoLocalRegistry { + id: format!("{}", self.0.id()), + from_env: std::env::var("STELLAR_REGISTRY_CONTRACT_ID").is_ok(), + }) + } + pub fn as_contract(&self) -> &Contract { &self.0 } diff --git a/crates/stellar-registry-cli/src/commands/fetch_contract_id.rs b/crates/stellar-registry-cli/src/commands/fetch_contract_id.rs index e8e0794..b3bd4d7 100644 --- a/crates/stellar-registry-cli/src/commands/fetch_contract_id.rs +++ b/crates/stellar-registry-cli/src/commands/fetch_contract_id.rs @@ -141,6 +141,28 @@ mod tests { assert!(!contract_id.to_string().is_empty()); } + #[tokio::test] + async fn explains_a_missing_local_registry() { + let registry = RegistryTest::new().await; + // A valid contract id with nothing deployed at it. + let missing = "CBESJIMX7J53SWJGJ7WQ6QTLJI4S5LPPJNC2BNVD63GIKAYCDTDOO322"; + let stderr = registry + .registry_cli("fetch-contract-id") + .env("STELLAR_REGISTRY_CONTRACT_ID", missing) + .arg("hello") + .assert() + .failure() + .get_output() + .stderr + .clone(); + let stderr = String::from_utf8_lossy(&stderr); + assert!( + stderr.contains("no Stellar Registry is deployed on the local network"), + "{stderr}" + ); + assert!(stderr.contains(missing), "{stderr}"); + } + #[tokio::test] async fn flagged() { let registry = RegistryTest::new().await; From 243a2ddb08148ab0de43395a24bb3b5fca87bf7b Mon Sep 17 00:00:00 2001 From: pselle Date: Wed, 4 Nov 2026 11:58:58 -0500 Subject: [PATCH 15/20] revert: take deploy --from-network back out of this PR Getting a contract onto a local network is a separate concern from testing with register_import!, so it moves out of this PR. This restores the CLI and stellar-registry-build to how they were before clone-contract, including dropping the missing-local-registry error that went with it. Co-Authored-By: Claude --- CLAUDE.md | 2 +- crates/stellar-registry-build/src/error.rs | 8 -- crates/stellar-registry-build/src/registry.rs | 46 +------ .../src/commands/deploy.rs | 86 +----------- .../src/commands/deploy/from_network.rs | 129 ------------------ .../src/commands/fetch_contract_id.rs | 22 --- .../stellar-registry-cli/src/commands/mod.rs | 2 +- 7 files changed, 11 insertions(+), 284 deletions(-) delete mode 100644 crates/stellar-registry-cli/src/commands/deploy/from_network.rs diff --git a/CLAUDE.md b/CLAUDE.md index 196cfd8..72307f6 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -50,7 +50,7 @@ Note: the `justfile` still carries some recipes from the monorepo. Prefer the `c `publish` → `deploy` → `install` / `create-alias` - `publish` — uploads wasm to the registry with semantic versioning -- `deploy` — instantiates a published wasm as a named contract, or with `--from-network` copies a contract registered on another network +- `deploy` — instantiates a published wasm as a named contract - `install` / `create-alias` — creates a local stellar-cli alias from the registry ## Testing diff --git a/crates/stellar-registry-build/src/error.rs b/crates/stellar-registry-build/src/error.rs index 2b46ae2..f038b57 100644 --- a/crates/stellar-registry-build/src/error.rs +++ b/crates/stellar-registry-build/src/error.rs @@ -25,12 +25,4 @@ pub enum Error { Rpc(#[from] soroban_rpc::Error), #[error(transparent)] Xdr(#[from] stellar_cli::xdr::Error), - #[error( - "no Stellar Registry is deployed on the local network (looked for one at \ - {id}{from_env}). Deploy the registry contract on local and set \ - STELLAR_REGISTRY_CONTRACT_ID to its id, or set up local development with Stellar \ - Scaffold.", - from_env = if *.from_env { " (from STELLAR_REGISTRY_CONTRACT_ID)" } else { "" } - )] - NoLocalRegistry { id: String, from_env: bool }, } diff --git a/crates/stellar-registry-build/src/registry.rs b/crates/stellar-registry-build/src/registry.rs index e6c37ac..216704c 100644 --- a/crates/stellar-registry-build/src/registry.rs +++ b/crates/stellar-registry-build/src/registry.rs @@ -1,4 +1,4 @@ -use stellar_cli::config::{self, network::passphrase}; +use stellar_cli::config; use crate::{ Error, @@ -17,26 +17,7 @@ impl Registry { } pub async fn new(config: &config::Args, name: Option<&str>) -> Result { - Self::from_root(Self::verified(config)?, config, name).await - } - - /// Like [`Self::new`], but always from the network's canonical root - /// registry, ignoring `STELLAR_REGISTRY_CONTRACT_ID`: that override names - /// one deployment (usually a local one), not a registry on every network. - pub async fn new_canonical(config: &config::Args, name: Option<&str>) -> Result { - let root = Registry(Contract::new( - verified_contract_id(&config.get_network()?.network_passphrase), - config, - )); - Self::from_root(root, config, name).await - } - - async fn from_root( - contract: Self, - config: &config::Args, - name: Option<&str>, - ) -> Result { - contract.ensure_local_root_exists(config).await?; + let contract = Self::verified(config)?; Ok(if let Some(name) = name { if let Ok(contract_id) = name.parse() { Registry(Contract::new(contract_id, config)) @@ -209,29 +190,6 @@ impl Registry { ) } - /// On a local network nothing is at the canonical root address unless - /// someone deployed a registry there, and every command would otherwise - /// fail with a bare "contract not found". Elsewhere the root exists. - async fn ensure_local_root_exists(&self, config: &config::Args) -> Result<(), Error> { - use stellar_cli::xdr; - if config.get_network()?.network_passphrase != passphrase::LOCAL { - return Ok(()); - } - let instance = self - .contract_data( - xdr::ScVal::LedgerKeyContractInstance, - xdr::ContractDataDurability::Persistent, - ) - .await?; - if instance.is_some() { - return Ok(()); - } - Err(Error::NoLocalRegistry { - id: format!("{}", self.0.id()), - from_env: std::env::var("STELLAR_REGISTRY_CONTRACT_ID").is_ok(), - }) - } - pub fn as_contract(&self) -> &Contract { &self.0 } diff --git a/crates/stellar-registry-cli/src/commands/deploy.rs b/crates/stellar-registry-cli/src/commands/deploy.rs index f2a4cae..78c24b7 100644 --- a/crates/stellar-registry-cli/src/commands/deploy.rs +++ b/crates/stellar-registry-cli/src/commands/deploy.rs @@ -19,7 +19,6 @@ use stellar_registry_build::{ use crate::commands::global; -pub mod from_network; pub mod util; #[derive(Parser, Debug, Clone)] @@ -30,21 +29,8 @@ pub struct Cmd { pub contract_name: Prefixed, /// Name of published contract to deploy from. Can use prefix of not using verified registry. /// E.g. `unverified/` - #[arg(long, required_unless_present = "from_network")] - pub wasm_name: Option, - /// Copy a contract registered on another network (e.g. `testnet`) instead - /// of deploying a published Wasm: its Wasm is deployed here from your - /// account, not through the registry, then registered as `--contract-name` - #[arg(long, conflicts_with_all = ["wasm_name", "version", "deployer"])] - pub from_network: Option, - /// Name of the contract on `--from-network`, if different from - /// `--contract-name` - #[arg(long, requires = "from_network")] - pub from_contract_name: Option, - /// With `--from-network`, copy the contract even if it is flagged as - /// compromised there - #[arg(long, requires = "from_network")] - pub force: bool, + #[arg(long)] + pub wasm_name: Prefixed, /// Arguments for constructor #[arg(last = true, id = "CONSTRUCTOR_ARGS")] pub slop: Vec, @@ -99,8 +85,6 @@ pub enum Error { InvalidReturnValue(String), #[error(transparent)] Registry(#[from] stellar_registry_build::Error), - #[error(transparent)] - FromNetwork(#[from] from_network::Error), #[error( "simulated fee of ~{} XLM is too large for a transaction; the call is likely paying unexpected storage rent", .0 / 10_000_000 @@ -110,13 +94,7 @@ pub enum Error { impl Cmd { pub async fn run(&self) -> Result<(), Error> { - let result = match &self.from_network { - Some(from_network) => from_network::deploy(self, from_network) - .await - .map_err(Error::from), - None => self.invoke().await, - }; - match result { + match self.invoke().await { Ok(contract_id) => { println!( "Contract {} deployed successfully to {contract_id}", @@ -132,20 +110,10 @@ impl Cmd { } } - /// `--wasm-name`, which clap requires unless `--from-network` is given. - fn wasm_name(&self) -> &Prefixed { - self.wasm_name - .as_ref() - .expect("--wasm-name is required without --from-network") - } - pub async fn hash(&self, registry: &Registry) -> Result { let res = registry .as_contract() - .invoke_with_result( - &["fetch_hash", "--wasm_name", self.wasm_name().name()], - true, - ) + .invoke_with_result(&["fetch_hash", "--wasm_name", self.wasm_name.name()], true) .await?; let res = res.trim_matches('"'); Ok(res.parse().unwrap()) @@ -166,7 +134,7 @@ impl Cmd { async fn invoke(&self) -> Result { let target_registry = self.contract_name.registry(&self.config).await?; - let wasm_registry = self.wasm_name().registry(&self.config).await?; + let wasm_registry = self.wasm_name.registry(&self.config).await?; let cross_registry = target_registry.as_contract().id() != wasm_registry.as_contract().id(); let client = self.config.rpc_client()?; let key = self.config.key_pair()?; @@ -183,7 +151,7 @@ impl Cmd { None }; let mut call_args: Vec = vec![ - ScVal::String(ScString(self.wasm_name().name().try_into().unwrap())), + ScVal::String(ScString(self.wasm_name.name().try_into().unwrap())), self.version.clone().map_or(ScVal::Void, |s| { ScVal::String(ScString(s.try_into().unwrap())) }), @@ -201,7 +169,7 @@ impl Cmd { // the trusted root pinned at construction, so we pass the name // rather than an address. Root has no prefix — its own name in // root storage is "registry". - let subregistry_name = self.wasm_name().channel().unwrap_or("registry"); + let subregistry_name = self.wasm_name.channel().unwrap_or("registry"); call_args.push(ScVal::String(ScString( subregistry_name.try_into().unwrap(), ))); @@ -350,44 +318,4 @@ mod tests { .assert() .failure(); } - - #[tokio::test] - async fn from_network_copies_a_registered_contract() { - let registry = RegistryTest::new().await; - publish(®istry, "hello", "0.0.1"); - registry - .registry_cli("deploy") - .arg("--contract-name") - .arg("hello") - .arg("--wasm-name") - .arg("hello") - .arg("--") - .arg("--admin=alice") - .assert() - .success(); - - // Source and target are both the local network here, so the copy - // needs a different name. - registry - .registry_cli("deploy") - .arg("--contract-name") - .arg("hello-copy") - .arg("--from-network") - .arg("local") - .arg("--from-contract-name") - .arg("hello") - .arg("--") - .arg("--admin=alice") - .assert() - .success(); - - let fetch = |name: &str| { - registry - .parse_cmd::(&[name]) - .unwrap() - }; - let original = fetch("hello").fetch_contract_id().await.unwrap(); - let copy = fetch("hello-copy").fetch_contract_id().await.unwrap(); - assert_ne!(original, copy, "a new deployment, not the same contract"); - } } diff --git a/crates/stellar-registry-cli/src/commands/deploy/from_network.rs b/crates/stellar-registry-cli/src/commands/deploy/from_network.rs deleted file mode 100644 index 6fb3ad2..0000000 --- a/crates/stellar-registry-cli/src/commands/deploy/from_network.rs +++ /dev/null @@ -1,129 +0,0 @@ -use stellar_cli::{ - commands::contract::{deploy::wasm, fetch}, - config::{self, UnresolvedContract, network}, -}; -use stellar_registry_build::registry::Registry; - -use super::Cmd; -use crate::CommandParser; -use crate::commands::{global, register_contract}; - -#[derive(thiserror::Error, Debug)] -pub enum Error { - #[error(transparent)] - Registry(#[from] stellar_registry_build::Error), - #[error( - "contract `{0}` is flagged as compromised on the source network; pass --force to deploy it anyway" - )] - ContractFlagged(String), - #[error(transparent)] - Fetch(#[from] fetch::Error), - #[error(transparent)] - Deploy(#[from] wasm::Error), - #[error(transparent)] - RegisterContract(#[from] register_contract::Error), - #[error(transparent)] - Config(#[from] config::Error), - #[error(transparent)] - Io(#[from] std::io::Error), - #[error(transparent)] - Clap(#[from] clap::Error), - #[error("deploying returned a transaction instead of a contract id")] - NoContractId, -} - -/// `deploy --from-network`: look the contract up by name on `from_network`, -/// deploy its Wasm on the target network directly from the source account -/// (not through the registry's `deploy`), then register it under -/// `--contract-name`. -pub async fn deploy(cmd: &Cmd, from_network: &str) -> Result { - let source = source_config(cmd, from_network); - let source_name = cmd - .from_contract_name - .as_ref() - .unwrap_or(&cmd.contract_name); - - // 1. Resolve the name on the source network, refusing flagged contracts - // unless `--force`. `STELLAR_REGISTRY_CONTRACT_ID` points at the - // target's registry, so it only applies when both are the same network. - let same_network = - source.get_network()?.network_passphrase == cmd.config.get_network()?.network_passphrase; - let channel = source_name.channel(); - let registry = if same_network { - Registry::new(&source, channel).await? - } else { - Registry::new_canonical(&source, channel).await? - }; - let name = source_name.name(); - let source_id = if cmd.force { - registry.fetch_contract_id_unchecked(name).await? - } else { - registry - .fetch_contract_id(name) - .await - .map_err(|e| match e { - stellar_registry_build::Error::ContractFlagged(_) => { - Error::ContractFlagged(source_name.to_string()) - } - other => other.into(), - })? - }; - - // 2. Fetch the deployed contract's own Wasm from the source network. - let wasm = fetch::Cmd { - contract_id: Some(UnresolvedContract::Resolved(source_id)), - wasm_hash: None, - out_file: None, - locator: source.locator.clone(), - network: source.network.clone(), - } - .get_bytes() - .await?; - let wasm_path = std::env::temp_dir().join(format!("{source_id}.wasm")); - std::fs::write(&wasm_path, wasm)?; - - // 3. Deploy it on the target network with the constructor arguments. - let wasm_path = wasm_path.to_string_lossy(); - let slop: Vec = cmd - .slop - .iter() - .map(|a| a.to_string_lossy().into_owned()) - .collect(); - let mut args = vec!["--wasm", wasm_path.as_ref()]; - if !slop.is_empty() { - args.push("--"); - args.extend(slop.iter().map(String::as_str)); - } - let mut deploy = wasm::Cmd::parse_arg_vec(&args)?; - deploy.config = cmd.config.config.clone(); - let contract_id = deploy - .execute(&deploy.config, false, false) - .await? - .into_result() - .ok_or(Error::NoContractId)?; - - // 4. Register it under `--contract-name`. - register_contract::Cmd { - contract_name: cmd.contract_name.clone(), - contract_address: format!("{contract_id}"), - owner: None, - dry_run: false, - config: cmd.config.clone(), - } - .run() - .await?; - - Ok(contract_id) -} - -/// The source network's view of the config: same locator, different network. -/// Env-provided RPC settings belong to the target, so they are not carried -/// over. -fn source_config(cmd: &Cmd, from_network: &str) -> global::Args { - let mut source = cmd.config.clone(); - source.config.network = network::Args { - network: Some(from_network.to_string()), - ..Default::default() - }; - source -} diff --git a/crates/stellar-registry-cli/src/commands/fetch_contract_id.rs b/crates/stellar-registry-cli/src/commands/fetch_contract_id.rs index b3bd4d7..e8e0794 100644 --- a/crates/stellar-registry-cli/src/commands/fetch_contract_id.rs +++ b/crates/stellar-registry-cli/src/commands/fetch_contract_id.rs @@ -141,28 +141,6 @@ mod tests { assert!(!contract_id.to_string().is_empty()); } - #[tokio::test] - async fn explains_a_missing_local_registry() { - let registry = RegistryTest::new().await; - // A valid contract id with nothing deployed at it. - let missing = "CBESJIMX7J53SWJGJ7WQ6QTLJI4S5LPPJNC2BNVD63GIKAYCDTDOO322"; - let stderr = registry - .registry_cli("fetch-contract-id") - .env("STELLAR_REGISTRY_CONTRACT_ID", missing) - .arg("hello") - .assert() - .failure() - .get_output() - .stderr - .clone(); - let stderr = String::from_utf8_lossy(&stderr); - assert!( - stderr.contains("no Stellar Registry is deployed on the local network"), - "{stderr}" - ); - assert!(stderr.contains(missing), "{stderr}"); - } - #[tokio::test] async fn flagged() { let registry = RegistryTest::new().await; diff --git a/crates/stellar-registry-cli/src/commands/mod.rs b/crates/stellar-registry-cli/src/commands/mod.rs index c5cf722..6a68d4f 100644 --- a/crates/stellar-registry-cli/src/commands/mod.rs +++ b/crates/stellar-registry-cli/src/commands/mod.rs @@ -97,7 +97,7 @@ pub enum Cmd { CreateAlias(Box), /// Get the current (latest) version of a published Wasm CurrentVersion(Box), - /// Deploy a named contract from a published Wasm, or copy one from another network + /// Deploy a named contract from a published Wasm Deploy(Box), /// Deploy a contract from a published Wasm without registering a name DeployUnnamed(Box), From 9da97a0586bbd06ec11b989ded9d197aed543e5a Mon Sep 17 00:00:00 2001 From: pselle Date: Wed, 4 Nov 2026 12:01:44 -0500 Subject: [PATCH 16/20] feat: explain a missing registry on the local network A fresh local network has nothing at the root registry address, so every registry command failed with a bare "contract not found". On local, check the root first and say how to fix it: deploy the registry contract and set STELLAR_REGISTRY_CONTRACT_ID, or use Stellar Scaffold. Co-Authored-By: Claude --- crates/stellar-registry-build/src/error.rs | 8 ++++++ crates/stellar-registry-build/src/registry.rs | 26 ++++++++++++++++++- .../src/commands/fetch_contract_id.rs | 22 ++++++++++++++++ 3 files changed, 55 insertions(+), 1 deletion(-) diff --git a/crates/stellar-registry-build/src/error.rs b/crates/stellar-registry-build/src/error.rs index f038b57..2b46ae2 100644 --- a/crates/stellar-registry-build/src/error.rs +++ b/crates/stellar-registry-build/src/error.rs @@ -25,4 +25,12 @@ pub enum Error { Rpc(#[from] soroban_rpc::Error), #[error(transparent)] Xdr(#[from] stellar_cli::xdr::Error), + #[error( + "no Stellar Registry is deployed on the local network (looked for one at \ + {id}{from_env}). Deploy the registry contract on local and set \ + STELLAR_REGISTRY_CONTRACT_ID to its id, or set up local development with Stellar \ + Scaffold.", + from_env = if *.from_env { " (from STELLAR_REGISTRY_CONTRACT_ID)" } else { "" } + )] + NoLocalRegistry { id: String, from_env: bool }, } diff --git a/crates/stellar-registry-build/src/registry.rs b/crates/stellar-registry-build/src/registry.rs index 216704c..d6b0ef6 100644 --- a/crates/stellar-registry-build/src/registry.rs +++ b/crates/stellar-registry-build/src/registry.rs @@ -1,4 +1,4 @@ -use stellar_cli::config; +use stellar_cli::config::{self, network::passphrase}; use crate::{ Error, @@ -18,6 +18,7 @@ impl Registry { pub async fn new(config: &config::Args, name: Option<&str>) -> Result { let contract = Self::verified(config)?; + contract.ensure_local_root_exists(config).await?; Ok(if let Some(name) = name { if let Ok(contract_id) = name.parse() { Registry(Contract::new(contract_id, config)) @@ -190,6 +191,29 @@ impl Registry { ) } + /// On a local network nothing is at the canonical root address unless + /// someone deployed a registry there, and every command would otherwise + /// fail with a bare "contract not found". Elsewhere the root exists. + async fn ensure_local_root_exists(&self, config: &config::Args) -> Result<(), Error> { + use stellar_cli::xdr; + if config.get_network()?.network_passphrase != passphrase::LOCAL { + return Ok(()); + } + let instance = self + .contract_data( + xdr::ScVal::LedgerKeyContractInstance, + xdr::ContractDataDurability::Persistent, + ) + .await?; + if instance.is_some() { + return Ok(()); + } + Err(Error::NoLocalRegistry { + id: format!("{}", self.0.id()), + from_env: std::env::var("STELLAR_REGISTRY_CONTRACT_ID").is_ok(), + }) + } + pub fn as_contract(&self) -> &Contract { &self.0 } diff --git a/crates/stellar-registry-cli/src/commands/fetch_contract_id.rs b/crates/stellar-registry-cli/src/commands/fetch_contract_id.rs index e8e0794..b3bd4d7 100644 --- a/crates/stellar-registry-cli/src/commands/fetch_contract_id.rs +++ b/crates/stellar-registry-cli/src/commands/fetch_contract_id.rs @@ -141,6 +141,28 @@ mod tests { assert!(!contract_id.to_string().is_empty()); } + #[tokio::test] + async fn explains_a_missing_local_registry() { + let registry = RegistryTest::new().await; + // A valid contract id with nothing deployed at it. + let missing = "CBESJIMX7J53SWJGJ7WQ6QTLJI4S5LPPJNC2BNVD63GIKAYCDTDOO322"; + let stderr = registry + .registry_cli("fetch-contract-id") + .env("STELLAR_REGISTRY_CONTRACT_ID", missing) + .arg("hello") + .assert() + .failure() + .get_output() + .stderr + .clone(); + let stderr = String::from_utf8_lossy(&stderr); + assert!( + stderr.contains("no Stellar Registry is deployed on the local network"), + "{stderr}" + ); + assert!(stderr.contains(missing), "{stderr}"); + } + #[tokio::test] async fn flagged() { let registry = RegistryTest::new().await; From 83413236bb6fa0282693358583da974afa98becb Mon Sep 17 00:00:00 2001 From: pselle Date: Wed, 4 Nov 2026 16:44:51 -0500 Subject: [PATCH 17/20] refactor: address review on register_import! - Move register_import! and its tests into register.rs; contract.rs keeps the shared resolve. - Drop the token_caller fixture and test-register-import-testnet recipe. - README: workspace dependencies in the example, link the justfile. - Docs: trim the args text, say tuple args are allowed with `contract` but named args aren't, and say the name must match the contract's import_contract! call. Co-Authored-By: Claude --- CLAUDE.md | 1 - Cargo.toml | 1 - crates/stellar-registry-macro/src/contract.rs | 544 +---------------- crates/stellar-registry-macro/src/lib.rs | 15 +- crates/stellar-registry-macro/src/register.rs | 545 ++++++++++++++++++ .../contracts/token_caller/.gitignore | 1 - .../contracts/token_caller/Cargo.toml | 21 - .../contracts/token_caller/src/lib.rs | 36 -- crates/stellar-registry/README.md | 10 +- justfile | 4 - 10 files changed, 563 insertions(+), 615 deletions(-) create mode 100644 crates/stellar-registry-macro/src/register.rs delete mode 100644 crates/stellar-registry-test/fixtures/contracts/token_caller/.gitignore delete mode 100644 crates/stellar-registry-test/fixtures/contracts/token_caller/Cargo.toml delete mode 100644 crates/stellar-registry-test/fixtures/contracts/token_caller/src/lib.rs diff --git a/CLAUDE.md b/CLAUDE.md index 72307f6..4558330 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -58,7 +58,6 @@ Note: the `justfile` still carries some recipes from the monorepo. Prefer the `c - Unit tests run without external dependencies. - Integration tests require a local Stellar RPC (Docker `stellar/quickstart`) and the `integration-tests` feature flag. - `just test-register-import` runs the `hello_caller` fixture, which is excluded from the workspace because `import_contract!` needs seeded cache files to compile. -- `just test-register-import-testnet` runs the `token_caller` fixture against testnet; it needs network access, so CI doesn't run it. - Command tests use `stellar_registry_test::RegistryTest`, defined in local crate `crates/stellar-registry-test` ## Cross-repo dependencies diff --git a/Cargo.toml b/Cargo.toml index e9f34f9..64bea4e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -5,7 +5,6 @@ members = ["crates/*", "crates/stellar-registry-test/fixtures/contracts/*"] # Needs seeded `import_contract!` cache files to compile; see its Cargo.toml. exclude = [ "crates/stellar-registry-test/fixtures/contracts/hello_caller", - "crates/stellar-registry-test/fixtures/contracts/token_caller", ] [workspace.package] diff --git a/crates/stellar-registry-macro/src/contract.rs b/crates/stellar-registry-macro/src/contract.rs index de0f8bf..7434d91 100644 --- a/crates/stellar-registry-macro/src/contract.rs +++ b/crates/stellar-registry-macro/src/contract.rs @@ -12,16 +12,14 @@ use syn::{ use stellar_registry_name::Prefixed; -use stellar_xdr::{Limited, Limits, ReadXdr, ScSpecEntry, ScSpecTypeDef}; - use crate::asset; use crate::util::{Name, explorer_url, manifest, mod_ident, network_name, track_network_env}; /// `import_contract!(env_expr, name)` — `name` is a bare ident or a string /// literal (optionally channel-prefixed, e.g. `"unverified/our_dao"`). -struct Input { - env: Expr, - name: Name, +pub(crate) struct Input { + pub(crate) env: Expr, + pub(crate) name: Name, } impl Parse for Input { @@ -33,97 +31,6 @@ impl Parse for Input { } } -/// `args = { name: value, .. }`, or a positional tuple `args = (..)`. -enum Args { - Named { - brace: syn::token::Brace, - fields: Vec<(Ident, Expr)>, - }, - Positional(Expr), -} - -impl Parse for Args { - fn parse(input: ParseStream) -> syn::Result { - if !input.peek(syn::token::Brace) { - return Ok(Self::Positional(input.parse()?)); - } - let content; - let brace = syn::braced!(content in input); - let mut fields = Vec::new(); - while !content.is_empty() { - let name: Ident = content.parse()?; - // `{ admin }` is shorthand for `{ admin: admin }`, as in a struct literal. - let value = if content.peek(Token![:]) { - content.parse::()?; - content.parse()? - } else { - syn::parse_quote!(#name) - }; - fields.push((name, value)); - if content.is_empty() { - break; - } - content.parse::()?; - } - Ok(Self::Named { brace, fields }) - } -} - -/// `register_import!(env_expr, name [, args = (..)] [, contract = Mock])` — -/// `name` as in [`Input`]; the options may come in either order. -struct RegisterInput { - env: Expr, - name: Name, - /// Constructor arguments, checked against the imported Wasm's constructor. - args: Option, - /// A native contract to register instead of the imported Wasm. - contract: Option, -} - -impl Parse for RegisterInput { - fn parse(input: ParseStream) -> syn::Result { - let Input { env, name } = input.parse()?; - let (mut args, mut contract) = (None, None); - while !input.is_empty() { - input.parse::()?; - if input.is_empty() { - break; - } - let key: Ident = input.parse()?; - let given = match key.to_string().as_str() { - "args" => args.is_some(), - "contract" => contract.is_some(), - other => { - return Err(syn::Error::new( - key.span(), - format!( - "unknown option `{other}`: expected `args = (..)` or `contract = ..`" - ), - )); - } - }; - if given { - return Err(syn::Error::new( - key.span(), - format!("`{key}` given more than once"), - )); - } - input.parse::()?; - if key == "args" { - args = Some(input.parse()?); - } else { - contract = Some(input.parse()?); - } - } - Ok(Self { - env, - name, - args, - contract, - }) - } -} - /// Whether `raw` names an asset rather than a registered contract: exactly /// `native`/`xlm`, or a `CODE:ISSUER` literal. Colons are never valid in a /// registry name (`Prefixed` rejects them outright), so `raw.contains(':')` @@ -387,7 +294,7 @@ fn expand_asset_token_client(env: &Expr, asset_xdr: &[u8]) -> proc_macro2::Token /// What a name resolved to at build time. #[derive(Debug)] -enum Resolved { +pub(crate) enum Resolved { /// A Wasm-backed contract: its deployed address and cached Wasm. Wasm { mod_ident: Ident, @@ -404,7 +311,7 @@ enum Resolved { /// Resolve `name` for `macro_name!` — shared by `import_contract!` and /// `register_import!` so a test registers at exactly the address (and with /// exactly the Wasm) the contract under test was compiled against. -fn resolve(name: &Name, macro_name: &str) -> syn::Result { +pub(crate) fn resolve(name: &Name, macro_name: &str) -> syn::Result { let span = name.span(); let err = |msg: String| syn::Error::new(span, msg); @@ -508,247 +415,6 @@ pub(crate) fn import_contract( }) } -/// The constructor's parameters (name and type), read from the contract spec -/// embedded in the Wasm; `None` if the contract has no constructor. -fn constructor_params(wasm: &[u8]) -> Result>, String> { - let err = - |e: &dyn std::fmt::Display| format!("could not read the contract spec from the Wasm: {e}"); - // Only the raw section comes from soroban-spec; it is parsed with this - // crate's stellar-xdr, which needn't match soroban-spec's. - let raw = soroban_spec::read::raw_from_wasm(wasm).map_err(|e| err(&e))?; - let mut reader = Limited::new(std::io::Cursor::new(raw), Limits::none()); - let entries = ScSpecEntry::read_xdr_iter(&mut reader) - .collect::, _>>() - .map_err(|e| err(&e))?; - Ok(entries.into_iter().find_map(|entry| match entry { - ScSpecEntry::FunctionV0(f) if f.name.to_utf8_string_lossy() == "__constructor" => Some( - f.inputs - .iter() - .map(|i| (i.name.to_utf8_string_lossy(), i.type_.clone())) - .collect(), - ), - _ => None, - })) -} - -/// A spec type as the Rust type a test would pass, for error messages. -fn type_name(t: &ScSpecTypeDef) -> String { - use ScSpecTypeDef as T; - match t { - T::Bool => "bool".into(), - T::U32 => "u32".into(), - T::I32 => "i32".into(), - T::U64 => "u64".into(), - T::I64 => "i64".into(), - T::U128 => "u128".into(), - T::I128 => "i128".into(), - T::Address => "Address".into(), - T::MuxedAddress => "MuxedAddress".into(), - T::String => "String".into(), - T::Symbol => "Symbol".into(), - T::Bytes => "Bytes".into(), - T::BytesN(b) => format!("BytesN<{}>", b.n), - T::Option(o) => format!("Option<{}>", type_name(&o.value_type)), - T::Vec(v) => format!("Vec<{}>", type_name(&v.element_type)), - T::Map(m) => format!( - "Map<{}, {}>", - type_name(&m.key_type), - type_name(&m.value_type) - ), - T::Udt(u) => u.name.to_utf8_string_lossy(), - other => other.name().to_string(), - } -} - -/// `args = { admin:
, fee: }` — a starting point to paste. -fn args_suggestion(params: &[(String, ScSpecTypeDef)]) -> String { - let fields: Vec = params - .iter() - .map(|(name, t)| format!("{name}: <{}>", type_name(t))) - .collect(); - format!("args = {{ {} }}", fields.join(", ")) -} - -/// Order named `args` by the constructor's parameters, reporting unknown, -/// repeated and missing names. -fn order_named_args<'a>( - fields: &'a [(Ident, Expr)], - params: &[(String, ScSpecTypeDef)], - brace_span: proc_macro2::Span, -) -> syn::Result> { - let expected = || args_suggestion(params); - for (i, (name, _)) in fields.iter().enumerate() { - if !params.iter().any(|(p, _)| name == p) { - return Err(syn::Error::new( - name.span(), - format!( - "the constructor has no parameter `{name}`; expected {}", - expected() - ), - )); - } - if fields[..i].iter().any(|(earlier, _)| earlier == name) { - return Err(syn::Error::new( - name.span(), - format!("`{name}` given more than once"), - )); - } - } - params - .iter() - .map(|(p, _)| { - fields - .iter() - .find(|(name, _)| name == p) - .map(|(_, value)| value) - .ok_or_else(|| { - syn::Error::new( - brace_span, - format!( - "missing constructor argument `{p}`; expected {}", - expected() - ), - ) - }) - }) - .collect() -} - -/// Constructor args for the imported Wasm, as a typed call to the -/// `Args::__constructor` that `contractimport!` generates, so rustc checks -/// each value against the constructor's parameter types. -fn wasm_args( - input: &RegisterInput, - mod_ident: &Ident, - params: Option<&[(String, ScSpecTypeDef)]>, -) -> syn::Result { - let name = input.name.raw(); - let values: Vec<&Expr> = match (&input.args, params) { - (None, None | Some([])) => return Ok(quote!(())), - (None, Some(params)) => { - return Err(syn::Error::new( - input.name.span(), - format!( - "`{name}`'s constructor takes arguments. Pass them with `args`:\n\n \ - register_import!(env, {name}, {})", - args_suggestion(params) - ), - )); - } - (Some(args), None) => { - let span = match args { - Args::Named { brace, .. } => brace.span.join(), - Args::Positional(e) => syn::spanned::Spanned::span(e), - }; - return Err(syn::Error::new( - span, - format!("`{name}` has no constructor, so it takes no `args`"), - )); - } - (Some(Args::Named { brace, fields }), Some(params)) => { - order_named_args(fields, params, brace.span.join())? - } - (Some(Args::Positional(Expr::Tuple(tuple))), Some(_)) => tuple.elems.iter().collect(), - (Some(Args::Positional(other)), Some(_)) => return Ok(quote!(#other)), - }; - // Spanned per value, so a type mismatch points at the value, not the macro. - let values = values - .iter() - .map(|v| quote::quote_spanned!(syn::spanned::Spanned::span(v)=> &(#v))); - Ok(quote!(#mod_ident::Args::__constructor(#(#values),*))) -} - -/// Emit a block expression registering a contract at the address -/// `import_contract!` baked in: the imported Wasm, or `contract` (a native -/// mock) when given. Evaluates to the `Address`. -fn expand_register( - input: &RegisterInput, - resolved: &Resolved, - params: Option<&[(String, ScSpecTypeDef)]>, -) -> syn::Result { - let (mod_ident, wasm_path, address) = match resolved { - Resolved::Wasm { - mod_ident, - wasm_path, - address, - } => (mod_ident, wasm_path, address), - Resolved::Sac { .. } | Resolved::Asset { .. } => { - return Err(syn::Error::new( - input.name.span(), - format!( - "register_import! does not support Stellar Asset Contracts yet, and `{}` \ - resolves to one. SAC support is coming; until then, register a test asset \ - yourself with `Env::register_stellar_asset_contract_v2`.", - input.name.raw() - ), - )); - } - }; - let env = &input.env; - let track = track_network_env(); - let (module, contract, args) = if let Some(contract) = &input.contract { - // A native mock has no spec to name its parameters by. - let args = match &input.args { - None => quote!(()), - Some(Args::Positional(args)) => quote!(#args), - Some(Args::Named { brace, .. }) => { - return Err(syn::Error::new( - brace.span.join(), - "named `args` need the imported Wasm's constructor; with `contract = ..`, \ - pass the mock's constructor arguments as a tuple, `args = (..)`", - )); - } - }; - (quote!(), quote!(#contract), args) - } else { - let args = wasm_args(input, mod_ident, params)?; - let wasm_path = wasm_path.to_string_lossy(); - ( - quote! { - #[allow(non_snake_case)] - mod #mod_ident { - #![allow(clippy::ref_option, clippy::too_many_arguments)] - use ::soroban_sdk; - soroban_sdk::contractimport!(file = #wasm_path); - } - }, - quote!(#mod_ident::WASM), - args, - ) - }; - Ok(quote! { - { - #track - #module - let __env: &::soroban_sdk::Env = #env; - ::soroban_sdk::Env::register_at( - __env, - &::soroban_sdk::Address::from_str(__env, #address), - #contract, - #args, - ) - } - }) -} - -pub(crate) fn register_import( - input: proc_macro::TokenStream, -) -> syn::Result { - let input: RegisterInput = syn::parse(input)?; - let resolved = resolve(&input.name, "register_import")?; - // Only the imported Wasm's constructor is read; a mock has no spec. - let params = match (&resolved, &input.contract) { - (Resolved::Wasm { wasm_path, .. }, None) => { - let err = |e: String| syn::Error::new(input.name.span(), e); - let wasm = std::fs::read(wasm_path) - .map_err(|e| err(format!("could not read {}: {e}", wasm_path.display())))?; - constructor_params(&wasm).map_err(err)? - } - _ => None, - }; - expand_register(&input, &resolved, params.as_deref()) -} - #[cfg(test)] mod helpers { use super::*; @@ -1006,203 +672,3 @@ mod codegen { assert!(err.contains("invalid issuer"), "{err}"); } } - -#[cfg(test)] -mod register { - use super::*; - use proc_macro2::Span; - use quote::quote; - use syn::{Ident, parse2}; - - const A: &str = "CBESJIMX7J53SWJGJ7WQ6QTLJI4S5LPPJNC2BNVD63GIKAYCDTDOO322"; - - fn wasm() -> Resolved { - Resolved::Wasm { - mod_ident: Ident::new("our_dao", Span::call_site()), - wasm_path: "/tmp/target/stellar/local/deployed/our_dao.wasm".into(), - address: A.to_string(), - } - } - - /// `__constructor(admin: Address, fee: u32)`. - fn params() -> Vec<(String, ScSpecTypeDef)> { - vec![ - ("admin".into(), ScSpecTypeDef::Address), - ("fee".into(), ScSpecTypeDef::U32), - ] - } - - fn expand( - tokens: proc_macro2::TokenStream, - resolved: &Resolved, - params: Option<&[(String, ScSpecTypeDef)]>, - ) -> syn::Result { - let input: RegisterInput = parse2(tokens).unwrap(); - expand_register(&input, resolved, params).map(|out| out.to_string()) - } - - fn expanded(tokens: proc_macro2::TokenStream, resolved: &Resolved) -> String { - expand(tokens, resolved, None).unwrap() - } - - fn with_params(tokens: proc_macro2::TokenStream) -> syn::Result { - expand(tokens, &wasm(), Some(¶ms())) - } - - fn parse_err(tokens: proc_macro2::TokenStream) -> String { - parse2::(tokens).err().unwrap().to_string() - } - - #[test] - fn parses_name_only() { - let input: RegisterInput = parse2(quote!(&env, "unverified/our-dao")).unwrap(); - assert_eq!(input.name.raw(), "unverified/our-dao"); - assert!(input.args.is_none()); - assert!(input.contract.is_none()); - } - - #[test] - fn parses_options_in_either_order_with_trailing_comma() { - for tokens in [ - quote!(&env, our_dao, args = (&admin, 7u32), contract = MockDao,), - quote!(&env, our_dao, contract = MockDao, args = (&admin, 7u32)), - ] { - let input: RegisterInput = parse2(tokens).unwrap(); - assert!(input.args.is_some()); - assert!(input.contract.is_some()); - } - } - - #[test] - fn rejects_unknown_and_repeated_options() { - let msg = parse_err(quote!(&env, our_dao, wasm = X)); - assert!(msg.contains("unknown option `wasm`"), "{msg}"); - let msg = parse_err(quote!(&env, our_dao, args = (), args = ())); - assert!(msg.contains("`args` given more than once"), "{msg}"); - // A positional third argument is not accepted. - assert!(parse2::(quote!(&env, our_dao, (admin,))).is_err()); - } - - #[test] - fn registers_the_cached_wasm_at_the_baked_address() { - let out = expanded(quote!(&env, our_dao), &wasm()); - assert!(out.contains("Env :: register_at"), "{out}"); - assert!(out.contains("contractimport !"), "{out}"); - assert!(out.contains("our_dao.wasm"), "{out}"); - assert!( - out.contains("our_dao :: WASM , () ,"), - "no constructor: {out}" - ); - assert!(out.contains(A), "bakes the resolved id: {out}"); - } - - #[test] - fn named_args_are_ordered_by_the_constructor_and_typed() { - // Given out of order, and `admin` in shorthand. - let out = with_params(quote!(&env, our_dao, args = { fee: 7u32, admin })).unwrap(); - assert!( - out.contains("our_dao :: Args :: __constructor (& (admin) , & (7u32))"), - "{out}" - ); - } - - #[test] - fn positional_tuple_args_are_typed_too() { - let out = with_params(quote!(&env, our_dao, args = (&admin, 7u32))).unwrap(); - assert!( - out.contains("our_dao :: Args :: __constructor (& (& admin) , & (7u32))"), - "{out}" - ); - } - - #[test] - fn missing_args_suggest_the_constructor_parameters() { - let msg = with_params(quote!(&env, our_dao)).unwrap_err().to_string(); - assert!(msg.contains("constructor takes arguments"), "{msg}"); - assert!( - msg.contains("register_import!(env, our_dao, args = { admin:
, fee: })"), - "{msg}" - ); - } - - #[test] - fn named_args_report_unknown_repeated_and_missing_names() { - let msg = with_params(quote!(&env, our_dao, args = { owner: a, fee: 1u32 })) - .unwrap_err() - .to_string(); - assert!(msg.contains("no parameter `owner`"), "{msg}"); - let msg = with_params(quote!(&env, our_dao, args = { admin, admin, fee: 1u32 })) - .unwrap_err() - .to_string(); - assert!(msg.contains("`admin` given more than once"), "{msg}"); - let msg = with_params(quote!(&env, our_dao, args = { admin })) - .unwrap_err() - .to_string(); - assert!(msg.contains("missing constructor argument `fee`"), "{msg}"); - } - - #[test] - fn args_without_a_constructor_are_rejected() { - let msg = expand(quote!(&env, our_dao, args = { admin }), &wasm(), None) - .unwrap_err() - .to_string(); - assert!(msg.contains("has no constructor"), "{msg}"); - } - - #[test] - fn named_args_are_rejected_for_a_mock() { - let msg = expand( - quote!(&env, our_dao, contract = MockDao, args = { admin }), - &wasm(), - None, - ) - .unwrap_err() - .to_string(); - assert!(msg.contains("as a tuple"), "{msg}"); - let out = expanded( - quote!(&env, our_dao, contract = MockDao, args = (&admin,)), - &wasm(), - ); - assert!(out.contains("MockDao , (& admin ,)"), "{out}"); - } - - #[test] - fn spec_types_are_named_as_rust_types() { - use stellar_xdr::{ScSpecTypeBytesN, ScSpecTypeVec}; - assert_eq!(type_name(&ScSpecTypeDef::I128), "i128"); - assert_eq!( - type_name(&ScSpecTypeDef::BytesN(ScSpecTypeBytesN { n: 32 })), - "BytesN<32>" - ); - assert_eq!( - type_name(&ScSpecTypeDef::Vec(Box::new(ScSpecTypeVec { - element_type: Box::new(ScSpecTypeDef::Address), - }))), - "Vec
" - ); - } - - #[test] - fn contract_option_replaces_the_wasm() { - let out = expanded(quote!(&env, our_dao, contract = mocks::MockDao), &wasm()); - assert!(out.contains("mocks :: MockDao"), "{out}"); - assert!(!out.contains("contractimport"), "{out}"); - assert!(out.contains(A), "still at the baked address: {out}"); - } - - #[test] - fn stellar_asset_contracts_are_a_clear_compile_error() { - let sac = Resolved::Sac { - address: A.to_string(), - }; - for tokens in [ - quote!(&env, xlm), - quote!(&env, "circle/usdc", contract = MockToken), - ] { - let input: RegisterInput = parse2(tokens).unwrap(); - let msg = expand_register(&input, &sac, None).unwrap_err().to_string(); - assert!(msg.contains("Stellar Asset Contracts"), "{msg}"); - assert!(msg.contains("SAC support is coming"), "{msg}"); - } - } -} diff --git a/crates/stellar-registry-macro/src/lib.rs b/crates/stellar-registry-macro/src/lib.rs index 3a08ac3..12ffe77 100644 --- a/crates/stellar-registry-macro/src/lib.rs +++ b/crates/stellar-registry-macro/src/lib.rs @@ -10,6 +10,7 @@ use proc_macro::TokenStream; mod asset; mod contract; mod contract_client; +mod register; mod util; use util::ProcMacroWrapper as _; @@ -101,15 +102,15 @@ pub fn import_contract(input: TokenStream) -> TokenStream { /// address and Wasm (same cache files, same `STELLAR_NO_REGISTRY` /// handling, same flagged-contract check). /// - `args = { name: value, .. }` passes constructor arguments by parameter -/// name, read from the imported Wasm's constructor; `{ admin }` is -/// shorthand for `{ admin: admin }`. Values are borrowed, not moved, and -/// type-checked against the constructor. Leaving out `args` when the +/// name, read from the imported Wasm's constructor. Values are type- +/// checked against the constructor. Leaving out `args` when the /// constructor takes parameters is a compile error that lists them. A /// positional tuple, `args = (..)`, also works. /// - `contract = ` registers a native `#[contract]` type instead of the -/// Wasm, for when you want to control what the dependency returns. Its -/// constructor arguments are a tuple, `args = (..)`, since a mock has no -/// spec to name them by. +/// Wasm, for when you want to control what the dependency returns. A mock +/// can have its own constructor, so `args` may be given with `contract`, but +/// only as a tuple: named `args` are rejected, since a mock has no spec to +/// name them by. /// /// Exported as `stellar_registry::testutils::register_import!`, behind /// `stellar-registry`'s `testutils` feature. The expansion calls @@ -122,7 +123,7 @@ pub fn import_contract(input: TokenStream) -> TokenStream { /// instead. #[proc_macro] pub fn register_import(input: TokenStream) -> TokenStream { - contract::register_import(input).to_token_stream() + register::register_import(input).to_token_stream() } /// Generate a contract client from a published wasm — from your workspace's diff --git a/crates/stellar-registry-macro/src/register.rs b/crates/stellar-registry-macro/src/register.rs new file mode 100644 index 0000000..063b41d --- /dev/null +++ b/crates/stellar-registry-macro/src/register.rs @@ -0,0 +1,545 @@ +//! `register_import!`: register an `import_contract!`-ed contract in a test +//! `Env` at the address `import_contract!` resolves to. +use quote::quote; +use syn::{ + Expr, Ident, Token, + parse::{Parse, ParseStream}, +}; + +use stellar_xdr::{Limited, Limits, ReadXdr, ScSpecEntry, ScSpecTypeDef}; + +use crate::contract::{Input, Resolved, resolve}; +use crate::util::{Name, track_network_env}; + +/// `args = { name: value, .. }`, or a positional tuple `args = (..)`. +enum Args { + Named { + brace: syn::token::Brace, + fields: Vec<(Ident, Expr)>, + }, + Positional(Expr), +} + +impl Parse for Args { + fn parse(input: ParseStream) -> syn::Result { + if !input.peek(syn::token::Brace) { + return Ok(Self::Positional(input.parse()?)); + } + let content; + let brace = syn::braced!(content in input); + let mut fields = Vec::new(); + while !content.is_empty() { + let name: Ident = content.parse()?; + // `{ admin }` is shorthand for `{ admin: admin }`, as in a struct literal. + let value = if content.peek(Token![:]) { + content.parse::()?; + content.parse()? + } else { + syn::parse_quote!(#name) + }; + fields.push((name, value)); + if content.is_empty() { + break; + } + content.parse::()?; + } + Ok(Self::Named { brace, fields }) + } +} + +/// `register_import!(env_expr, name [, args = { .. }] [, contract = Mock])`. +/// `name` must match the contract's `import_contract!` call, since both +/// resolve it to the same address. The options may come in either order. +struct RegisterInput { + env: Expr, + name: Name, + /// Constructor arguments, checked against the imported Wasm's constructor. + args: Option, + /// A native contract to register instead of the imported Wasm. + contract: Option, +} + +impl Parse for RegisterInput { + fn parse(input: ParseStream) -> syn::Result { + let Input { env, name } = input.parse()?; + let (mut args, mut contract) = (None, None); + while !input.is_empty() { + input.parse::()?; + if input.is_empty() { + break; + } + let key: Ident = input.parse()?; + let given = match key.to_string().as_str() { + "args" => args.is_some(), + "contract" => contract.is_some(), + other => { + return Err(syn::Error::new( + key.span(), + format!( + "unknown option `{other}`: expected `args = (..)` or `contract = ..`" + ), + )); + } + }; + if given { + return Err(syn::Error::new( + key.span(), + format!("`{key}` given more than once"), + )); + } + input.parse::()?; + if key == "args" { + args = Some(input.parse()?); + } else { + contract = Some(input.parse()?); + } + } + Ok(Self { + env, + name, + args, + contract, + }) + } +} + +/// The constructor's parameters (name and type), read from the contract spec +/// embedded in the Wasm; `None` if the contract has no constructor. +fn constructor_params(wasm: &[u8]) -> Result>, String> { + let err = + |e: &dyn std::fmt::Display| format!("could not read the contract spec from the Wasm: {e}"); + // Only the raw section comes from soroban-spec; it is parsed with this + // crate's stellar-xdr, which needn't match soroban-spec's. + let raw = soroban_spec::read::raw_from_wasm(wasm).map_err(|e| err(&e))?; + let mut reader = Limited::new(std::io::Cursor::new(raw), Limits::none()); + let entries = ScSpecEntry::read_xdr_iter(&mut reader) + .collect::, _>>() + .map_err(|e| err(&e))?; + Ok(entries.into_iter().find_map(|entry| match entry { + ScSpecEntry::FunctionV0(f) if f.name.to_utf8_string_lossy() == "__constructor" => Some( + f.inputs + .iter() + .map(|i| (i.name.to_utf8_string_lossy(), i.type_.clone())) + .collect(), + ), + _ => None, + })) +} + +/// A spec type as the Rust type a test would pass, for error messages. +fn type_name(t: &ScSpecTypeDef) -> String { + use ScSpecTypeDef as T; + match t { + T::Bool => "bool".into(), + T::U32 => "u32".into(), + T::I32 => "i32".into(), + T::U64 => "u64".into(), + T::I64 => "i64".into(), + T::U128 => "u128".into(), + T::I128 => "i128".into(), + T::Address => "Address".into(), + T::MuxedAddress => "MuxedAddress".into(), + T::String => "String".into(), + T::Symbol => "Symbol".into(), + T::Bytes => "Bytes".into(), + T::BytesN(b) => format!("BytesN<{}>", b.n), + T::Option(o) => format!("Option<{}>", type_name(&o.value_type)), + T::Vec(v) => format!("Vec<{}>", type_name(&v.element_type)), + T::Map(m) => format!( + "Map<{}, {}>", + type_name(&m.key_type), + type_name(&m.value_type) + ), + T::Udt(u) => u.name.to_utf8_string_lossy(), + other => other.name().to_string(), + } +} + +/// `args = { admin:
, fee: }` — a starting point to paste. +fn args_suggestion(params: &[(String, ScSpecTypeDef)]) -> String { + let fields: Vec = params + .iter() + .map(|(name, t)| format!("{name}: <{}>", type_name(t))) + .collect(); + format!("args = {{ {} }}", fields.join(", ")) +} + +/// Order named `args` by the constructor's parameters, reporting unknown, +/// repeated and missing names. +fn order_named_args<'a>( + fields: &'a [(Ident, Expr)], + params: &[(String, ScSpecTypeDef)], + brace_span: proc_macro2::Span, +) -> syn::Result> { + let expected = || args_suggestion(params); + for (i, (name, _)) in fields.iter().enumerate() { + if !params.iter().any(|(p, _)| name == p) { + return Err(syn::Error::new( + name.span(), + format!( + "the constructor has no parameter `{name}`; expected {}", + expected() + ), + )); + } + if fields[..i].iter().any(|(earlier, _)| earlier == name) { + return Err(syn::Error::new( + name.span(), + format!("`{name}` given more than once"), + )); + } + } + params + .iter() + .map(|(p, _)| { + fields + .iter() + .find(|(name, _)| name == p) + .map(|(_, value)| value) + .ok_or_else(|| { + syn::Error::new( + brace_span, + format!( + "missing constructor argument `{p}`; expected {}", + expected() + ), + ) + }) + }) + .collect() +} + +/// Constructor args for the imported Wasm, as a typed call to the +/// `Args::__constructor` that `contractimport!` generates, so rustc checks +/// each value against the constructor's parameter types. +fn wasm_args( + input: &RegisterInput, + mod_ident: &Ident, + params: Option<&[(String, ScSpecTypeDef)]>, +) -> syn::Result { + let name = input.name.raw(); + let values: Vec<&Expr> = match (&input.args, params) { + (None, None | Some([])) => return Ok(quote!(())), + (None, Some(params)) => { + return Err(syn::Error::new( + input.name.span(), + format!( + "`{name}`'s constructor takes arguments. Pass them with `args`:\n\n \ + register_import!(env, {name}, {})", + args_suggestion(params) + ), + )); + } + (Some(args), None) => { + let span = match args { + Args::Named { brace, .. } => brace.span.join(), + Args::Positional(e) => syn::spanned::Spanned::span(e), + }; + return Err(syn::Error::new( + span, + format!("`{name}` has no constructor, so it takes no `args`"), + )); + } + (Some(Args::Named { brace, fields }), Some(params)) => { + order_named_args(fields, params, brace.span.join())? + } + (Some(Args::Positional(Expr::Tuple(tuple))), Some(_)) => tuple.elems.iter().collect(), + (Some(Args::Positional(other)), Some(_)) => return Ok(quote!(#other)), + }; + // Spanned per value, so a type mismatch points at the value, not the macro. + let values = values + .iter() + .map(|v| quote::quote_spanned!(syn::spanned::Spanned::span(v)=> &(#v))); + Ok(quote!(#mod_ident::Args::__constructor(#(#values),*))) +} + +/// Emit a block expression registering a contract at the address +/// `import_contract!` baked in: the imported Wasm, or `contract` (a native +/// mock) when given. Evaluates to the `Address`. +fn expand_register( + input: &RegisterInput, + resolved: &Resolved, + params: Option<&[(String, ScSpecTypeDef)]>, +) -> syn::Result { + let (mod_ident, wasm_path, address) = match resolved { + Resolved::Wasm { + mod_ident, + wasm_path, + address, + } => (mod_ident, wasm_path, address), + Resolved::Sac { .. } | Resolved::Asset { .. } => { + return Err(syn::Error::new( + input.name.span(), + format!( + "register_import! does not support Stellar Asset Contracts yet, and `{}` \ + resolves to one. SAC support is coming; until then, register a test asset \ + yourself with `Env::register_stellar_asset_contract_v2`.", + input.name.raw() + ), + )); + } + }; + let env = &input.env; + let track = track_network_env(); + let (module, contract, args) = if let Some(contract) = &input.contract { + // A native mock has no spec to name its parameters by. + let args = match &input.args { + None => quote!(()), + Some(Args::Positional(args)) => quote!(#args), + Some(Args::Named { brace, .. }) => { + return Err(syn::Error::new( + brace.span.join(), + "named `args` need the imported Wasm's constructor; with `contract = ..`, \ + pass the mock's constructor arguments as a tuple, `args = (..)`", + )); + } + }; + (quote!(), quote!(#contract), args) + } else { + let args = wasm_args(input, mod_ident, params)?; + let wasm_path = wasm_path.to_string_lossy(); + ( + quote! { + #[allow(non_snake_case)] + mod #mod_ident { + #![allow(clippy::ref_option, clippy::too_many_arguments)] + use ::soroban_sdk; + soroban_sdk::contractimport!(file = #wasm_path); + } + }, + quote!(#mod_ident::WASM), + args, + ) + }; + Ok(quote! { + { + #track + #module + let __env: &::soroban_sdk::Env = #env; + ::soroban_sdk::Env::register_at( + __env, + &::soroban_sdk::Address::from_str(__env, #address), + #contract, + #args, + ) + } + }) +} + +pub(crate) fn register_import( + input: proc_macro::TokenStream, +) -> syn::Result { + let input: RegisterInput = syn::parse(input)?; + let resolved = resolve(&input.name, "register_import")?; + // Only the imported Wasm's constructor is read; a mock has no spec. + let params = match (&resolved, &input.contract) { + (Resolved::Wasm { wasm_path, .. }, None) => { + let err = |e: String| syn::Error::new(input.name.span(), e); + let wasm = std::fs::read(wasm_path) + .map_err(|e| err(format!("could not read {}: {e}", wasm_path.display())))?; + constructor_params(&wasm).map_err(err)? + } + _ => None, + }; + expand_register(&input, &resolved, params.as_deref()) +} + +#[cfg(test)] +mod tests { + use super::*; + use proc_macro2::Span; + use quote::quote; + use syn::{Ident, parse2}; + + const A: &str = "CBESJIMX7J53SWJGJ7WQ6QTLJI4S5LPPJNC2BNVD63GIKAYCDTDOO322"; + + fn wasm() -> Resolved { + Resolved::Wasm { + mod_ident: Ident::new("our_dao", Span::call_site()), + wasm_path: "/tmp/target/stellar/local/deployed/our_dao.wasm".into(), + address: A.to_string(), + } + } + + /// `__constructor(admin: Address, fee: u32)`. + fn params() -> Vec<(String, ScSpecTypeDef)> { + vec![ + ("admin".into(), ScSpecTypeDef::Address), + ("fee".into(), ScSpecTypeDef::U32), + ] + } + + fn expand( + tokens: proc_macro2::TokenStream, + resolved: &Resolved, + params: Option<&[(String, ScSpecTypeDef)]>, + ) -> syn::Result { + let input: RegisterInput = parse2(tokens).unwrap(); + expand_register(&input, resolved, params).map(|out| out.to_string()) + } + + fn expanded(tokens: proc_macro2::TokenStream, resolved: &Resolved) -> String { + expand(tokens, resolved, None).unwrap() + } + + fn with_params(tokens: proc_macro2::TokenStream) -> syn::Result { + expand(tokens, &wasm(), Some(¶ms())) + } + + fn parse_err(tokens: proc_macro2::TokenStream) -> String { + parse2::(tokens).err().unwrap().to_string() + } + + #[test] + fn parses_name_only() { + let input: RegisterInput = parse2(quote!(&env, "unverified/our-dao")).unwrap(); + assert_eq!(input.name.raw(), "unverified/our-dao"); + assert!(input.args.is_none()); + assert!(input.contract.is_none()); + } + + #[test] + fn parses_options_in_either_order_with_trailing_comma() { + for tokens in [ + quote!(&env, our_dao, args = (&admin, 7u32), contract = MockDao,), + quote!(&env, our_dao, contract = MockDao, args = (&admin, 7u32)), + ] { + let input: RegisterInput = parse2(tokens).unwrap(); + assert!(input.args.is_some()); + assert!(input.contract.is_some()); + } + } + + #[test] + fn rejects_unknown_and_repeated_options() { + let msg = parse_err(quote!(&env, our_dao, wasm = X)); + assert!(msg.contains("unknown option `wasm`"), "{msg}"); + let msg = parse_err(quote!(&env, our_dao, args = (), args = ())); + assert!(msg.contains("`args` given more than once"), "{msg}"); + // A positional third argument is not accepted. + assert!(parse2::(quote!(&env, our_dao, (admin,))).is_err()); + } + + #[test] + fn registers_the_cached_wasm_at_the_baked_address() { + let out = expanded(quote!(&env, our_dao), &wasm()); + assert!(out.contains("Env :: register_at"), "{out}"); + assert!(out.contains("contractimport !"), "{out}"); + assert!(out.contains("our_dao.wasm"), "{out}"); + assert!( + out.contains("our_dao :: WASM , () ,"), + "no constructor: {out}" + ); + assert!(out.contains(A), "bakes the resolved id: {out}"); + } + + #[test] + fn named_args_are_ordered_by_the_constructor_and_typed() { + // Given out of order, and `admin` in shorthand. + let out = with_params(quote!(&env, our_dao, args = { fee: 7u32, admin })).unwrap(); + assert!( + out.contains("our_dao :: Args :: __constructor (& (admin) , & (7u32))"), + "{out}" + ); + } + + #[test] + fn positional_tuple_args_are_typed_too() { + let out = with_params(quote!(&env, our_dao, args = (&admin, 7u32))).unwrap(); + assert!( + out.contains("our_dao :: Args :: __constructor (& (& admin) , & (7u32))"), + "{out}" + ); + } + + #[test] + fn missing_args_suggest_the_constructor_parameters() { + let msg = with_params(quote!(&env, our_dao)).unwrap_err().to_string(); + assert!(msg.contains("constructor takes arguments"), "{msg}"); + assert!( + msg.contains("register_import!(env, our_dao, args = { admin:
, fee: })"), + "{msg}" + ); + } + + #[test] + fn named_args_report_unknown_repeated_and_missing_names() { + let msg = with_params(quote!(&env, our_dao, args = { owner: a, fee: 1u32 })) + .unwrap_err() + .to_string(); + assert!(msg.contains("no parameter `owner`"), "{msg}"); + let msg = with_params(quote!(&env, our_dao, args = { admin, admin, fee: 1u32 })) + .unwrap_err() + .to_string(); + assert!(msg.contains("`admin` given more than once"), "{msg}"); + let msg = with_params(quote!(&env, our_dao, args = { admin })) + .unwrap_err() + .to_string(); + assert!(msg.contains("missing constructor argument `fee`"), "{msg}"); + } + + #[test] + fn args_without_a_constructor_are_rejected() { + let msg = expand(quote!(&env, our_dao, args = { admin }), &wasm(), None) + .unwrap_err() + .to_string(); + assert!(msg.contains("has no constructor"), "{msg}"); + } + + #[test] + fn named_args_are_rejected_for_a_mock() { + let msg = expand( + quote!(&env, our_dao, contract = MockDao, args = { admin }), + &wasm(), + None, + ) + .unwrap_err() + .to_string(); + assert!(msg.contains("as a tuple"), "{msg}"); + let out = expanded( + quote!(&env, our_dao, contract = MockDao, args = (&admin,)), + &wasm(), + ); + assert!(out.contains("MockDao , (& admin ,)"), "{out}"); + } + + #[test] + fn spec_types_are_named_as_rust_types() { + use stellar_xdr::{ScSpecTypeBytesN, ScSpecTypeVec}; + assert_eq!(type_name(&ScSpecTypeDef::I128), "i128"); + assert_eq!( + type_name(&ScSpecTypeDef::BytesN(ScSpecTypeBytesN { n: 32 })), + "BytesN<32>" + ); + assert_eq!( + type_name(&ScSpecTypeDef::Vec(Box::new(ScSpecTypeVec { + element_type: Box::new(ScSpecTypeDef::Address), + }))), + "Vec
" + ); + } + + #[test] + fn contract_option_replaces_the_wasm() { + let out = expanded(quote!(&env, our_dao, contract = mocks::MockDao), &wasm()); + assert!(out.contains("mocks :: MockDao"), "{out}"); + assert!(!out.contains("contractimport"), "{out}"); + assert!(out.contains(A), "still at the baked address: {out}"); + } + + #[test] + fn stellar_asset_contracts_are_a_clear_compile_error() { + let sac = Resolved::Sac { + address: A.to_string(), + }; + for tokens in [ + quote!(&env, xlm), + quote!(&env, "circle/usdc", contract = MockToken), + ] { + let input: RegisterInput = parse2(tokens).unwrap(); + let msg = expand_register(&input, &sac, None).unwrap_err().to_string(); + assert!(msg.contains("Stellar Asset Contracts"), "{msg}"); + assert!(msg.contains("SAC support is coming"), "{msg}"); + } + } +} diff --git a/crates/stellar-registry-test/fixtures/contracts/token_caller/.gitignore b/crates/stellar-registry-test/fixtures/contracts/token_caller/.gitignore deleted file mode 100644 index 03314f7..0000000 --- a/crates/stellar-registry-test/fixtures/contracts/token_caller/.gitignore +++ /dev/null @@ -1 +0,0 @@ -Cargo.lock diff --git a/crates/stellar-registry-test/fixtures/contracts/token_caller/Cargo.toml b/crates/stellar-registry-test/fixtures/contracts/token_caller/Cargo.toml deleted file mode 100644 index 70dfc2b..0000000 --- a/crates/stellar-registry-test/fixtures/contracts/token_caller/Cargo.toml +++ /dev/null @@ -1,21 +0,0 @@ -[package] -name = "token_caller" -version = "0.0.0" -edition = "2024" -publish = false - -# Imports `unverified/my-token`, which only resolves on testnet, so it can't -# be a workspace member. Run with `just test-register-import-testnet`. -[workspace] - -[lib] -crate-type = ["cdylib"] -doctest = false - -[dependencies] -soroban-sdk = "28.0.0" -stellar-registry = { path = "../../../../stellar-registry" } - -[dev-dependencies] -soroban-sdk = { version = "28.0.0", features = ["testutils"] } -stellar-registry = { path = "../../../../stellar-registry", features = ["testutils"] } diff --git a/crates/stellar-registry-test/fixtures/contracts/token_caller/src/lib.rs b/crates/stellar-registry-test/fixtures/contracts/token_caller/src/lib.rs deleted file mode 100644 index acf83ef..0000000 --- a/crates/stellar-registry-test/fixtures/contracts/token_caller/src/lib.rs +++ /dev/null @@ -1,36 +0,0 @@ -#![no_std] -//! Imports `unverified/my-token`, a token registered on testnet, so both -//! macros fetch its id and Wasm from testnet. -use soroban_sdk::{Env, String, contract, contractimpl}; - -#[contract] -pub struct Contract; - -#[contractimpl] -impl Contract { - pub fn token_symbol(env: &Env) -> String { - stellar_registry::import_contract!(env, "unverified/my-token").symbol() - } -} - -#[cfg(test)] -mod test { - use super::*; - use soroban_sdk::{Address, testutils::Address as _}; - use stellar_registry::testutils::register_import; - - #[test] - fn reads_the_imported_tokens_symbol() { - let env = Env::default(); - register_import!(&env, "unverified/my-token", args = { - name: String::from_str(&env, "Test Token"), - symbol: String::from_str(&env, "TT"), - decimals: 7u32, - owner: Address::generate(&env), - initial_supply: 1_000i128, - }); - - let client = ContractClient::new(&env, &env.register(Contract, ())); - assert_eq!(client.token_symbol(), String::from_str(&env, "TT")); - } -} diff --git a/crates/stellar-registry/README.md b/crates/stellar-registry/README.md index 67b9c32..f8c2534 100644 --- a/crates/stellar-registry/README.md +++ b/crates/stellar-registry/README.md @@ -34,12 +34,12 @@ Enable the `testutils` feature for tests (alongside `soroban-sdk`'s own): ```toml [dependencies] -soroban-sdk = "27" -stellar-registry = "0.1" +soroban-sdk = { workspace = true } +stellar-registry = { workspace = true } [dev-dependencies] -soroban-sdk = { version = "27", features = ["testutils"] } -stellar-registry = { version = "0.1", features = ["testutils"] } +soroban-sdk = { workspace = true, features = ["testutils"] } +stellar-registry = { workspace = true, features = ["testutils"] } ``` Then, given a contract that imports `our_dao`: @@ -77,7 +77,7 @@ Running tests is an ordinary build, so there is nothing to build first. Both mac STELLAR_NETWORK=testnet cargo test ``` -For tests with no network access, such as in CI, set `STELLAR_NO_REGISTRY=1` and put those two files in place before building (from `stellar registry fetch-contract-id` and `stellar contract fetch`, or from copies you commit). The [`hello_caller` fixture](https://github.com/stellar-registry/cli/tree/main/crates/stellar-registry-test/fixtures/contracts/hello_caller) does this, run by `just test-register-import`. +For tests with no network access, such as in CI, set `STELLAR_NO_REGISTRY=1` and put those two files in place before building (from `stellar registry fetch-contract-id` and `stellar contract fetch`, or from copies you commit). The [`hello_caller` fixture](https://github.com/stellar-registry/cli/tree/main/crates/stellar-registry-test/fixtures/contracts/hello_caller) does this, run by [`just test-register-import`](https://github.com/stellar-registry/cli/tree/main/justfile) Stellar Asset Contracts (`xlm`, `"CODE:ISSUER"`, or a registered name that resolves to a SAC) are not supported by `register_import!` yet and give a compile error. Until they are, register a test asset with `Env::register_stellar_asset_contract_v2`. diff --git a/justfile b/justfile index 3fe32f8..f0017b6 100644 --- a/justfile +++ b/justfile @@ -62,10 +62,6 @@ test-register-import: echo CBESJIMX7J53SWJGJ7WQ6QTLJI4S5LPPJNC2BNVD63GIKAYCDTDOO322 > $deployed/hello_v1.id STELLAR_NO_REGISTRY=1 STELLAR_NETWORK=local cargo test --manifest-path $fixture/Cargo.toml -# Test the token_caller fixture against testnet -test-register-import-testnet: build - STELLAR_NETWORK=testnet cargo test --manifest-path crates/stellar-registry-test/fixtures/contracts/token_caller/Cargo.toml - clippy *args: cargo clippy --all {{ args }} \ -- -Dclippy::pedantic -Aclippy::must_use_candidate -Aclippy::missing_errors_doc -Aclippy::missing_panics_doc From 8305ee86a76c66a1f8a1606a0ff96586d6a8fd00 Mon Sep 17 00:00:00 2001 From: pselle Date: Thu, 5 Nov 2026 11:34:52 -0500 Subject: [PATCH 18/20] docs: tidy register_import! docs and messages Drop the shorthand note from the README, add a missing period, keep type-checked on one line in the macro docs, list the named `args` form in the unknown-option error, and list register_import! in CLAUDE.md. Co-Authored-By: Claude --- CLAUDE.md | 2 +- crates/stellar-registry-macro/src/lib.rs | 4 ++-- crates/stellar-registry-macro/src/register.rs | 2 +- crates/stellar-registry/README.md | 4 ++-- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/CLAUDE.md b/CLAUDE.md index 4558330..fc40001 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -41,7 +41,7 @@ Note: the `justfile` still carries some recipes from the monorepo. Prefer the `c |-------|---------| | `stellar-registry-cli` | The `stellar registry` CLI: `publish`, `deploy`, `download`, `install`/`create-alias`, `upgrade`, `register-contract`, and `account` (`account register`, `account preauthorize`, ...) | | `stellar-registry-build` | Library for interacting with the registry at build time | -| `stellar-registry-macro` | Macro crate defining procedural macros `import_contract!`, `import_contract_client!`, and `import_asset`. Proc-macro crates are special and need to only export proc-macros. | +| `stellar-registry-macro` | Macro crate defining procedural macros `import_contract!`, `import_contract_client!`, `import_asset`, and the test helper `register_import!`. Proc-macro crates are special and need to only export proc-macros. | | `stellar-registry` | Re-exports the `import_contract!`, `import_contract_client!`, and `import_asset!` macros from `stellar-registry-macro`. Its `testutils` feature adds `testutils::register_import!` for testing contracts that use `import_contract!`. Might export more behavior later. (published to crates.io; dev-dependency of `stellar-registry/contracts`) | | `stellar-registry-name` | Defines standard name parsing/formatting used by `stellar-registry-macro` and `stellar-registry-cli` | | `stellar-registry-test` | Unpublished testing tools used throughout this monorepo as a dev-dependency | diff --git a/crates/stellar-registry-macro/src/lib.rs b/crates/stellar-registry-macro/src/lib.rs index 12ffe77..46fd455 100644 --- a/crates/stellar-registry-macro/src/lib.rs +++ b/crates/stellar-registry-macro/src/lib.rs @@ -102,8 +102,8 @@ pub fn import_contract(input: TokenStream) -> TokenStream { /// address and Wasm (same cache files, same `STELLAR_NO_REGISTRY` /// handling, same flagged-contract check). /// - `args = { name: value, .. }` passes constructor arguments by parameter -/// name, read from the imported Wasm's constructor. Values are type- -/// checked against the constructor. Leaving out `args` when the +/// name, read from the imported Wasm's constructor. Values are +/// type-checked against the constructor. Leaving out `args` when the /// constructor takes parameters is a compile error that lists them. A /// positional tuple, `args = (..)`, also works. /// - `contract = ` registers a native `#[contract]` type instead of the diff --git a/crates/stellar-registry-macro/src/register.rs b/crates/stellar-registry-macro/src/register.rs index 063b41d..14c979d 100644 --- a/crates/stellar-registry-macro/src/register.rs +++ b/crates/stellar-registry-macro/src/register.rs @@ -76,7 +76,7 @@ impl Parse for RegisterInput { return Err(syn::Error::new( key.span(), format!( - "unknown option `{other}`: expected `args = (..)` or `contract = ..`" + "unknown option `{other}`: expected `args = {{ .. }}`, `args = (..)` or `contract = ..`" ), )); } diff --git a/crates/stellar-registry/README.md b/crates/stellar-registry/README.md index f8c2534..0026fc8 100644 --- a/crates/stellar-registry/README.md +++ b/crates/stellar-registry/README.md @@ -68,7 +68,7 @@ fn proposes() { Two options, in either order: -- `args = { .. }` passes constructor arguments by name: `register_import!(&env, our_dao, args = { admin, quorum: 7u32 })`. The names and types come from the imported contract's constructor, so a misspelled name or wrong type is a compile error, and leaving `args` out when the constructor needs some fails with the list to fill in, e.g. `args = { admin:
, quorum: }`. `{ admin }` is shorthand for `{ admin: admin }`; values are borrowed, not moved. A positional tuple, `args = (&admin, 7u32)`, also works. +- `args = { .. }` passes constructor arguments by name: `register_import!(&env, our_dao, args = { admin, quorum: 7u32 })`. The names and types come from the imported contract's constructor, so a misspelled name or wrong type is a compile error, and leaving `args` out when the constructor needs some fails with the list to fill in, e.g. `args = { admin:
, quorum: }`. A positional tuple, `args = (&admin, 7u32)`, also works. - `contract = ..` registers a native contract in place of the Wasm, when you want to control what the dependency returns: `register_import!(&env, our_dao, contract = MockDao)`. `MockDao` is an ordinary `#[contract]` type with the functions your contract calls; pass its constructor arguments as a tuple. Running tests is an ordinary build, so there is nothing to build first. Both macros resolve the name at compile time on the network the build uses, and share the cached id and Wasm (`target/stellar//deployed/.id` and `.wasm`). Run tests on a network where the name is registered: @@ -77,7 +77,7 @@ Running tests is an ordinary build, so there is nothing to build first. Both mac STELLAR_NETWORK=testnet cargo test ``` -For tests with no network access, such as in CI, set `STELLAR_NO_REGISTRY=1` and put those two files in place before building (from `stellar registry fetch-contract-id` and `stellar contract fetch`, or from copies you commit). The [`hello_caller` fixture](https://github.com/stellar-registry/cli/tree/main/crates/stellar-registry-test/fixtures/contracts/hello_caller) does this, run by [`just test-register-import`](https://github.com/stellar-registry/cli/tree/main/justfile) +For tests with no network access, such as in CI, set `STELLAR_NO_REGISTRY=1` and put those two files in place before building (from `stellar registry fetch-contract-id` and `stellar contract fetch`, or from copies you commit). The [`hello_caller` fixture](https://github.com/stellar-registry/cli/tree/main/crates/stellar-registry-test/fixtures/contracts/hello_caller) does this, run by [`just test-register-import`](https://github.com/stellar-registry/cli/tree/main/justfile). Stellar Asset Contracts (`xlm`, `"CODE:ISSUER"`, or a registered name that resolves to a SAC) are not supported by `register_import!` yet and give a compile error. Until they are, register a test asset with `Env::register_stellar_asset_contract_v2`. From a78263674faef22d3561d00f423444d056bf80fc Mon Sep 17 00:00:00 2001 From: pselle Date: Thu, 5 Nov 2026 11:36:05 -0500 Subject: [PATCH 19/20] chore: update hello_caller's lockfile to match main Co-Authored-By: Claude --- .../fixtures/contracts/hello_caller/Cargo.lock | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/crates/stellar-registry-test/fixtures/contracts/hello_caller/Cargo.lock b/crates/stellar-registry-test/fixtures/contracts/hello_caller/Cargo.lock index 30dec20..a534417 100644 --- a/crates/stellar-registry-test/fixtures/contracts/hello_caller/Cargo.lock +++ b/crates/stellar-registry-test/fixtures/contracts/hello_caller/Cargo.lock @@ -1796,9 +1796,9 @@ checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" [[package]] name = "stellar-build" -version = "0.0.6" +version = "0.0.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "97fd5e3be902a9658fb9f642d4eef7837ab1d51e52dde9be1d9b218326499d97" +checksum = "915a3455d5ef6013227e3ba69e7fd966c0830c4620f84c06b1dc684cae3a9465" dependencies = [ "cargo_metadata", "sha2", @@ -1808,14 +1808,14 @@ dependencies = [ [[package]] name = "stellar-registry" -version = "0.1.2" +version = "0.1.3" dependencies = [ "stellar-registry-macro", ] [[package]] name = "stellar-registry-macro" -version = "0.1.2" +version = "0.1.3" dependencies = [ "proc-macro2", "quote", From 9ee15b131bc9e24571ac726071377ce86b203a09 Mon Sep 17 00:00:00 2001 From: pselle Date: Thu, 5 Nov 2026 11:38:06 -0500 Subject: [PATCH 20/20] docs: link the justfile with blob/, and write import_asset! in CLAUDE.md Co-Authored-By: Claude --- CLAUDE.md | 2 +- crates/stellar-registry/README.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/CLAUDE.md b/CLAUDE.md index fc40001..1eebd8b 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -41,7 +41,7 @@ Note: the `justfile` still carries some recipes from the monorepo. Prefer the `c |-------|---------| | `stellar-registry-cli` | The `stellar registry` CLI: `publish`, `deploy`, `download`, `install`/`create-alias`, `upgrade`, `register-contract`, and `account` (`account register`, `account preauthorize`, ...) | | `stellar-registry-build` | Library for interacting with the registry at build time | -| `stellar-registry-macro` | Macro crate defining procedural macros `import_contract!`, `import_contract_client!`, `import_asset`, and the test helper `register_import!`. Proc-macro crates are special and need to only export proc-macros. | +| `stellar-registry-macro` | Macro crate defining procedural macros `import_contract!`, `import_contract_client!`, `import_asset!`, and the test helper `register_import!`. Proc-macro crates are special and need to only export proc-macros. | | `stellar-registry` | Re-exports the `import_contract!`, `import_contract_client!`, and `import_asset!` macros from `stellar-registry-macro`. Its `testutils` feature adds `testutils::register_import!` for testing contracts that use `import_contract!`. Might export more behavior later. (published to crates.io; dev-dependency of `stellar-registry/contracts`) | | `stellar-registry-name` | Defines standard name parsing/formatting used by `stellar-registry-macro` and `stellar-registry-cli` | | `stellar-registry-test` | Unpublished testing tools used throughout this monorepo as a dev-dependency | diff --git a/crates/stellar-registry/README.md b/crates/stellar-registry/README.md index 0026fc8..884931c 100644 --- a/crates/stellar-registry/README.md +++ b/crates/stellar-registry/README.md @@ -77,7 +77,7 @@ Running tests is an ordinary build, so there is nothing to build first. Both mac STELLAR_NETWORK=testnet cargo test ``` -For tests with no network access, such as in CI, set `STELLAR_NO_REGISTRY=1` and put those two files in place before building (from `stellar registry fetch-contract-id` and `stellar contract fetch`, or from copies you commit). The [`hello_caller` fixture](https://github.com/stellar-registry/cli/tree/main/crates/stellar-registry-test/fixtures/contracts/hello_caller) does this, run by [`just test-register-import`](https://github.com/stellar-registry/cli/tree/main/justfile). +For tests with no network access, such as in CI, set `STELLAR_NO_REGISTRY=1` and put those two files in place before building (from `stellar registry fetch-contract-id` and `stellar contract fetch`, or from copies you commit). The [`hello_caller` fixture](https://github.com/stellar-registry/cli/tree/main/crates/stellar-registry-test/fixtures/contracts/hello_caller) does this, run by [`just test-register-import`](https://github.com/stellar-registry/cli/blob/main/justfile). Stellar Asset Contracts (`xlm`, `"CODE:ISSUER"`, or a registered name that resolves to a SAC) are not supported by `register_import!` yet and give a compile error. Until they are, register a test asset with `Env::register_stellar_asset_contract_v2`.