Importable registry #[contracttrait]s + StatelessDeployable (registry-traits crate) - #33
Draft
willemneal wants to merge 3 commits into
Draft
Importable registry #[contracttrait]s + StatelessDeployable (registry-traits crate)#33willemneal wants to merge 3 commits into
willemneal wants to merge 3 commits into
Conversation
Decouple the registry's public #[contracttrait] default bodies from the
concrete `crate::Contract` so a downstream contract can
`impl Deployable for MyContract {}` and reuse the exact logic
(admin-sep style). The former inherent `impl Contract` helpers move onto
a neutral `RegistryHelpers` zero-sized type — contract-agnostic, all
state behind the fixed storage-key convention; admin routing now goes
through `Self::require_admin` (`Batchable: AdministratableExtension`).
Expose an `rlib` target so the traits are importable at all.
Add `StatelessDeployable::deploy_stateless`: content-addressed deploy
(salt = wasm_hash, init = () always), idempotent — returns the existing
instance if the derived address already holds an executable rather than
trapping with AlreadyDeployed. Folds in the old #10 `deploy_immutable`.
Wired with one line in `registry/src/lib.rs`.
Prove external importability with a compile-only `registry-consumer`
crate that implements the whole trait set on a foreign contract type.
Tests (new `immutable_hello` zero-arg-ctor fixture): salt = wasm hash,
returned id equals the offline-derived address, a second call is a
no-op success, and no init args are ever passed (an arg-requiring
constructor traps).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01AVMqraqa1ERu7HATfbNiEj
Move the importable #[contracttrait]s + storage/name/version/events/error into a new `registry-traits` rlib that depends ONLY on soroban-sdk — never admin-sep or soroban-sdk-tools, both of which pin sdk ^25 and would block a soroban-sdk 26 consumer (perch). This is what perch will `use`. - error.rs: `scerr` -> hand-written `#[contracterror]`, same discriminants (1..=21) and same exported spec, so the wire ABI is unchanged. - storage.rs: soroban-sdk-tools `InstanceItem` -> plain instance storage at the identical `ROOT_REG` key. - admin: `Batchable`'s auth no longer needs admin-sep's `AdministratableExtension`; it requires the admin stored at the same `ADMIN` instance key via a tiny in-crate `admin` module (storage-key convention), so downstream contracts reuse the traits with no admin trait. soroban-sdk pin is `>=25.3.1, <27`: the source compiles under 25/26/27, but cargo won't unify a range spanning two majors, so the ceiling sits at the consumer's major + 1 (perch = 26 -> `<27`). In this workspace admin-sep's ^25 keeps everything on 25.3.x. The `registry` cdylib now depends on `registry-traits` and re-exports its modules; it keeps admin-sep only for its own Administratable/Upgradable entry points. `registry-consumer` now depends solely on registry-traits, mirroring perch. Verified: contracts workspace stays on sdk 25.3.1 with all tests green; a standalone consumer pinning soroban-sdk 26 resolves to a single sdk 26 and compiles the whole trait set (27 too). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01AVMqraqa1ERu7HATfbNiEj
…on 27.0.2) Perch — the downstream consumer — pins soroban-sdk 27.0.2, so a <27 ceiling forced a two-major split (perch 27 + registry-traits 26) that cargo won't unify, blocking perch#39. <28 still admits the contracts workspace's 25.3.x (admin-sep-pinned), so both consumers resolve to a single soroban-sdk. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01AVMqraqa1ERu7HATfbNiEj
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Implements stellar-registry/perch#38 — part of stellar-registry/perch#37; supersedes #10.
What this does
Makes the registry's
#[contracttrait]s importable by a downstream contract on a different soroban-sdk, and adds a content-addressedStatelessDeployabledeploy.1. Importable traits (admin-sep style)
The public
#[contracttrait]default bodies no longer hard-reference the concretecrate::Contract/crate::storage. Helpers moved onto a neutral zero-sizedRegistryHelpersnamespace; everything is env/storage-key based, so the traits type-check against a foreignSelf. Admin auth routes through a fixedADMINstorage-key convention (no admin trait required downstream). The registry crate gained anrlibtarget.2. New
registry-traitscrate (the thing perch depends on)The importable traits +
storage/name/version/events/errorwere extracted intocontracts/registry-traits(rlib). It depends only onsoroban-sdk— neveradmin-seporsoroban-sdk-tools, both of which pin sdk^25and would block a sdk-26 consumer. To get there:error.rs:soroban-sdk-tools::scerr→ hand-written#[contracterror](identical discriminants1..=21, same exported spec → unchanged wire ABI).storage.rs:soroban-sdk-tools::InstanceItem→ plain instance storage at the sameROOT_REGkey.admin.rs: tiny in-crate admin (reads/require-auths theADMINinstance key, byte-identical to admin-sep's) replaces theAdministratableExtensionbound onBatchable.The
registrycdylib now depends onregistry-traitsand re-exports its modules; it keepsadmin-seponly for its ownAdministratable/Upgradableentry points (sdk 25).soroban-sdk pin is
>=25.3.1, <27. The source compiles clean under 25, 26 and 27 (all verified), but cargo won't unify a range spanning two majors — a<28range makes a sdk-26 consumer resolve two soroban-sdk versions. So the ceiling sits at the consumer's major + 1. Perch is on soroban-sdk 26 →<27. When perch moves to 27, bump the pin to<28.3.
StatelessDeployable::deploy_stateless(folds in #10)salt = wasm_hash,init = ()always → the deployed address is a pure function of(deployer, wasm_hash); a wasm whose__constructorneeds args just traps (self-enforcing deployer-independence, no ABI introspection). Idempotent: if the derived address already holds an executable it returns it instead of trapping withAlreadyDeployed. Wired with one line inregistry/src/lib.rs. A caller-supplieddeployermust authorize at the root of the call; defaulting to the registry needs no external auth.Proof / tests
contracts/test/registry-consumer(compile-only) implements the full trait set on a foreign contract type, depending only onregistry-traits(exactly as perch will).soroban-sdk = "26.0.1"resolves to a single sdk 26 and compilesimpl Deployable/StatelessDeployable/… for Probe {}.deploy_stateless(registry/src/test/stateless.rs, new zero-arg-ctor fixtureimmutable_hello): salt == wasm hash; returned id equals the offline-derivedwith_address(registry, hash).deployed_address(); a second call is a no-op success (also viatry_); an arg-requiring constructor traps (proving no init args are ever passed); explicit deployer honored + idempotent.Build/test status
just build(stellar scaffold build) — OK; contracts workspace stays on soroban-sdk 25.3.1.cargo test --workspace— 48 pass, 0 fail (46 registry + 1 registry-traits + 1 tansu-manager).just clippyandjust clippy-test(pedantic,-Dwarnings) — clean.cargo fmt --all --check— clean.For the perch side
Depend on this crate at this PR's git rev:
and in the stateless registry contract:
Done vs TODO
Done
#[contracttrait]s importable from an external crate (proven byregistry-consumer); registry contract still builds + passes its suite.registry-traits) compiles under soroban-sdk 26 (perch) — and 25/27.StatelessDeployable::deploy_stateless+ tests (salt = wasm hash; offline-derived address match; idempotent no-op; no init args).TODO / follow-ups
StatelessDeployablehere.registry-traitsat this rev.registry-traits'soroban-sdkpin (and dev-dep) from<27to<28(source already builds under 27).deploy_statelessis intentionally permissionless (any published wasm, content-addressed, no manager gate). If a managed subregistry wants to gate it, gate at the perch layer.🤖 Generated with Claude Code
https://claude.ai/code/session_01AVMqraqa1ERu7HATfbNiEj