MetaMask / MetaMask/connect-monorepo

[Bug] connect-evm keeps wrong chain on reconnect and breaks `switchChain`

Open
#349 0 comments 0 reactions 0 assignees View on GitHub
Dominant language
TypeScript
Stars
12
Forks
11
PR merge metrics
No merged PRs in 30d

Description

*Bug: connect-evm overrides the wallet's chain with the dapp's first configured chain on every connect, then `switchChain` no-ops*

*Packages:* `@metamask/connect-evm@2.1.1`, `@metamask/connect-multichain@1.2.0`, used through wagmi `metaMask()` connector (`@wagmi/connectors@8.1.0`).

*Symptom.* After a disconnect and reconnect, the dapp believes the wallet is on chain A while the extension is on chain B for this origin. Asking the SDK to switch to chain A sends nothing to the wallet, so the mismatch can never be resolved from the dapp.

*Reproduction*
1. Configure wagmi with chains `[mainnet, optimism]` and the `metaMask()` connector.
2. Connect, switch to Optimism in the wallet, disconnect from the dapp.
3. Reconnect. The SDK provider reports `eth_chainId = 0x1`, while the extension's own `eth_chainId` for the origin returns `0xa`.
4. Call `switchChain({ chainId: 1 })`. It resolves immediately, no wallet request is sent, the extension stays on Optimism.

*Where it breaks in `packages/connect-evm/src/connect.ts`*
• https://github.com/MetaMask/connect-monorepo/blob/%40metamask/connect-evm%402.1.1/packages/connect-evm/src/connect.ts#L356 — `connect()` stores `chainIds[0]` as `#pendingPreferredChainId`. wagmi passes `config.chains` in configured order, so this is always the dapp's first chain, also on reconnect.
• https://github.com/MetaMask/connect-monorepo/blob/%40metamask/connect-evm%402.1.1/packages/connect-evm/src/connect.ts#L310-L335 — `#getSelectedChainId` returns the pending preferred chain whenever it is permitted, before the cache, and never queries the wallet.
• https://github.com/MetaMask/connect-monorepo/blob/%40metamask/connect-evm%402.1.1/packages/connect-evm/src/connect.ts#L735-L736 — `#onSessionChanged` feeds that value into `#onConnect`, which becomes `provider.selectedChainId`.
• https://github.com/MetaMask/connect-monorepo/blob/%40metamask/connect-evm%402.1.1/packages/connect-evm/src/connect.ts#L654-L656 — `eth_chainId` via the provider is answered from `selectedChainId`, so dapps and wagmi `getChainId()` cannot observe the real chain.
• https://github.com/MetaMask/connect-monorepo/blob/%40metamask/connect-evm%402.1.1/packages/connect-evm/src/connect.ts#L457-L459 — `switchChain` returns early when `selectedChainId === chainId`. Combined with the above, a switch to the presumed chain is never delivered.
• https://github.com/MetaMask/connect-monorepo/blob/%40metamask/connect-evm%402.1.1/packages/connect-evm/src/connect.ts#L628 — `disconnect()` clears `selectedChainId`, so the next connect repopulates it from the preferred chain. That is why it shows up after a connect/disconnect cycle.

*Suggested fix.* Use `#pendingPreferredChainId` only when creating a brand-new session. On session restore, query the wallet's actual per-origin chain (e.g. an `eth_chainId` passthrough) before setting `selectedChainId`. Alternatively drop the early return in `switchChain` so the wallet stays the source of truth.

*Related, by design: `StreamMiddleware - Unknown response id`*
`wallet_switchEthereumChain` is in `EIP1193_PASSTHROUGH_METHODS` (`packages/connect-multichain/src/domain/multichain/api/constants.ts`). The router forwards it at
https://github.com/MetaMask/connect-monorepo/blob/%40metamask/connect-multichain%401.2.0/packages/connect-multichain/src/multichain/rpc/requestRouter.ts#L61-L63 and
https://github.com/MetaMask/connect-monorepo/blob/%40metamask/connect-multichain%401.2.0/packages/connect-multichain/src/multichain/rpc/requestRouter.ts#L154-L169
to `sendEip1193Message`, which at
https://github.com/MetaMask/connect-monorepo/blob/%40metamask/connect-multichain%401.2.0/packages/connect-multichain/src/multichain/transports/default/index.ts#L154-L177
posts a raw JSON-RPC message with its own id onto the extension's legacy `metamask-provider` substream. The extension's inpage provider also listens there, does not recognise the id, and logs the warning. The SDK's own listener still resolves the request, so the line is noise, not the cause of the failed switch.

*Dapp side.* Nothing non-standard: a plain wagmi chain list and `switchChain`. Only the MetaMask SDK connector is affected; injected, WalletConnect, Ledger and Safe connectors answer `eth_chainId` from the wallet.

Contributor guide

Open the contributing guide

Research direction

Start in packages/connect-evm/src/connect.ts at connect(), #getSelectedChainId, #onSessionChanged, eth_chainId handling, switchChain, and disconnect(). Trace reconnect behavior and compare the provider's selected chain with the wallet's per-origin chain; done means reconnect reports the wallet's actual chain and switchChain sends a request when the chains differ. The requestRouter and default transport paths explain the related response-id warning, not the main failure.

Written by the indexing model from the issue text.

Assessment

Tech stack
typescript
Domain
api, blockchain
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Active
Clarity
Clearly specified
Newbie friendliness
68/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.