Skip to content

Feat/metamask multichain#101

Open
GiMa-SwapKit wants to merge 4 commits into
swapkit:developfrom
GiMa-SwapKit:feat/metamask-multichain
Open

Feat/metamask multichain#101
GiMa-SwapKit wants to merge 4 commits into
swapkit:developfrom
GiMa-SwapKit:feat/metamask-multichain

Conversation

@GiMa-SwapKit

Copy link
Copy Markdown

feat(wallets): connect MetaMask via @metamask/connect-multichain (EVM + Solana)

Summary

Routes WalletOption.METAMASK through MetaMask's official multichain SDK
([@metamask/connect-multichain](https://docs.metamask.io/metamask-connect/multichain/))
instead of the legacy injected window.ethereum path. A single
[CAIP-25](https://github.com/ChainAgnostic/CAIPs/blob/main/CAIPs/caip-25.md)
session — and a single approval prompt — now covers EVM and Solana (and any
future ecosystem MetaMask adds), rather than a per-chain injected provider.

A new metamaskWallet connector exposes skClient.connectMetamask(chains, options?).
It lazily imports the SDK, derives supportedNetworks from each requested chain's
RPC URL, opens one session over all the requested CAIP-2 scopes, then adapts the
session into each toolbox SwapKit already has.

This is the multichain counterpart to the upstream @metamask/connect-evm work in
[swapkit/SwapKit#1632](swapkit/SwapKit#1632), chosen here
because the goal is EVM and Solana from one connection. connect-evm cannot do
Solana; the multichain client can, and per MetaMask's own guidance the two share
session infrastructure so this is the right long-term target.

Behavior change

  • WalletOption.METAMASK no longer uses the injected window.ethereum provider.
    It is split out of the injected evmWallet group and routed to the new connector.
    The generic WalletOption.EIP6963 option still serves every other injected wallet.
  • Selecting MetaMask can now authorize Solana in the same prompt as the EVM chains.

How it works

The multichain client has no per-chain EIP-1193 provider — all interaction goes
through invokeMethod({ scope, request }). The connector reshapes that into what
each existing SwapKit toolbox already consumes:

  • EVM → a thin EIP-1193 shim over invokeMethod (it answers eth_accounts /
    eth_chainId from the session and forwards everything else to the wallet/RPC) →
    wrapped in an ethers BrowserProvider → fed to the existing getWeb3WalletMethods,
    unchanged.
  • Solana → a SolanaProvider-style signer whose signTransaction calls
    solana_signTransaction (serialize → base64 → invoke → deserialize). It signs and
    returns; SwapKit broadcasts via its own sendRawTransaction, matching
    getSolanaToolbox({ signer }).
  • Future ecosystems → add one adapter per ecosystem as SwapKit gains a toolbox.

Account resolution scans every sessionScopes bucket and matches accounts by their
CAIP-10 namespace:reference, so it is robust whether getSession returns full
CAIP-2 keys (eip155:1) or the namespace-collapsed form (eip155 + references).

Changes

Library

  • packages/wallets/src/metamask/index.ts — new metamaskWallet connector (connectMetamask).
  • packages/wallets/package.json — add @metamask/connect-multichain and @solana/web3.js; add ./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-export it.

Playground

  • playgrounds/vite-lite/vite.config.tsoptimizeDeps config required to run the connect SDK (exclude @metamask/multichain-ui; include the dynamically-imported CJS deps).

Note: unlike upstream #1632, there is no libsodium-wrappers-sumo override here —
that dependency is not present in this repo's lockfile, so the fix doesn't apply.

Test plan

  • bun run build:ci builds all packages and generates d.ts files cleanly.
  • bun run type-check passes for every package, @swapkit/wallets and @swapkit/sdk included.
  • Runtime, not yet verified — extension present: selecting MetaMask connects
    directly and resolves both an EVM and a Solana address from one prompt.
  • Runtime, not yet verified — Solana solana_signTransaction round-trip
    (serialize → invoke → deserialize → SwapKit broadcast) and the EIP-1193 shim
    under a real BrowserProvider.

The runtime items need a UI to exercise them; vite-lite has no MetaMask button yet.
Happy to add one in a follow-up (or here) for an end-to-end smoke test.

Open questions / follow-ups

  • @metamask/connect-multichain is at 1.1.0; confirm the solana_signTransaction
    return shape against the installed version if it bumps.
  • Persistent-session re-hydration on reload (wallet_sessionChanged) is intentionally
    left to SwapKit's reconnect layer rather than the connector.
  • Optional: expose headless / onDisplayUri passthrough so the widget can render its
    own QR instead of MetaMask's built-in modal.

SwapKit and others added 4 commits June 25, 2026 16:15
… + Solana)

Route WalletOption.METAMASK through MetaMask's multichain CAIP-25 SDK so a single
session/approval covers EVM and Solana (and future ecosystems). A dedicated
metamaskWallet connector opens one session over all requested scopes, then adapts
invokeMethod into each existing SwapKit toolbox:

  - EVM:    an EIP-1193 shim over invokeMethod -> ethers BrowserProvider ->
            getWeb3WalletMethods (reused unchanged).
  - Solana: a SolanaProvider-style signer whose signTransaction calls
            solana_signTransaction (sign-only; SwapKit broadcasts) -> getSolanaToolbox.

Also registers metamaskWallet in the SDK defaultWallets + re-exports, repoints the
METAMASK types, adds @metamask/connect-multichain and @solana/web3.js deps plus the
./metamask subpath export, and adds the Vite optimizeDeps config the connect SDK needs.
Import Chain as a value, drop the readonly->mutable EVMChains cast, use the
Chain.Solana enum in chainToScope, and pass supportedChains as a plain Chain[]
([...EVMChains, Chain.Solana]) with directSigningSupport — mirroring the phantom
and evmWallet connectors. Removing the 'as EVMChain[]' cast restores createWallet's
generic inference, which also clears the cascaded 'name is missing' (index.ts) and
'possibly undefined' (types.ts) errors.
getWeb3WalletMethods always wraps the toolbox in prepareNetworkSwitch, which is
built for injected wallets that switch an active chain before signing. In a
CAIP-25 multichain session the scope already pins the chain, so answer
wallet_switchEthereumChain / wallet_addEthereumChain locally as no-ops instead
of forwarding them into invokeMethod (where they're meaningless and may throw).
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