Chain Switching & Multichain
Relevant source files
The following files were used as context for generating this wiki page:
The Chain Switching and Multichain system within the Walletless project provides a mechanism for E2E testing environments to simulate cross-chain interactions seamlessly. It allows a single provider instance to manage multiple blockchain configurations, switching between them programmatically or via standard EIP-1193 RPC calls.
This functionality is critical for DApps that operate across multiple L1s or L2s, ensuring that the test environment correctly updates RPC endpoints, internal wallet clients, and state notifications when a chain change occurs. The system maintains 100% realism by routing read operations to specific chain RPCs while handling signing locally for the currently active chain.
Sources: src/provider.ts:89-106, README.md:162-175
Multichain Configuration
The provider is initialized with a list of supported chains and their corresponding RPC URLs. If no chains are provided, it defaults to Ethereum Mainnet. The first chain in the configuration array is treated as the default active chain upon initialization.
Sources: src/provider.ts:111-125, src/types.ts:25-36
Configuration Parameters
| Parameter | Type | Description |
|---|---|---|
chains | CompatibleChain[] | Array of supported chains. The first entry is the default. |
rpcUrls | Record<number, string> | Map of chain IDs to their specific RPC endpoints. |
defaultChain | Chain | Fallback chain (Mainnet) if chains is empty. |
Sources: src/types.ts:27-36, src/constants.ts:79
Chain Switching Logic
Chain switching can be triggered through two primary interfaces:
- RPC Method: Calling
wallet_switchEthereumChainvia the standard EIP-1193requestmethod. - Control API: Using the
setChain(chainId)method directly on the provider or via thesetChainhelper function.
When a switch is initiated, the provider validates that the target chainId exists within its supportedChainIds. If valid, it updates the internal rpcUrl, recreates the walletClient (via viem) to point to the new transport, and updates the internal state. Finally, it emits a chainChanged event to notify the DApp.
Sources: src/provider.ts:333-359, src/provider-controls.ts:36-44, src/provider.ts:466-485
Internal Update Flow
The following diagram illustrates the sequence of events when a chain switch is requested:
RPC Routing & Multichain
The provider employs a "Man-in-the-Middle" pattern where the behavior of the request function changes based on the active chain's configuration. Read methods are forwarded to the rpcUrl associated with the current chain, while write methods use the walletClient configured for that specific chain.
Sources: src/provider.ts:413-435, src/constants.ts:85-155
Integration with Wagmi Connector
The e2eConnector bridges the provider's multichain capabilities with the Wagmi ecosystem. When the provider's chain changes, the connector listens for the chainChanged event and forwards the update to the Wagmi emitter. This ensures that hooks like useChainId or useAccount reflect the correct network state.
Sources: src/connector.ts:101-115, src/connector.ts:187-199
Connector Chain Switching
When Wagmi calls switchChain, the connector invokes the provider's wallet_switchEthereumChain method. This maintains synchronization between the external Wagmi state and the internal virtual provider state.
Sources: src/connector.ts:211-231
Error Handling
If a switch is attempted to a chain ID that was not defined during the provider's initialization, the system throws an error. In the context of the RPC method, it returns an EIP-1193 compliant error code.
Sources: src/provider.ts:338-345, src/types.ts:167-174
| Error Condition | Behavior | Error Code |
|---|---|---|
| Unsupported Chain | Throws "Chain [ID] is not supported" | 4902 (Chain not added) |
| Missing RPC URL | Falls back to http://127.0.0.1:8545 | N/A |
| Invalid Provider | Throws "Provider does not support setChain" | N/A |
Sources: src/provider.ts:344, src/provider-controls.ts:40, test/provider.spec.ts:269-280
The multichain system ensures that developers can test complex scenarios, such as bridging or cross-chain governance, by simply calling setChain(provider, newChainId) within their test scripts, causing the entire DApp to point to a new virtual environment instantaneously.
Sources: README.md:175-188