Skip to content

feat(wallets): connect MetaMask via @metamask/connect-evm SDK#1632

Open
shahbaz17 wants to merge 4 commits into
swapkit:developfrom
shahbaz17:feat/metamask-connect-evm-sdk
Open

feat(wallets): connect MetaMask via @metamask/connect-evm SDK#1632
shahbaz17 wants to merge 4 commits into
swapkit:developfrom
shahbaz17:feat/metamask-connect-evm-sdk

Conversation

@shahbaz17
Copy link
Copy Markdown

Connect MetaMask via the official @metamask/connect-evm SDK

Summary

Routes WalletOption.METAMASK through MetaMask's official @metamask/connect-evm SDK instead of the legacy injected window.ethereum path. A single connector now handles the desktop extension, mobile QR/deeplink, and session persistence, and renders MetaMask's built-in install/QR modal when the extension isn't present.

A new metamaskWallet connector exposes skClient.connectMetamask(chains). It lazily imports the SDK, derives supportedNetworks from each chain's RPC URL, wraps the returned EIP-1193 provider in an ethers BrowserProvider, and reuses the shared getWeb3WalletMethods so all downstream wallet behavior is unchanged.

Behavior change

  • WalletOption.METAMASK no longer uses the injected window.ethereum provider — it uses @metamask/connect-evm. The generic WalletOption.EIP6963 option still serves every other injected wallet.
  • In the Vite playground, the discovered MetaMask is no longer listed under "EIP6963 WALLETS" (it would otherwise be a duplicate that bypasses the SDK). The SDK already covers the installed extension.

How it works

flowchart LR
  click["User selects MetaMask"] --> sdk["skClient.connectMetamask(chains)"]
  sdk --> client["createEVMClient (connect-evm)"]
  client --> ext{"Extension installed?"}
  ext -->|Yes| inject["Use injected EIP-1193 provider"]
  ext -->|No| modal["multichain-ui install/QR modal + display_uri"]
  inject --> wrap["ethers BrowserProvider + getWeb3WalletMethods"]
  modal --> wrap
  wrap --> add["addChain per EVM chain"]
Loading

Changes

Library

  • packages/wallets/src/metamask/index.ts — new metamaskWallet connector (connectMetamask).
  • packages/wallets/package.json — add @metamask/connect-evm dependency + ./metamask subpath export.
  • packages/wallets/src/utils.ts — route METAMASK to the new connector (removed from the injected evmWallet group).
  • packages/wallets/src/types.ts — repoint METAMASK types to metamaskWallet.
  • packages/sdk/src/index.ts — register metamaskWallet in defaultWallets and re-exports.
  • packages/ui/src/react/swapkit-context.tsx — widget connects MetaMask via the SDK.

Playground

  • playgrounds/vite/src/WalletPicker.tsx — wire the MetaMask button to connectMetamask; filter MetaMask out of EIP-6963 discovery.
  • playgrounds/vite/vite.config.ts, playgrounds/vite/package.json — bundler config to run the SDK (see note below).

Unrelated build fix

  • package.json — pin libsodium-wrappers-sumo/libsodium-sumo to 0.7.15 via overrides (see note below).

Consumer note (Vite)

Apps bundling with Vite need this optimizeDeps config to run the connect SDK:

optimizeDeps: {
  // Stencil lazy web components resolve chunks via import.meta.url; pre-bundling
  // breaks that, so serve the UI package as native ESM.
  exclude: ["@metamask/multichain-ui"],
  // These CJS deps are dynamically imported with NAMED imports; without listing
  // them Vite emits default-only interop chunks and the mobile/QR flow throws.
  include: [
    "@metamask/mobile-wallet-protocol-core",
    "@metamask/mobile-wallet-protocol-dapp-client",
    "eciesjs",
  ],
}

Worth capturing in the integration docs.

Note on the libsodium commit

This commit is unrelated to MetaMask: libsodium-wrappers-sumo@0.7.16 ships a broken ESM build (its .mjs imports a sibling package's file) that aborts Vite's dependency pre-bundling. Pinning to 0.7.15 restores it. Included only so the demo runs end-to-end and is independently revertable — happy to split into its own PR if preferred.

Test plan

  • bun install is clean; bun --cwd=playgrounds/vite dev starts with no "Failed to resolve" warnings.
  • Extension absent: selecting MetaMask renders the QR/install modal and fires the SDK (display_uri + analytics).
  • Extension present: selecting MetaMask connects directly; no duplicate MetaMask under "EIP6963 WALLETS".
  • bun type-check passes across packages.

shahbaz17 and others added 4 commits June 4, 2026 19:08
Introduce a dedicated `metamaskWallet` (exposing `connectMetamask`) built on
MetaMask's official `@metamask/connect-evm` SDK, replacing the injected
`window.ethereum` path for `WalletOption.METAMASK`. The connector lazily imports
the SDK, derives `supportedNetworks` from each chain's RPC URL, wraps the
returned EIP-1193 provider in an ethers `BrowserProvider`, and reuses the shared
`getWeb3WalletMethods`. This unlocks desktop extension, mobile QR/deeplink, and
session-based connections.

- new connector at packages/wallets/src/metamask/index.ts
- add @metamask/connect-evm dependency and ./metamask subpath export
- route WalletOption.METAMASK to the new connector in loadWallet
- repoint the METAMASK wallet types to metamaskWallet

Co-authored-by: Cursor <cursoragent@cursor.com>
Register metamaskWallet in the SDK's defaultWallets (and re-export it) so
`skClient.connectMetamask(...)` is available on every client, and update the
widget context to connect MetaMask via the SDK connector instead of the injected
connectEVMWallet path.

Co-authored-by: Cursor <cursoragent@cursor.com>
Connect the dedicated METAMASK button via skClient.connectMetamask and drop the
duplicate, SDK-bypassing MetaMask entry from the live EIP-6963 discovery list
(the SDK already handles the installed extension).

Add the Vite dependency-optimizer config required to run the connect SDK:
- exclude @metamask/multichain-ui so its Stencil lazy web components (resolved
  via import.meta.url) load as native ESM and the install/QR modal renders
- include the dynamically-imported CJS deps (mobile-wallet-protocol-core,
  mobile-wallet-protocol-dapp-client, eciesjs) so esbuild emits proper named
  exports instead of default-only interop chunks (otherwise the mobile/QR flow
  throws on undefined named members)

Note: committed with --no-verify because biome intentionally ignores
playgrounds/**, so the lint hook errors with "no files processed" on a
playground-only commit. type-check passes.

Co-authored-by: Cursor <cursoragent@cursor.com>
libsodium-wrappers-sumo@0.7.16 ships a broken ESM build whose
libsodium-wrappers.mjs imports ./libsodium-sumo.mjs from a sibling package,
which Vite's esbuild optimizer cannot resolve and aborts dependency
pre-bundling. Pin both packages to the last working 0.7.15 via overrides.

This is an unrelated build fix bundled here only so the Vite playground demo
runs end-to-end; it is independently revertable. The bun.lock update also
captures the @metamask/connect-evm and playground devDependency additions from
the preceding commits.

Co-authored-by: Cursor <cursoragent@cursor.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant