This document describes how the packages in connect-monorepo fit together — how the
multichain client, the ecosystem adapters, and the transports compose. For per-package API
details, see each package's own README.
MetaMask Connect is layered. @metamask/connect-multichain is the client: it speaks the
CAIP-25 Multichain API, manages the session, and negotiates transports. The ecosystem
adapters (connect-evm, connect-solana) wrap the client to expose familiar,
ecosystem-specific surfaces (EIP-1193 and Wallet Standard).
%%{ init: { 'flowchart': { 'curve': 'bumpX' } } }%%
graph TD;
subgraph Adapters["Ecosystem adapters"]
connect_evm(["@metamask/connect-evm<br/>(EIP-1193)"]);
connect_solana(["@metamask/connect-solana<br/>(Wallet Standard)"]);
end
subgraph Client["Multichain client"]
connect_multichain(["@metamask/connect-multichain<br/>(CAIP-25 Multichain API)"]);
end
subgraph Support["Support packages"]
multichain_ui(["@metamask/multichain-ui<br/>(connection UI)"]);
analytics(["@metamask/analytics<br/>(telemetry)"]);
end
connect_evm --> connect_multichain;
connect_solana --> connect_multichain;
connect_evm --> analytics;
connect_multichain --> analytics;
connect_multichain --> multichain_ui;
playgrounds(["Playgrounds<br/>(private, for testing)"]);
playgrounds -.-> connect_evm;
playgrounds -.-> connect_solana;
playgrounds -.-> connect_multichain;
The canonical, auto-generated dependency graph of the published packages lives in the root README. This diagram adds the private playgrounds and the conceptual layering for context.
Key points:
- One session, many ecosystems. The EVM and Solana adapters both drive the same
underlying
MultichainCoreinstance, so a dapp using both shares a single CAIP-25 session.createMultichainClientis a singleton per global context. - Adapters are optional. A dapp can use
@metamask/connect-multichaindirectly for the full scope-based API, or an adapter for a drop-in EIP-1193 / Wallet Standard experience. - Support packages are internal.
multichain-ui(connection UI) andanalytics(telemetry) are pulled in transitively through@metamask/connect-multichain; dapps aren't intended to import them directly.
When a dapp calls connect(), the multichain client detects the platform and picks a
transport. Two concrete transports exist:
DefaultTransport— direct messaging to the MetaMask extension and mobile in-app browser viawindow.postMessage(themetamask-contentscriptchannel).MWPTransport— remote connection to MetaMask Mobile over the Mobile Wallet Protocol. ADappClientconnects through the relay (wss://mm-sdk-relay.api.cx.metamask.io/connection/websocket); the dapp shows a QR code (desktop) or deeplink (mobile native web / React Native) viamultichain-ui, the wallet scans/opens it, and an end-to-end encrypted session is established.
Once a transport is connected, the client owns the CAIP-25 session and routes all RPC
through wallet_invokeMethod. The Multichain API is exposed two equivalent ways: directly
on the client (connect / disconnect / invokeMethod), and as a standard
@metamask/multichain-api-client
provider at client.provider (wired to the client by an internal adapter).
%%{ init: { 'flowchart': { 'curve': 'bumpX' } } }%%
graph TD;
start(["createMultichainClient()"]) --> detect{"Platform detection<br/>isReactNative / isMetaMaskMobileWebView /<br/>isMobile + EIP-6963 extension presence"};
detect -->|"in-app webview, OR<br/>desktop web + extension + preferExtension"| direct["DefaultTransport<br/>window.postMessage"];
detect -->|"otherwise (no extension,<br/>mobile, node)"| mwp["MWPTransport<br/>DappClient"];
direct --> ext["MetaMask Extension /<br/>Mobile in-app browser"];
mwp -.->|"shows QR / deeplink"| ui["multichain-ui<br/>install modal / QR / deeplink"];
mwp --> relay["Relay<br/>wss://mm-sdk-relay.api.cx.metamask.io/connection/websocket"];
ui -.->|"QR scan / deeplink open"| mobile["MetaMask Mobile"];
relay <-->|"E2E encrypted (ECIES)"| mobile;
ext --> session["CAIP-25 session<br/>wallet_invokeMethod"];
mobile --> session;
store[("StoreAdapter / KV store<br/>web / RN / node")];
session -.->|"persists transport type"| store;
mwp -.->|"persists MWP pairing session<br/>(relay channel + ECIES keypair)"| store;
store -.->|"restores transport type +<br/>MWP session on reload"| start;
Notes:
- Platform entry points. The client ships three builds —
index.browser.ts,index.native.ts,index.node.ts— that differ only in their UI modals (web/rn/node) and storage adapter (localStorage/ AsyncStorage / filesystem). - Resumption. The selected transport type is persisted via the platform
StoreAdapter. For MWP, the pairing session — the relay channel and the dapp's ECIES keypair — is persisted in the same KV store by the Mobile Wallet ProtocolSessionStore, so the encrypted channel resumes across reloads without re-scanning. The CAIP-25 session itself (scopes + accounts) lives in the wallet and is re-fetched viawallet_getSession. On load the client restores the stored transport type (and, for the extension path, re-verifies extension presence) before resuming, so a connection survives page reloads without re-prompting. - Headless mode. With
ui.headless: true, the client skipsmultichain-uiand emitsdisplay_urievents so the dapp can render its own QR code. - Telemetry. Connection events are reported through
@metamask/analyticswith atransport_typeofbrowser(extension),mwp, orunknown— unlessanalytics.enabledisfalse.
- Root README — integration options, getting started, CSP requirements.
@metamask/connect-multichain— client API and the CAIP standards it implements.@metamask/connect-evm/@metamask/connect-solana— adapter APIs.@metamask/multichain-ui— connection UI components.@metamask/analytics— telemetry.