> For the complete documentation index, see [llms.txt](/llms.txt).

# Migrate from the legacy SDK

This guide walks you through migrating from the legacy MetaMask SDK (`@metamask/sdk` or `@metamask/sdk-react`) to MetaMask Connect EVM (`@metamask/connect-evm`).

MetaMask Connect EVM is a rewrite of the legacy SDK, built on the [CAIP-25 Multichain API](https://github.com/ChainAgnostic/CAIPs/blob/main/CAIPs/caip-25.md). Key enhancements include:

- Async initialization.
- A singleton client.
- Built-in support for EVM, [Solana](/metamask-connect/solana/), and [multichain](/metamask-connect/multichain/) sessions.

## Steps[​](#steps "Direct link to Steps")

### 1\. Replace packages[​](#1-replace-packages "Direct link to 1. Replace packages")

Remove the old packages and install the new ones:

- npm
- Yarn
- pnpm
- Bun

```
# Remove old
npm uninstall @metamask/sdk
# For React Native, remove
npm uninstall @metamask/sdk-react

# Install new (EVM only)
npm install @metamask/connect-evm

```

```
# Remove old
yarn remove @metamask/sdk
# For React Native, remove
yarn remove @metamask/sdk-react

# Install new (EVM only)
yarn add @metamask/connect-evm

```

```
# Remove old
pnpm remove @metamask/sdk
# For React Native, remove
pnpm remove @metamask/sdk-react

# Install new (EVM only)
pnpm add @metamask/connect-evm

```

```
# Remove old
bun remove @metamask/sdk
# For React Native, remove
bun remove @metamask/sdk-react

# Install new (EVM only)
bun add @metamask/connect-evm

```

### 2\. Update imports[​](#2-update-imports "Direct link to 2. Update imports")

Replace `@metamask/sdk` and `@metamask/sdk-react` imports with the new `@metamask/connect-evm` package.

note

`@metamask/sdk-react` has no direct replacement. If you were using `MetaMaskProvider` and `useSDK`, migrate to [Wagmi hooks](/metamask-connect/evm/quickstart/wagmi/) or manage the client instance in your own React context (see [React context pattern](#react-context-pattern-replacing-usesdk) below).

**Old: (remove these imports)**

```
- import { MetaMaskSDK } from '@metamask/sdk'
- import { MetaMaskProvider, useSDK } from '@metamask/sdk-react' // For React Native, remove these imports

```

**New (EVM):**

```
+ import { createEVMClient, getInfuraRpcUrls } from '@metamask/connect-evm'

```

### 3\. Update initialization[​](#3-update-initialization "Direct link to 3. Update initialization")

Replace the `MetaMaskSDK` constructor and `init` call with [createEVMClient](/metamask-connect/evm/reference/methods/#createevmclient), which handles initialization in a single async step.

caution

`createEVMClient` is async, so always `await` it before accessing the client. The client is also a singleton. Calling `createEVMClient` multiple times merges options into the same instance. Do not recreate it on every render.

**Old:**

```
- const sdk = new MetaMaskSDK({
-  dappMetadata: {
-    name: 'My Dapp',
-    url: window.location.href,
-  },
-   infuraAPIKey: 'YOUR_INFURA_KEY',
-   readonlyRPCMap: {
-     '0x89': 'https://polygon-rpc.com',
-   },
-  headless: true,
-  extensionOnly: false,
-  openDeeplink: link => window.open(link, '_blank'),
- })
- await sdk.init()

```

**New:**

```
+ const client = await createEVMClient({
+  dapp: {
+    name: 'My Dapp',
+    url: window.location.href,
+  },
+  api: {
+    supportedNetworks: {
+      ...getInfuraRpcUrls({ infuraApiKey: 'YOUR_INFURA_KEY', chainIds: ['0x1', '0xaa36a7'] }),
+      '0x89': 'https://polygon-rpc.com',
+    },
+  },
+  ui: {
+    headless: true,
+    preferExtension: false,
+  },
+  mobile: {
+    preferredOpenLink: (link: string) => window.open(link, '_blank'),
+  },
+ })

```

#### Option mapping[​](#option-mapping "Direct link to Option mapping")

Use the following table to map `MetaMaskSDK` configuration options to their equivalents in `createEVMClient`. The table includes renamed options, options that moved into grouped objects (for example, `ui` and `mobile`), and options that MetaMask Connect EVM no longer exposes.

| Old (MetaMaskSDK)      | New (createEVMClient)                                                                                                     | Notes                                                      |
| ---------------------- | ------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------- |
| dappMetadata           | dapp                                                                                                                      | Same shape: { name, url, iconUrl }                         |
| dappMetadata.name      | dapp.name                                                                                                                 | Required                                                   |
| dappMetadata.url       | dapp.url                                                                                                                  | Auto-set in browsers; required in Node.js and React Native |
| infuraAPIKey           | api.supportedNetworks via [getInfuraRpcUrls({ infuraApiKey })](/metamask-connect/evm/reference/methods/#getinfurarpcurls) | Helper generates RPC URLs for all Infura-supported chains  |
| readonlyRPCMap         | api.supportedNetworks                                                                                                     | Merge into the same object                                 |
| headless               | ui.headless                                                                                                               | Same behavior                                              |
| extensionOnly          | ui.preferExtension                                                                                                        | true prefers extension (default); not the same as "only"   |
| openDeeplink           | mobile.preferredOpenLink                                                                                                  | Same signature: (deeplink: string) => void                 |
| useDeeplink            | mobile.useDeeplink                                                                                                        | Same behavior                                              |
| timer                  | Removed                                                                                                                   | No longer configurable                                     |
| enableAnalytics        | Removed                                                                                                                   | No longer available                                        |
| communicationServerUrl | Removed                                                                                                                   | Managed internally                                         |
| storage                | Removed                                                                                                                   | Managed internally                                         |

### 4\. Update connection flow[​](#4-update-connection-flow "Direct link to 4. Update connection flow")

In MetaMask Connect EVM, you request chain permissions during [connect](/metamask-connect/evm/reference/methods/#connect) and receive the connected accounts and selected chain ID in a single response. This replaces the previous flow where you connected first and then made a separate JSON-RPC request for `eth_chainId`.

**Old:**

```
- const accounts = await sdk.connect()
- const chainId = await sdk.getProvider().request({ method: 'eth_chainId' })

```

**New:**

```
+ const { accounts, chainId } = await client.connect({
+   chainIds: ['0x1'],
+ })

```

`connect` now returns an object with both `accounts` and `chainId` in a single call. The `chainIds` parameter specifies which chains to request permission for. Ethereum Mainnet (`0x1`) is always included regardless of what you pass.

note

Chain IDs must be hex strings. Use `'0x1'`, not `1` or `'1'`, in `chainIds` and `supportedNetworks` keys.

#### Connect-and-sign shortcut[​](#connect-and-sign-shortcut "Direct link to Connect-and-sign shortcut")

Use [connectAndSign](/metamask-connect/evm/reference/methods/#connectandsign) to connect and sign a `personal_sign` message in one user approval. The method returns `{ accounts, chainId, signature }`:

```
const { accounts, chainId, signature } = await client.connectAndSign({
  message: 'Sign in to My Dapp',
  chainIds: ['0x1'],
})

```

Breaking change in `@metamask/connect-evm` 1.0.0

`connectAndSign` previously returned the signature as a bare string. It now returns an object, so read `.signature` from the returned object to get the signed value.

#### Connect-and-execute shortcut[​](#connect-and-execute-shortcut "Direct link to Connect-and-execute shortcut")

Use [connectWith](/metamask-connect/evm/reference/methods/#connectwith) to connect and execute any JSON-RPC method in a single user approval. The method returns `{ accounts, chainId, result }`:

```
const {
  accounts,
  chainId,
  result: txHash,
} = await client.connectWith({
  method: 'eth_sendTransaction',
  params: account => [{ from: account, to: '0x...', value: '0x0' }],
  chainIds: ['0x1'],
})

```

Breaking change in `@metamask/connect-evm` 1.0.0

`connectWith` previously returned the raw RPC result. It now returns an object, so read `.result` from the returned object to get the RPC response value.

React Native polyfills

Browser-based setups (Vite, Webpack) work without polyfills. If you are migrating a React Native app and encounter errors referencing `Buffer`, `crypto`, `stream`, or `Event is not defined`, see [React Native Metro polyfill issues](/metamask-connect/troubleshooting/metro-polyfill-issues/).

### 5\. Update provider access[​](#5-update-provider-access "Direct link to 5. Update provider access")

In MetaMask Connect EVM, [client.getProvider](/metamask-connect/evm/reference/methods/#getprovider) returns an EIP-1193 provider. You no longer use the `SDKProvider` returned by `sdk.getProvider`.

**Old:**

```
- const provider = sdk.getProvider() // SDKProvider (may be undefined)
- await provider.request({ method: 'eth_chainId' })

```

**New:**

```
+ const provider = client.getProvider() // EIP-1193 provider (always exists)
+ await provider.request({ method: 'eth_chainId' })

```

Key differences:

- The provider is a standard EIP-1193 provider, not the custom `SDKProvider`.
- The provider is available immediately after `createEVMClient` resolves, even before `connect`.
- Read-only calls (like `eth_blockNumber`) work immediately against `supportedNetworks` RPCs. Account-dependent calls require `connect` first.
- `client.getProvider` never returns `undefined`.

### 6\. Update event handling[​](#6-update-event-handling "Direct link to 6. Update event handling")

EIP-1193 provider events work the same way:

```
const provider = client.getProvider()
provider.on('chainChanged', chainId => {
  /* hex string */
})
provider.on('accountsChanged', accounts => {
  /* address array */
})
provider.on('disconnect', () => {
  /* ... */
})

```

MetaMask Connect EVM also supports SDK-level event handlers that you register during initialization. The `connect` handler receives both `chainId` and `accounts` (a MetaMask Connect extension of the standard EIP-1193 `connect` event, which only includes `chainId`). See the [Ethereum provider API events](/metamask-connect/evm/reference/provider-api/#events) for the full event reference.

```
const client = await createEVMClient({
  dapp: { name: 'My Dapp' },
  api: {
    supportedNetworks: {
      '0x1': 'https://mainnet.infura.io/v3/YOUR_KEY',
    },
  },
  eventHandlers: {
    displayUri: uri => {
      /* render QR code for mobile connection */
    },
    connect: ({ chainId, accounts }) => {
      /* connection established */
    },
    disconnect: () => {
      /* disconnected */
    },
  },
})

```

You can also listen for the `display_uri` event on the provider for custom QR code UI.

Event naming

The `eventHandlers` option uses camel case (`displayUri`), while the provider event uses snake case (`display_uri`). Both deliver the same URI string for QR code rendering.

```
const provider = client.getProvider()
provider.on('display_uri', uri => {
  /* render custom QR code */
})

```

### 7\. Adopt new capabilities[​](#7-adopt-new-capabilities "Direct link to 7. Adopt new capabilities")

MetaMask Connect EVM introduces features that are not available in `@metamask/sdk`:

| Capability                                                                   | Description                                                                                                                                                                      |
| ---------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **Multichain client**                                                        | [createMultichainClient](/metamask-connect/multichain/reference/methods/#createmultichainclient) from @metamask/connect-multichain supports CAIP-25 scopes across EVM and Solana |
| [invokeMethod](/metamask-connect/multichain/reference/methods/#invokemethod) | Call RPC methods on specific CAIP-2 scopes without switching chains                                                                                                              |
| **Solana support**                                                           | [createSolanaClient](/metamask-connect/solana/reference/methods/#createsolanaclient) from @metamask/connect-solana with Wallet Standard adapter                                  |
| [connectAndSign](/metamask-connect/evm/reference/methods/#connectandsign)    | Connect and sign a message in a single user approval                                                                                                                             |
| [connectWith](/metamask-connect/evm/reference/methods/#connectwith)          | Connect and execute any RPC method in a single user approval                                                                                                                     |
| **Partial disconnect**                                                       | [disconnect(scopes)](/metamask-connect/multichain/reference/methods/#disconnect) revokes specific CAIP scopes while keeping others active                                        |
| **Singleton client**                                                         | Subsequent createEVMClient calls merge options into the existing instance                                                                                                        |

#### Next step: Go multichain[​](#next-step-go-multichain "Direct link to Next step: Go multichain")

If your dapp supports (or plans to support) both EVM and Solana, consider upgrading to the [multichain client](/metamask-connect/multichain/quickstart/javascript/). The EVM client is built on top of `createMultichainClient` internally, so the upgrade is straightforward:

```
import { createMultichainClient } from '@metamask/connect-multichain'

const multichainClient = await createMultichainClient({
  dapp: { name: 'My Dapp', url: window.location.href },
  api: {
    supportedNetworks: {
      'eip155:1': 'https://mainnet.infura.io/v3/YOUR_INFURA_API_KEY',
      'solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp':
        'https://solana-mainnet.infura.io/v3/YOUR_INFURA_API_KEY',
    },
  },
})

// EVM call
await multichainClient.invokeMethod({
  scope: 'eip155:1',
  request: { method: 'eth_getBalance', params: ['0x...', 'latest'] },
})

// Solana call
await multichainClient.invokeMethod({
  scope: 'solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp',
  request: { method: 'getBalance', params: { pubkey: '...' } },
})

```

See the [multichain quickstart](/metamask-connect/multichain/quickstart/javascript/) for a full walkthrough.

## Full option mapping[​](#full-option-mapping "Direct link to Full option mapping")

| Old (@metamask/sdk)    | New (@metamask/connect-evm)                                                                                              | Status                                |
| ---------------------- | ------------------------------------------------------------------------------------------------------------------------ | ------------------------------------- |
| new MetaMaskSDK(opts)  | await createEVMClient(opts)                                                                                              | Renamed, async                        |
| sdk.init               | Not needed                                                                                                               | Init happens in createEVMClient       |
| sdk.connect            | client.connect({ chainIds })                                                                                             | Returns { accounts, chainId }         |
| sdk.getProvider        | client.getProvider                                                                                                       | Returns EIP-1193 provider             |
| sdk.disconnect         | client.disconnect                                                                                                        | Same, plus partial disconnect support |
| dappMetadata           | dapp                                                                                                                     | Renamed                               |
| infuraAPIKey           | [getInfuraRpcUrls({ infuraApiKey })](/metamask-connect/evm/reference/methods/#getinfurarpcurls) in api.supportedNetworks | Helper function                       |
| readonlyRPCMap         | api.supportedNetworks                                                                                                    | Merged with Infura URLs               |
| headless               | ui.headless                                                                                                              | Moved to ui namespace                 |
| extensionOnly          | ui.preferExtension                                                                                                       | Renamed, slightly different semantics |
| openDeeplink           | mobile.preferredOpenLink                                                                                                 | Moved to mobile namespace             |
| useDeeplink            | mobile.useDeeplink                                                                                                       | Moved to mobile namespace             |
| SDKProvider            | EIP1193Provider                                                                                                          | Standard provider interface           |
| timer                  | Removed                                                                                                                  | —                                     |
| enableAnalytics        | Removed                                                                                                                  | —                                     |
| communicationServerUrl | Removed                                                                                                                  | —                                     |
| storage                | Removed                                                                                                                  | —                                     |

### React context pattern (replacing `useSDK`)[​](#react-context-pattern-replacing-usesdk "Direct link to react-context-pattern-replacing-usesdk")

If you were using `@metamask/sdk-react`, you can create a minimal React context to hold the EVM client instance:

```
import { createContext, useContext, useEffect, useRef, useState } from 'react'
import { createEVMClient, getInfuraRpcUrls } from '@metamask/connect-evm'
import type { MetamaskConnectEVM } from '@metamask/connect-evm'

const EVMContext = createContext<MetamaskConnectEVM | null>(null)

export function EVMProvider({ children }: { children: React.ReactNode }) {
  const [client, setClient] = useState<MetamaskConnectEVM | null>(null)
  const initialized = useRef(false)

  useEffect(() => {
    if (initialized.current) return
    initialized.current = true
    createEVMClient({
      dapp: { name: 'My Dapp', url: window.location.href },
      api: { supportedNetworks: getInfuraRpcUrls({ infuraApiKey: 'YOUR_INFURA_API_KEY' }) },
    }).then(setClient)
  }, [])

  return <EVMContext.Provider value={client}>{children}</EVMContext.Provider>
}

export function useEVMClient() {
  return useContext(EVMContext)
}

```

For a full-featured solution, consider using [Wagmi](/metamask-connect/evm/quickstart/wagmi/) with the MetaMask connector, which provides React hooks out of the box.

tip

Test on both extension and mobile. The transport layer has changed, and behavior differences may surface in one environment but not the other.

## Next steps[​](#next-steps "Direct link to Next steps")

- [Connect to EVM quickstart](/metamask-connect/evm/quickstart/javascript/)
- [Manage user accounts](/metamask-connect/evm/guides/manage-user-accounts/)
- [Send transactions](/metamask-connect/evm/guides/send-transactions/)
- [Production readiness checklist](/metamask-connect/evm/guides/best-practices/production-readiness/)
- [React Native polyfill troubleshooting](/metamask-connect/troubleshooting/metro-polyfill-issues/)
