Skip to content

chain: hoist ChainMethod into a shared leaf crate #537

Description

@mfw78

From the chain-seam review (2026-07-22), spun out of the #453 alloy-chains substitution and sibling to the nullislabs/nexum-runtime#52 / nullislabs/nexum-runtime#51 error-typing cleanup.

The permitted JSON-RPC read-surface allowlist ChainMethod is defined twice, byte-identical, and kept in sync by hand:

  • guest: crates/nexum-sdk/src/chain/method.rs
  • host: crates/nexum-runtime/src/host/component/chain.rs

Both carry the same 19 variants in the same order with the same #[strum(serialize = "...")] wire strings and the same as_str(); the only difference is the guest also derives Hash. Nothing enforces that they stay equal. Add or rename a read method on one side and forget the other and they drift silently, with no compile error, because the WIT edge between them is a plain String. This is a security-relevant allowlist (it exists so signing and mutating methods cannot be represented and never cross the seam), so silent drift is exactly the failure we do not want.

Unlike the Chain newtypes retired in #453, this is not an alloy duplication - alloy has no method-name enum, it passes bare &'static str literals - so the fix is de-duplication, not substitution.

Scope

One allowlist, two definitions, three consumer sites:

role definition consumers
guest nexum-sdk/src/chain/method.rs nexum-sdk/src/chain/transport.rs (try_from, as_str), re-exported at chain::ChainMethod
host nexum-runtime/src/host/component/chain.rs nexum-runtime/src/host/provider_pool.rs; videre-host tests (zero_leak.rs, platform.rs) via nexum_runtime::host::component::ChainMethod

Approach

Introduce a shared leaf crate nexum-chain-method (model it on the existing shared guest/host leaf nexum-status-body):

  • Single pub enum ChainMethod with the derive superset Debug, Clone, Copy, PartialEq, Eq, Hash, EnumString, IntoStaticStr and the as_str(self) -> &'static str inherent method.
  • Sole dependency strum (already in both consumers' graphs). The enum needs no allocation, so make the crate #![no_std] - future-proof and zero cost.
  • nexum-sdk and nexum-runtime both depend on it; delete the two hand-rolled copies.
  • Preserve the existing public paths so consumers are untouched: guest re-exports it at chain::ChainMethod (already does), host re-exports it at host::component::ChainMethod so nexum_runtime::host::component::ChainMethod still resolves for the videre-host tests.
  • Register the crate in the workspace members list and pin version = "0.1.0".

Both consumers are L1 (guest SDK and host runtime), so the new crate lives entirely within L1. Gate-safe: nexum-chain-method matches none of videre|intent|venue|cow, so the venue-agnostic crate-graph scan stays green. Must build for wasm32-wasip2 (the guest links it); strum is wasm-safe.

Timing

Independent standalone PR. Because both consumers are L1 they ride the M5 L1 carve together, so the three-repo split is not a forcing function here (contrast nullislabs/nexum-runtime#52 / nullislabs/nexum-runtime#51, which span L1 and L2). Landing pre-split is still simpler - one repo, one Cargo.lock - but this can go in any milestone; parked in M8 as maintainability debt.

Acceptance criteria

  • ChainMethod is defined once, in nexum-chain-method; no hand-rolled copy remains in nexum-sdk or nexum-runtime.
  • chain::ChainMethod (guest) and nexum_runtime::host::component::ChainMethod (host) still resolve; no consumer edits beyond the import source.
  • The crate builds for wasm32-wasip2; check-venue-agnostic stays green.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions