feat: import_contract! macro + consolidate registry macros (stellar-scaffold/cli#419) - #17
Conversation
Addresses review on #17 ("stop slopping me bro"): the macro was built by analogy to import_contract_client! and conflated a contract with its wasm. - Drop @Version. A deployed contract has no version (only a wasm does); reject `@` with a clear compile_error!. - Stop delegating codegen to import_contract_client!(name), which resolves a wasm by name and wrongly assumes contract-name == wasm-name. Instead fetch the deployed contract's own on-chain wasm by address (`stellar contract fetch --id`) and inline `soroban_sdk::contractimport!` — so a registered contract whose wasm was never published still works. - Fail compilation if the contract is flagged as compromised (#38, #52). No on-chain getter exists, so read the registry's ContractEntry persistent ledger entry directly via RPC (key (Symbol("CR"), <canonical name>); a 3-element vec == flagged), behind a new `fetch-contract-id --reject-flagged`. - Online builds no longer trust the cached .id, so a contract flagged after the first build can't slip through; the .id/.wasm caches are the offline (STELLAR_NO_REGISTRY=1) fallback, and env override / offline are the only explicit opt-outs of the flag check. - Self-contained rustdoc (no import_contract_client! reference). New: stellar_registry_build::Registry::is_contract_flagged (raw ledger read), fetch_contract_id --reject-flagged. Design/plan docs get a post-review revision note. Verified end to end on testnet: fetch-contract-id --reject-flagged returns the address when unflagged, errors "contract `oz` is flagged as compromised" once flag_contract sets it, and the plain lookup is unchanged. Unit tests + pedantic clippy pass. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Q5b9Nj9oNVLjeRetxCeyxP
chadoh
left a comment
There was a problem hiding this comment.
Better but still a lot to improve here
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Q5b9Nj9oNVLjeRetxCeyxP
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Q5b9Nj9oNVLjeRetxCeyxP
Implement the import_contract! macro helper functions in TDD style: - Create crate skeleton with proc-macro lib type - Wire workspace dependencies (proc-macro2, quote, syn) - Add 6 pure helper functions: mod_name_from, split_version, env_var_name, validate_contract_id, cache_id_path, manifest - Add comprehensive unit tests for all helpers (5 tests, all passing) Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Q5b9Nj9oNVLjeRetxCeyxP
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Q5b9Nj9oNVLjeRetxCeyxP
Add resolve_address (fully injectable for testability) and fetch_contract_id (shell-out to stellar CLI) to implement address resolution precedence: env var > cache > registry lookup. Includes 4 unit tests verifying the resolution order (env override, cache fallback, no-registry error, fetch). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Q5b9Nj9oNVLjeRetxCeyxP
Implement struct Input with syn::parse::Parse trait to parse the macro's input (env expression and contract name as bare ident or string literal). Implement fn expand() to emit a block expression that delegates wasm import to import_contract_client! macro and constructs the client bound to the resolved contract address. Add codegen test module with 3 tests covering parsing and code generation. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Q5b9Nj9oNVLjeRetxCeyxP
…istry Adds the #[proc_macro] entry point that ties together the pure helpers, build-time address resolution, and codegen from the prior tasks, and re-exports it as stellar_registry::import_contract for consumers. This wires every previously-dead helper into the entry point, so no dead_code warnings remain and no #[allow(dead_code)] was needed. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Q5b9Nj9oNVLjeRetxCeyxP
Prevents a compiler panic on names like "foo/" or "@v1.0.0" (empty module identifier) and stops a malformed fetch-contract-id response from poisoning the .id cache. Findings from the final whole-branch review. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Q5b9Nj9oNVLjeRetxCeyxP
Addresses review on #17 ("stop slopping me bro"): the macro was built by analogy to import_contract_client! and conflated a contract with its wasm. - Drop @Version. A deployed contract has no version (only a wasm does); reject `@` with a clear compile_error!. - Stop delegating codegen to import_contract_client!(name), which resolves a wasm by name and wrongly assumes contract-name == wasm-name. Instead fetch the deployed contract's own on-chain wasm by address (`stellar contract fetch --id`) and inline `soroban_sdk::contractimport!` — so a registered contract whose wasm was never published still works. - Fail compilation if the contract is flagged as compromised (#38, #52). No on-chain getter exists, so read the registry's ContractEntry persistent ledger entry directly via RPC (key (Symbol("CR"), <canonical name>); a 3-element vec == flagged), behind a new `fetch-contract-id --reject-flagged`. - Online builds no longer trust the cached .id, so a contract flagged after the first build can't slip through; the .id/.wasm caches are the offline (STELLAR_NO_REGISTRY=1) fallback, and env override / offline are the only explicit opt-outs of the flag check. - Self-contained rustdoc (no import_contract_client! reference). New: stellar_registry_build::Registry::is_contract_flagged (raw ledger read), fetch_contract_id --reject-flagged. Design/plan docs get a post-review revision note. Verified end to end on testnet: fetch-contract-id --reject-flagged returns the address when unflagged, errors "contract `oz` is flagged as compromised" once flag_contract sets it, and the plain lookup is unchanged. Unit tests + pedantic clippy pass. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Q5b9Nj9oNVLjeRetxCeyxP
This builds on the initial PrefixedName type but adds a Versioned type which makes checking for versions on wasm parsable. Also addressed other issues in PR review.
29d0cf1 to
0acb8ae
Compare
import_contract! macro (stellar-scaffold/cli#419)import_contract! macro + consolidate registry macros (stellar-scaffold/cli#419)
chadoh
left a comment
There was a problem hiding this comment.
Didn't have time to give this a full deep-dive today, but from what I saw it's looking great! One comment so far. Happy to have you push back on this idea, but I think it could be nice. See below.
- Remove stale-by-the-time-it-lands parenthetical about needing to be on the latest Registry CLI - Correct occurrences of "contract" when we meant "wasm"
Mirror the `as_deref() == Ok("1")` syntax used by sibling `contract.rs`
Co-authored-by: Chad Ostrowski <221614+chadoh@users.noreply.github.com>
Co-authored-by: Chad Ostrowski <221614+chadoh@users.noreply.github.com>
chadoh
left a comment
There was a problem hiding this comment.
Dude this looks awesome now! I love how much stellar-registry-name cleaned everything up! Thanks for sticking it out with me and adjusting to all my giant architectural change requests.
I have a few small nits & cleanup notes above. After you address those, this looks ready to go!
🎉 🎉 🎊 👏🏼 👏🏼
chadoh
left a comment
There was a problem hiding this comment.
wait actually the docs for stellar_registry crate are stale; let me fix them real quick
chadoh
left a comment
There was a problem hiding this comment.
ok all good let's gooooo (but do address my nits & suggestions above first)
b9eb9c7 to
9896b56
Compare
Can't use the variables directly in the `format!`, need to call `.display()` explicitly. ``` error[E0277]: `std::path::Path` doesn't implement `std::fmt::Display` --> crates/stellar-registry-macro/src/contract.rs:90:88 ``` But in `contract_client.rs` we want to show the full path, not just the `wasm.name()`. Since `Versioned` implements `Display`, we can shorten this to put `wasm` in the `format!`.
`canonical_name` returns the name as it should be formatted _on chain_, which means _no underscores_. It replaces underscores with hyphens. `mod_name` returns a name appropriate for use as a Rust module, which means _no hyphens_. It replaces hyphens with underscores. This also adds a test to ensure that `mod_name` lowercases the input. This shouldn't happen because the canonical on-chain form of the name is not allowed to have capital letters.
9896b56 to
e484fad
Compare
## 🤖 New release * `stellar-registry-name`: 0.1.0 * `stellar-registry-macro`: 0.1.0 * `stellar-registry`: 0.0.11 -> 0.1.0 * `stellar-registry-build`: 0.0.9 -> 0.1.0 * `stellar-registry-cli`: 0.0.22 -> 0.1.0 (✓ API compatible changes) <details><summary><i><b>Changelog</b></i></summary><p> ## `stellar-registry-name` <blockquote> ## [0.0.1](https://github.com/stellar-registry/cli/releases/tag/stellar-registry-name-v0.1.0) - 2026-07-27 ### Added - [**breaking**] `import_contract!` macro + consolidate registry macros (stellar-scaffold/cli#419) ([#17](#17)) </blockquote> ## `stellar-registry-macro` <blockquote> ## [0.0.1](https://github.com/stellar-registry/cli/releases/tag/stellar-registry-macro-v0.1.0) - 2026-07-27 ### Added - [**breaking**] `import_contract!` macro + consolidate registry macros (stellar-scaffold/cli#419) ([#17](#17)) </blockquote> ## `stellar-registry` <blockquote> ## [0.0.12](stellar-registry-v0.0.11...stellar-registry-v0.1.0) - 2026-07-27 ### Added - [**breaking**] `import_contract!` macro + consolidate registry macros (stellar-scaffold/cli#419) ([#17](#17)) </blockquote> ## `stellar-registry-build` <blockquote> ## [0.0.10](stellar-registry-build-v0.0.9...stellar-registry-build-v0.1.0) - 2026-07-27 ### Added - [**breaking**] `import_contract!` macro + consolidate registry macros (stellar-scaffold/cli#419) ([#17](#17)) </blockquote> ## `stellar-registry-cli` <blockquote> ## [0.0.23](stellar-registry-cli-v0.0.22...stellar-registry-cli-v0.1.0) - 2026-07-27 ### Added - [**breaking**] `import_contract!` macro + consolidate registry macros (stellar-scaffold/cli#419) ([#17](#17)) </blockquote> </p></details> --- This PR was generated with [release-plz](https://github.com/release-plz/release-plz/). --------- Co-authored-by: aha-release-plz[bot] <211513279+aha-release-plz[bot]@users.noreply.github.com> Co-authored-by: Chad Ostrowski <221614+chadoh@users.noreply.github.com>
Implements
import_contract!(stellar-scaffold/cli#419) and consolidates all registry macros into this repo:import_contract!,import_contract_client!, andimport_asset!now live incrates/stellar-registry-macroand are re-exported fromstellar-registry. Thestellar-scaffold-macrodependency is gone.What
stellar_registry::import_contract!(env, name)returns a type-safe sorobanClientalready bound to the named contract's deployed on-chain address, resolved at build time:Unlike
import_contract_client!(which imports a published wasm, optionally@version),import_contract!imports a deployed contract: it has no version, and its client types are generated from the deployed instance's own wasm (stellar contract fetch --id …), so contracts whose wasm was never published to the registry still work.Parse, don't validate
Names are now typed in
stellar-registry-build::nameand parsing is the only way to construct them:Prefixed—nameorchannel/name. Rejects empty names,@(deployed contracts have no version), multiple slashes, and invalid characters. Private fields;name()/channel()/mod_name()/canonical_name()accessors.Versioned—Prefixed+ optional@version(leadingvtolerated). A malformed version is an error, never silently "latest" (the old code dropped@v1.0.0on the floor).import_contract!parsesPrefixed,import_contract_client!parsesVersioned— so "contracts have no version" is enforced by the type, not a string check. The same types back the CLI's clap args (14 commands), so bad names fail at arg parsing with real messages.Address resolution & caching
stellar registry fetch-contract-idevery build — a cached id is deliberately ignored so a contract flagged after the first build can't slip through. Result cached attarget/stellar/<network>/deployed/[<channel>__]<mod_name>.id, wasm beside it. The wasm is refetched when the resolved address changes (redeploy under the same name).deployed/namespace + channel-qualified stems mean the cache can never collide with registry-downloaded wasms, workspace-built wasms, or the same name on a different channel.STELLAR_NO_REGISTRY=1forbids network calls and requires the cached id + wasm.STELLAR_CONTRACT_ID_<NAME>override is removed (per review).Flagged contracts (breaking)
Registry::fetch_contract_id(build crate) now rejects flagged contracts by default;fetch_contract_id_uncheckedis the explicit opt-out.stellar registry fetch-contract-iderrors on flagged contracts unless--force;create-aliashonors its existing--forcethe same way.import_contract!fails compilation on a flagged contract (with an up-to-date plugin).Errors now say which failure occurred (CLI missing vs plugin missing vs plugin too old vs flagged vs not found, with the exact stderr) and link the registry explorer on testnet/mainnet.
Dependency footprint
stellar-registry-macrois re-exported fromstellar-registry, which user contracts depend on — so it must stay light.stellar-registry-buildnow gates everything network-facing behind the defaultclifeature; withdefault-features = false(what the macro uses) its tree is semver + thiserror, not the stellar-cli stack.Removed
docs/superpowers/(per review)named_registry.rs(superseded byname::Prefixed)stellar-scaffold-macrodependency🤖 Generated with Claude Code