chore: withMultichain#17
Conversation
|
@codex review |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: ccebfb775a
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| for (const handle of owned.reverse()) { | ||
| await stopRuntime(handle); | ||
| } |
There was a problem hiding this comment.
Continue stopping runtimes after teardown errors
The finally cleanup awaits each stopRuntime call inline, so if stopping one owned runtime rejects, the loop aborts and later runtimes are never stopped. In multi-chain scenarios this can leave anvil processes running and pollute subsequent tests (port conflicts/state leakage). Cleanup should attempt all handles and then surface aggregated errors instead of failing fast on the first stop failure.
Useful? React with 👍 / 👎.
| /** | ||
| * Context after {@link withFundedWallet}: the targeted chain entry includes `wallet`. Prefer reading `ctx.chains[chain].wallet`. | ||
| */ | ||
| export type ScenarioFundedWalletContext = ScenarioRuntimeClientsContext; |
There was a problem hiding this comment.
Restore funded-wallet type narrowing
ScenarioFundedWalletContext is now just an alias of ScenarioRuntimeClientsContext, which removes the compile-time guarantee that a wallet-producing fixture ran. As a result, chains like scenario(withChain(), withErc20Balance({ token, amount }), ...) now type-check but fail at runtime when withErc20Balance cannot find a recipient wallet. This regresses fixture-order safety that previously prevented these mistakes before execution.
Useful? React with 👍 / 👎.
There was a problem hiding this comment.
Pull request overview
This PR introduces first-class multi-chain scenario support by refactoring scenario runtime state into a namespaced ctx.chains map and adding a new withMultiChain runtime fixture. It updates all runtime/wallet/token/contract/isolation fixtures, tests, examples, and documentation to use chain-scoped context.
Changes:
- Add
withMultiChainto start/attach multiple runtimes and expose them underctx.chains.<key>. - Refactor existing fixtures (
withChain,withFork,withExternalRuntime,withSnapshot, wallet/token/contract fixtures, bundler) to read/write chain-scoped state. - Update examples + docs to the new
ctx.chainsmodel and expand tests for multi-chain behavior.
Reviewed changes
Copilot reviewed 48 out of 48 changed files in this pull request and generated 7 comments.
Show a summary per file
| File | Description |
|---|---|
vocs.config.ts |
Adds docs nav link for the new withMultiChain fixture. |
packages/examples/examples/scenarios.test.ts |
Migrates examples to ctx.chains and adds multi-chain scenario coverage. |
packages/core/src/scenarios/utils.ts |
Replaces runtime-client assertions with requireChainScopedRuntimeClients. |
packages/core/src/scenarios/types.ts |
Introduces ScenarioChainContext and moves runtime state under ScenarioContext.chains. |
packages/core/src/scenarios/scenario-typing.test.ts |
Adds typing test for withMultiChain and updates imports. |
packages/core/src/scenarios/scenario-invalid-chain.typespec.ts |
Removes the previous compile-only invalid-order typespec. |
packages/core/src/scenarios/requireContext.test.ts |
Updates requireContext test to the new chains key. |
packages/core/src/scenarios/index.ts |
Exports withMultiChain + new types; adjusts exported type surface. |
packages/core/src/scenarios/fixtures/withSnapshot.ts |
Adds chain targeting to snapshots via ctx.chains[chain]. |
packages/core/src/scenarios/fixtures/withSnapshot.test.ts |
Updates snapshot tests to chain-scoped context. |
packages/core/src/scenarios/fixtures/withMultiChain.ts |
New fixture to start/attach multiple runtimes and manage lifecycle. |
packages/core/src/scenarios/fixtures/withMultiChain.test.ts |
Adds unit tests for multi-chain startup/teardown + duplicate key handling. |
packages/core/src/scenarios/fixtures/withImpersonation.ts |
Adds chain targeting and writes wallet state into chain entry. |
packages/core/src/scenarios/fixtures/withImpersonation.test.ts |
Updates impersonation tests for chain-scoped context. |
packages/core/src/scenarios/fixtures/withFundedWallet.ts |
Adds chain targeting; funds and updates wallet per chain entry. |
packages/core/src/scenarios/fixtures/withFundedWallet.test.ts |
Updates funded-wallet tests for chain-scoped context. |
packages/core/src/scenarios/fixtures/withFork.ts |
Writes fork runtime state under ctx.chains[chainKey]. |
packages/core/src/scenarios/fixtures/withFork.test.ts |
Updates fork tests to assert chain entry fields. |
packages/core/src/scenarios/fixtures/withExternalRuntime.ts |
Attaches external runtime under ctx.chains[chainKey]. |
packages/core/src/scenarios/fixtures/withExternalRuntime.test.ts |
Updates external-runtime tests for chain-scoped context. |
packages/core/src/scenarios/fixtures/withErc20Balance.ts |
Adds chain targeting and uses chain entry wallet as default recipient. |
packages/core/src/scenarios/fixtures/withErc20Balance.test.ts |
Updates ERC-20 balance tests for chain-scoped context. |
packages/core/src/scenarios/fixtures/withDeployments.ts |
Deploys into ctx.chains[chain].deployments; updates hook context. |
packages/core/src/scenarios/fixtures/withDeployments.test.ts |
Updates deployment tests to read chain-scoped deployments. |
packages/core/src/scenarios/fixtures/withContracts.ts |
Injects contracts into ctx.chains[chain].contracts; updates hook context. |
packages/core/src/scenarios/fixtures/withContracts.test.ts |
Updates contracts tests to read chain-scoped contracts. |
packages/core/src/scenarios/fixtures/withChain.ts |
Writes chain runtime under ctx.chains[chainKey]. |
packages/core/src/scenarios/fixtures/withChain.test.ts |
Updates chain tests to assert chain entry fields. |
packages/core/src/scenarios/fixtures/withBundler.ts |
Adds chain targeting and stores bundler wiring on chain entry. |
packages/core/src/scenarios/fixtures/withBundler.test.ts |
Updates bundler tests for chain-scoped context. |
packages/core/src/scenarios/fixtures/PRIMITIVES.md |
Updates primitives listing to reflect multi-chain changes. |
docs/pages/quickstart.mdx |
Migrates quickstart example to ctx.chains.default. |
docs/pages/overview.mdx |
Migrates overview examples and type-safety explanation to ctx.chains. |
docs/pages/index.mdx |
Migrates homepage example to chain-scoped context. |
docs/pages/fixtures/wallets/withImpersonation.mdx |
Documents new chain option and chain-scoped effects. |
docs/pages/fixtures/wallets/withFundedWallet.mdx |
Documents new chain option and chain-scoped effects. |
docs/pages/fixtures/tokens/withErc20Balance.mdx |
Documents new chain option and chain-scoped behavior. |
docs/pages/fixtures/runtime/withMultiChain.mdx |
New documentation page for withMultiChain. |
docs/pages/fixtures/runtime/withFork.mdx |
Documents chainKey and chain-scoped context. |
docs/pages/fixtures/runtime/withExternalRuntime.mdx |
Documents chainKey and chain-scoped context. |
docs/pages/fixtures/runtime/withChain.mdx |
Documents chainKey and chain-scoped context. |
docs/pages/fixtures/runtime/withBundler.mdx |
Updates bundler docs to chain-scoped context and chain option. |
docs/pages/fixtures/runtime/index.mdx |
Updates runtime fixture overview to ctx.chains model + multi-chain guidance. |
docs/pages/fixtures/isolation/withSnapshot.mdx |
Documents snapshot targeting via chain. |
docs/pages/fixtures/contracts/withDeployments.mdx |
Updates deployments docs to chain-scoped context + new config shape. |
docs/pages/fixtures/contracts/withContracts.mdx |
Updates contracts docs to chain-scoped context + new config shape. |
README.md |
Updates readme examples + primitive list to ctx.chains and introduces withMultiChain. |
AGENTS.md |
Updates architecture guidance to reflect implemented multi-chain approach. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| test("withMultiChain output type is runtime clients context", () => { | ||
| expectTypeOf<StepOut<ReturnType<typeof withMultiChain>>>().toEqualTypeOf<ScenarioRuntimeClientsContext>(); | ||
| }); |
There was a problem hiding this comment.
There’s no longer a negative typing test ensuring invalid fixture order is a compile-time error (for example, withChain() followed by withErc20Balance({ token, amount }) without withFundedWallet). Given the context refactor to ctx.chains, it would be good to add an @ts-expect-error type test here to preserve the advertised fixture-order type safety.
| --- | ||
|
|
||
| ## `withBundler({ entryPoint, mode? })` | ||
| ## `withBundler({ entryPoint, mode?[, chain] })` |
There was a problem hiding this comment.
The signature in the heading is a bit confusing: withBundler({ entryPoint, mode?[, chain] }) reads like chain is conditional on mode. Since chain is an independent optional property, consider rephrasing it to something like withBundler({ entryPoint, mode?, chain? }) for clarity.
| ## `withBundler({ entryPoint, mode?[, chain] })` | |
| ## `withBundler({ entryPoint, mode?, chain? })` |
| /** | ||
| * Map of deployment name → spec; names become keys on `ctx.deployments`. | ||
| * Deployments for {@link withDeployments}. Use `chain` to select `ctx.chains[chain]` (default `default`). | ||
| */ | ||
| export type WithDeploymentsConfig = Record<string, DeploymentSpec>; | ||
| export type WithDeploymentsConfig = { | ||
| chain?: string; | ||
| deployments: Record<string, DeploymentSpec>; | ||
| }; |
There was a problem hiding this comment.
WithDeploymentsConfig is now an object with a deployments property, which is a breaking change from the previous Record<string, DeploymentSpec> shape. If this package is consumed externally, consider adding an overload (or a backward-compatible union type) that still accepts the old shape, or ensure the release is versioned/documented as a breaking API change.
| /** | ||
| * Map of contract name → injection spec; names become keys on `ctx.contracts`. | ||
| * Contract injections for {@link withContracts}. Use `chain` to select `ctx.chains[chain]` (default `default`). | ||
| */ | ||
| export type WithContractsConfig = Record<string, ContractInjection>; | ||
|
|
||
| type WithContractsIn = ScenarioRuntimeClientsContext & { | ||
| contracts?: ScenarioContracts; | ||
| }; | ||
| type WithContractsOut = ScenarioRuntimeClientsContext & { | ||
| contracts: ScenarioContracts; | ||
| export type WithContractsConfig = { | ||
| chain?: string; | ||
| contracts: Record<string, ContractInjection>; | ||
| }; |
There was a problem hiding this comment.
WithContractsConfig now requires a { contracts: ... } wrapper instead of the previous Record<string, ContractInjection> shape. This is an API-breaking change for callers. Consider supporting both shapes via an overload/union (or ensure the change is explicitly treated as a breaking change in versioning/changelog).
| export type { | ||
| AfterDeployContext, | ||
| AfterSetCodeContext, | ||
| ContractArtifact, | ||
| BundlerContext, | ||
| ScenarioBundlerContext, | ||
| ScenarioChainContext, | ||
| DeploymentArgsResolver, |
There was a problem hiding this comment.
BundlerContext was removed from the exported types here. If it was part of the public API, this is a breaking change for downstream consumers importing that type; consider keeping it as a deprecated alias (e.g., to ScenarioBundlerContext or to the bundler fields within ScenarioChainContext) to ease migration, or ensure the release is versioned accordingly.
| /** | ||
| * Context after {@link withFundedWallet}: the targeted chain entry includes `wallet`. Prefer reading `ctx.chains[chain].wallet`. | ||
| */ | ||
| export type ScenarioFundedWalletContext = ScenarioRuntimeClientsContext; | ||
|
|
There was a problem hiding this comment.
ScenarioFundedWalletContext is currently just an alias of ScenarioRuntimeClientsContext, so it no longer encodes that any chain entry actually has wallet set after withFundedWallet. This weakens the intended fixture-order type safety (e.g., withErc20Balance({ token, amount }) can no longer be made a type error unless a wallet is guaranteed). Consider making withFundedWallet’s output type (and ScenarioFundedWalletContext) require wallet on the targeted chain key (defaulting to default), ideally via a generic chain-key type parameter so downstream fixtures can enforce ordering again.
| export function requireChainScopedRuntimeClients( | ||
| ctx: ScenarioContext, | ||
| chainKey: string, | ||
| ): asserts ctx is ScenarioContext & { | ||
| chains: Record<string, ScenarioChainContext> & Record<typeof chainKey, ScenarioChainContext>; | ||
| } { |
There was a problem hiding this comment.
The assertion type in requireChainScopedRuntimeClients doesn’t actually narrow chainKey: Record<typeof chainKey, ...> collapses to Record<string, ...> because chainKey is typed as string. If the goal is to make ctx.chains[chainKey] non-optional without !, make the function generic (e.g., <K extends string>(..., chainKey: K)) so the asserted type includes Record<K, ScenarioChainContext>.
ccebfb7 to
f21f958
Compare
Add a bridge simulation fixture for multi-chain scenarios. It exposes an execute callback that allows tests to manually trigger deterministic native or ERC-20 balance transfers between defined source and destination chains.
e4067f2 to
8cc1d8c
Compare
Clarify each fixture example with production-adjacent testing scenarios and explicit notes on what setup Statecraft replaces, then align index/landing docs to route readers from minimal examples to business-logic-focused workflows. Made-with: Cursor
No description provided.