MetaMask / MetaMask/connect-monorepo
[connect-evm] 2x bundle size increase over @metamask/sdk
Nobody has claimed this yet.
- Dominant language
- TypeScript
- Stars
- 12
- Forks
- 11
- PR merge metrics
- No merged PRs in 30d
Description
**`@metamask/connect-evm` bundle is around 2× larger than `@metamask/sdk` due to unrelated transitive dependencies**
**Package:** `@metamask/connect-evm@0.9.1`
---
### Summary
Migrating from `@metamask/sdk` to `@metamask/connect-evm` produces a nearly **2×** increase in minified chunk size with no change in runtime behavior. I created a minimal project following only the instructions at https://docs.metamask.io/metamask-connect/evm/quickstart/wagmi/ for both sdk and connect-evm. The difference in chunks was as follows:
| Package | Chunk size (minified) |
|---|---|
| `@metamask/sdk` | 542.12 kB |
| `@metamask/connect-evm` | 1026.73 kB |
---
### Root cause
`@metamask/connect-evm` is architecturally a thin EIP-1193 adapter over `@metamask/connect-multichain`; every session, transport, and RPC call is delegated through `createMultichainClient`, and `MetamaskConnectEVM` wraps a `MultichainCore` instance directly. This means the **entire `connect-multichain` dependency graph** is transitively bundled into EVM-only consumers, including any multichain-specific code that is never exercised in that context.
The coupling runs deeper than the top-level class. The `EIP1193Provider` implementation imports `EventEmitter` and `RPCInvokeMethodErr` directly from `@metamask/connect-multichain`. These are generic primitives, a base event emitter and an RPC error class, that have no inherent relationship to multichain functionality. Their presence inside `connect-multichain` is a strong signal that the shared foundation has never been properly separated from protocol-specific code.
This is further evidenced by `EIP1193Provider` accessing `MultichainCore`'s `options` property via an `as any` type assertion, with an accompanying `TODO` noting it requires refactoring. `connect-evm` is reaching into `connect-multichain` internals through an unofficial channel because the public API surface of `connect-multichain` was never designed to support `connect-evm` being layered on top of it. This is an architectural boundary problem, not just a dependency graph one.
Additionally, `@metamask/chain-agnostic-permission` is imported solely for `parseScopeString`, which is called once in `disconnect()` to check whether a scope's namespace prefix equals `"eip155"`, a trivial string operation that does not justify pulling in an entire package.
Finally, `@metamask/analytics` is deeply integrated into the request lifecycle, with `analytics.track(...)` called on every request, success, rejection, and failure path throughout the class. This makes it impossible to tree-shake without a build-time module alias override, forcing consumers who do not want telemetry to maintain external workarounds (e.g., a Vite/Rollup `resolve.alias` stub).
---
### Expected behaviour
A package named `connect-evm` should have a dependency graph constrained to EVM connectivity primitives only. The shared transport, session management, storage adapters, event emitter, and RPC error types that are currently embedded inside `connect-multichain` should be extracted into a `connect-core` (or equivalent) base package with a stable, intentionally designed public API. Both `connect-evm` and `connect-multichain` would then depend on `connect-core`, allowing EVM-only consumers to avoid bundling any multichain-specific code entirely, and eliminating the need for `as any` casts across the abstraction boundary.
---
### Suggested remediation
1. Extract shared primitives: `EventEmitter`, `RPCInvokeMethodErr`, provider transport, session lifecycle, storage adapters, and CAIP scope utilities into a `@metamask/connect-core` package with an explicit, stable public API.
2. Rebuild `@metamask/connect-evm` on top of `@metamask/connect-core` rather than `@metamask/connect-multichain`, so the multichain-specific dependency graph is not transitively included and the `as any` internal access can be replaced with first-class API surface.
3. Remove `@metamask/chain-agnostic-permission` as a dependency of `connect-evm`. The single usage (`parseScopeString` for an `eip155` namespace prefix check) should either be inlined as a string operation or provided by `connect-core` as a lightweight utility.
4. Declare `@metamask/analytics` as an optional peer dependency, or place it behind a side-effect-free export condition, so consumers are not forced to ship telemetry code or maintain build-tool workarounds to suppress it.
---
**Impact**
This bundle bloat directly degrades Lighthouse/Core Web Vitals scores (TTI, TBT), increases cold-start parse and compile time in V8/SpiderMonkey, and imposes unnecessary bandwidth costs on end users, particularly on mobile and constrained connections.
---
**EDIT**
The drastic change in bundle size originally reported was not only related to MetaMask, but also changes to Rolldown in Vite 8.x. After reverting to 3.5.0 and rebuilding I'm now seeing the metamask bundle at around 540kB. To isolate more properly, I created minimal reproduction projects building with vite 8.0.4, and the difference is closer to 2x. All of the rest above is still valid, and 2x increase is unreasonable. The full list of dependencies:
```
"dependencies": {
"@metamask/connect-evm": "^0.9.1",
"@tanstack/react-query": "^5.97.0",
"react": "^19.2.4",
"react-dom": "^19.2.4",
"viem": "~2.47.11",
"wagmi": "3.6.1"
},
```
vs
```
"dependencies": {
"@metamask/sdk": "^0.34.0",
"@tanstack/react-query": "^5.97.0",
"react": "^19.2.4",
"react-dom": "^19.2.4",
"viem": "~2.47.11",
"wagmi": "3.5.0"
},
```
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start by tracing createMultichainClient and MetamaskConnectEVM into EIP1193Provider, including its EventEmitter, RPCInvokeMethodErr, options access, disconnect scope parsing, and analytics calls. Compare minimal Vite builds for @metamask/connect-evm and @metamask/sdk; done means EVM-only consumers no longer pull multichain-specific dependencies and the reported bundle gap is reduced.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- react, typescript, vite
- Domain
- build-system, frontend, performance
- Issue type
- Refactor
- Difficulty
- 5/5
- Estimated time
- Over a week
- Activity status
- Quiet
- Clarity
- Mostly clear
- Newbie friendliness
- 30/100