Lightweight E2E Provider for Web3 DApps
This proposal outlines a lightweight, dependency-free solution for E2E testing in Dapps. By replacing heavy browser automation frameworks and real wallet extensions with a custom EIP-1193 Ethereum Provider, we can achieve deterministic, high-performance tests that run natively in standard frameworks like Cypress or Playwright.
Problem Statement
Current Web3 E2E testing strategies face significant bottlenecks:
- High Overhead: Tools like Synpress require launching full browser extensions (Metamask), consuming significant CI/CD resources.
- Flakiness: Tests depend on external blockchain states, network latency, and UI changes in 3rd-party wallet extensions.
- Complex Configuration: Setting up the environment requires complex seed phrase management and synchronization between the test runner and the browser extension.
- Slow Execution: Waiting for wallet animations, block confirmations, and network switching drastically slows down test suites.
Proposed Solution: The Virtual E2E Connector
The solution is a “Man-in-the-Middle” injected provider that sits between the DApp and the network. Instead of interacting with a real wallet extension, the DApp interacts with our custom provider which implements the standard EIP-1193 interface.
This provider functions as a traffic controller:
-
Read operations (
eth_call,eth_getBalance, etc.)→ Forwarded directly to Anvil RPC
-
Write operations (
eth_sendTransaction,eth_sign, etc.)→ Routed to Signing / Impersonation logic, then forwarded to Anvil RPC
This keeps 100% chain realism, while maintaining full control in tests.
Advantages
- Incredible Test Speed: Test execution becomes dramatically faster since there’s no browser extension overhead, RPC latency is controlled, and wallet interactions are fully virtualized.
- Framework Agnostic: Works with Cypress, Playwright, Selenium, or any tool that can intercept network traffic.
- Zero External Dependencies: Built entirely on
viemtypes and nativefetch. No heavy peer dependencies. - Total Control: We can simulate edge cases that are hard to reproduce with real wallets (e.g., RPC errors, specific error codes, delayed signatures, chain switching failures).
- CI/CD Friendly: Runs effortlessly in headless browsers (Docker containers).
Architecture Diagram
DApp UI
|- Read -> Custom Provider -> Anvil RPC
| (eth_call, eth_getBalance, ...)
|
`- Write -> Custom Provider -> Sign/Impersonation logic -> Anvil RPC
(eth_sendTx, eth_sign, ...)
Implementation Details
The Traffic Splitter
The core logic resides in a Proxy Provider that inspects the RPC method.
// lib/e2e-connector/provider.ts
const READ_METHODS = [
"eth_call",
"eth_getBalance",
"eth_blockNumber",
"eth_getTransactionReceipt",
"eth_getLogs",
// ...
];
export function createE2EProvider(config: E2EConnectorConfig) {
return {
async request({ method, params }) {
// READ → Anvil
if (READ_METHODS.includes(method)) {
return fetch(config.rpcUrl, {
method: "POST",
body: JSON.stringify({
jsonrpc: "2.0",
id: 1,
method,
params,
}),
})
.then((res) => res.json())
.then((r) => r.result);
}
// WRITE → Sign locally, then send to Anvil
return handleWrite(method, params, config);
},
};
}
Signing / Impersonation Logic
// lib/e2e-connector/sign.ts
import { createWalletClient, http } from "viem";
export async function handleWrite(method, params, config) {
const client = createWalletClient({
transport: http(config.rpcUrl),
chain: config.chain,
account: config.account, // private key or impersonated account
});
switch (method) {
case "eth_sendTransaction":
return client.sendTransaction(params[0]);
case "eth_sign":
case "personal_sign":
return client.signMessage({
message: params[0],
});
default:
throw new Error(`Unsupported method: ${method}`);
}
}
Integration with Wagmi/Viem
The solution uses a standard Wagmi Connector factory, making it “Plug and Play”. The DApp does not need to change its code logic, only its configuration
// src/config/wagmiConfig.ts
const isE2E = process.env.CI === "true";
export const config = createConfig({
connectors: isE2E
? [
e2eConnector({
rpcUrl: "http://127.0.0.1:8545", // Anvil
account: TEST_PRIVATE_KEY, // or impersonated
chain: mainnetFork,
}),
]
: [
/* real wallets */
],
});
Final Result
You now have:
- A real blockchain
- A virtual wallet
- A deterministic environment
- A super-fast E2E stack
- And zero dependency on Metamask or fake endpoints
This is as close as you can get to production behavior with testing-level control.