Skip to content

chore: withMultichain#17

Open
joepegler wants to merge 4 commits into
devfrom
feat/with_multichain
Open

chore: withMultichain#17
joepegler wants to merge 4 commits into
devfrom
feat/with_multichain

Conversation

@joepegler
Copy link
Copy Markdown
Owner

No description provided.

@joepegler
Copy link
Copy Markdown
Owner Author

@codex review

Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 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".

Comment on lines +120 to +122
for (const handle of owned.reverse()) {
await stopRuntime(handle);
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge 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 👍 / 👎.

Comment thread packages/core/src/scenarios/types.ts Outdated
/**
* Context after {@link withFundedWallet}: the targeted chain entry includes `wallet`. Prefer reading `ctx.chains[chain].wallet`.
*/
export type ScenarioFundedWalletContext = ScenarioRuntimeClientsContext;
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge 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 👍 / 👎.

Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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 withMultiChain to start/attach multiple runtimes and expose them under ctx.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.chains model 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.

Comment on lines +22 to +24
test("withMultiChain output type is runtime clients context", () => {
expectTypeOf<StepOut<ReturnType<typeof withMultiChain>>>().toEqualTypeOf<ScenarioRuntimeClientsContext>();
});
Copy link

Copilot AI Mar 27, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Copilot uses AI. Check for mistakes.
---

## `withBundler({ entryPoint, mode? })`
## `withBundler({ entryPoint, mode?[, chain] })`
Copy link

Copilot AI Mar 27, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Suggested change
## `withBundler({ entryPoint, mode?[, chain] })`
## `withBundler({ entryPoint, mode?, chain? })`

Copilot uses AI. Check for mistakes.
Comment on lines +24 to +30
/**
* 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>;
};
Copy link

Copilot AI Mar 27, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Copilot uses AI. Check for mistakes.
Comment on lines 25 to 31
/**
* 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>;
};
Copy link

Copilot AI Mar 27, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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).

Copilot uses AI. Check for mistakes.
Comment on lines 15 to 21
export type {
AfterDeployContext,
AfterSetCodeContext,
ContractArtifact,
BundlerContext,
ScenarioBundlerContext,
ScenarioChainContext,
DeploymentArgsResolver,
Copy link

Copilot AI Mar 27, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Copilot uses AI. Check for mistakes.
Comment on lines +110 to +114
/**
* Context after {@link withFundedWallet}: the targeted chain entry includes `wallet`. Prefer reading `ctx.chains[chain].wallet`.
*/
export type ScenarioFundedWalletContext = ScenarioRuntimeClientsContext;

Copy link

Copilot AI Mar 27, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Copilot uses AI. Check for mistakes.
Comment thread packages/core/src/scenarios/utils.ts Outdated
Comment on lines 36 to 41
export function requireChainScopedRuntimeClients(
ctx: ScenarioContext,
chainKey: string,
): asserts ctx is ScenarioContext & {
chains: Record<string, ScenarioChainContext> & Record<typeof chainKey, ScenarioChainContext>;
} {
Copy link

Copilot AI Mar 27, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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>.

Copilot uses AI. Check for mistakes.
@joepegler joepegler force-pushed the feat/with_multichain branch from ccebfb7 to f21f958 Compare March 28, 2026 06:23
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.
@joepegler joepegler force-pushed the feat/with_multichain branch from e4067f2 to 8cc1d8c Compare April 8, 2026 19:54
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
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants