Virtual Provider Architecture
Relevant source files
The following files were used as context for generating this wiki page:
The Virtual Provider Architecture in @wonderland/walletless implements a "Man-in-the-Middle" (MitM) pattern for EIP-1193 compatible Ethereum providers. Its primary purpose is to enable high-performance end-to-end (E2E) testing by intercepting blockchain interactions. It distinguishes between read operations, which are forwarded to a remote or local RPC (typically Anvil), and write operations, which are handled locally using private key signing logic.
Sources: README.md:10-15, src/types.ts:16-21, src/provider.ts:101-105
This architecture eliminates the need for browser extensions or manual wallet confirmations during automated testing. By virtualizing the wallet interaction layer, the system provides deterministic control over account impersonation, chain switching, and error simulation (such as user rejections) without external dependencies beyond viem.
Sources: README.md:27-32, src/provider.ts:107-111
Core Components and Request Routing
The architecture is centered around a request handler that categorizes incoming JSON-RPC methods into three distinct paths: Read, Write (Signing), and Wallet (State).
Request Flow Logic
The provider evaluates every request through isWalletMethod, isWriteMethod, and isReadMethod checks to determine the appropriate execution path.
The diagram shows how the provider intercepts DApp requests and routes them based on method classification.
Sources: src/provider.ts:476-505, src/constants.ts:129-216
Method Categorization
The system classifies RPC methods to maintain 100% chain realism while controlling sensitive wallet operations.
| Category | Description | Examples | Handling Logic |
|---|---|---|---|
| Read | Blockchain state queries | eth_call, eth_getBalance | Forwarded directly to the configured RPC URL. |
| Write | Operations requiring signatures | eth_sendTransaction, personal_sign | Signed locally using the internal walletClient. |
| Wallet | Provider/Account state | eth_accounts, eth_chainId | Resolved from the provider's internal state. |
Sources: src/constants.ts:85-188, src/provider.ts:250-466
Provider Internal State
The createE2EProvider function initializes a E2EProviderWithInternal object. This object maintains an InternalState which is mutable via control methods, allowing tests to manipulate the environment dynamically.
State Structure
The internal state tracks the active account, current chain configuration, and simulation flags.
This diagram represents the relationship between the provider and its internal management structures.
Sources: src/provider.ts:85-99, src/types.ts:105-110
Account Resolution
The provider can resolve accounts from multiple input formats: Anvil indices (0-9), checksummed/lowercase addresses, or raw private keys. If an address is provided, it is looked up against the default ANVIL_ACCOUNTS list.
Sources: src/provider.ts:38-83, src/constants.ts:20-63
Control Mechanisms
The architecture provides specific hooks for E2E testing frameworks to simulate various wallet behaviors without modifying the application code.
Signature and Transaction Rejection
Tests can toggle rejectSignature or rejectTransaction flags. When enabled, the provider immediately throws a ProviderRpcError with code 4001 (User Rejected Request) for relevant methods.
// Example from internal handler
if (method === "eth_sendTransaction" && internal.rejectTransaction) {
throw new ProviderRpcError(
ProviderErrorCode.UserRejectedRequest,
"User rejected the transaction request."
);
}
Sources: src/provider.ts:273-294, src/types.ts:167-185
Multichain Switching
Switching chains via setChain or wallet_switchEthereumChain updates the internal rpcUrl and recreates the walletClient to point to the new target. This allows the provider to support complex multichain scenarios where different chains have different RPC endpoints.
Sources: src/provider.ts:420-449, src/provider.ts:553-575
Wagmi Integration
The e2eConnector acts as a bridge between the Virtual Provider and the Wagmi library. It can either wrap a pre-constructed provider or create one internally based on configuration.
This sequence demonstrates the initialization and connection flow when using the Wagmi connector.
Sources: src/connector.ts:109-158
Key Connector Features
- Event Forwarding: The connector listens to provider events (
accountsChanged,chainChanged,disconnect) and re-emits them to Wagmi's emitter. - Provider Persistence: If an external provider is passed to the connector, it is used directly, allowing the test runner to maintain a reference for calling control methods like
setSigningAccount. - Automatic Checksumming: Addresses emitted via
accountsChangedare automatically checksummed usingviem.getAddress.
Sources: src/connector.ts:124-140, src/connector.ts:54-61
Conclusion
The Virtual Provider Architecture provides a robust, lightweight foundation for Web3 testing. By separating concerns between local state management and remote blockchain interaction, it achieves high performance (up to 16x faster than traditional extension-based testing) while maintaining full compatibility with the EIP-1193 standard and modern libraries like Wagmi and Viem.
Sources: README.md:34-45, src/provider.ts:101-115