EIP-1193 compatible interface for connecting to MetaMask and interacting with Ethereum Virtual Machine (EVM) networks.
@metamask/connect-evm provides a modern replacement for MetaMask SDK V1, offering enhanced functionality and cross-platform compatibility. It wraps the Multichain SDK to provide a simplified, EIP-1193 compliant API for dapp developers.
- EIP-1193 Provider Interface - Seamless integration with existing dapp code using the standard Ethereum provider interface
- Cross-Platform Support - Works with browser extensions and mobile applications
- React Native Support - Native mobile deeplink handling via
preferredOpenLinkoption
yarn add @metamask/connect-evmor
npm install @metamask/connect-evmimport { createEVMClient, getInfuraRpcUrls } from '@metamask/connect-evm';
const client = await createEVMClient({
dapp: {
name: 'My DApp',
url: 'https://mydapp.com',
},
api: {
supportedNetworks: {
// use the `getInfuraRpcUrls` helper to generate a map of Infura RPC endpoints
...getInfuraRpcUrls({ infuraApiKey: INFURA_API_KEY }),
// or specify your own CAIP Chain ID to rpc endpoint mapping
// Hex chain IDs mapped to RPC URLs
'0x1': 'https://mainnet.infura.io/v3/YOUR_KEY', // Ethereum Mainnet
'0x89': 'https://polygon-mainnet.infura.io/v3/YOUR_KEY', // Polygon
},
},
});
// Connect to MetaMask
let accounts, chainId;
try {
({ accounts, chainId } = await client.connect({ chainIds: ['0x1', '0x89'] })); // Connect to Ethereum Mainnet and Polygon
} catch (error) {
if (error.code === 4001) {
console.log('User rejected the connection request');
} else if (error.code === -32002) {
console.log('Connection request already pending');
}
}
console.log({ accounts }); // The connected accounts where the first account is the selected account
console.log({ chainId }); // The currently active chainId
// Get the EIP-1193 provider
const provider = client.getProvider();
// Sign a message
const signedMessage = await provider.request({
method: 'personal_sign',
params: ['0x0', accounts[0]],
});When using @metamask/connect-evm in React Native, the standard browser deeplink mechanism (window.location.href) doesn't work. Instead, you can provide a custom preferredOpenLink function via the mobile option to handle deeplinks using React Native's Linking API.
import { Linking } from 'react-native';
import { createEVMClient } from '@metamask/connect-evm';
const client = await createEVMClient({
dapp: {
name: 'My React Native DApp',
url: 'https://mydapp.com',
},
api: {
// Chain IDs are specified in hex format
supportedNetworks: {
'0x1': 'https://mainnet.infura.io/v3/YOUR_KEY',
},
},
// React Native: use Linking.openURL for deeplinks
mobile: {
preferredOpenLink: (deeplink: string) => {
Linking.openURL(deeplink).catch((err) => {
console.error('Failed to open deeplink:', err);
});
},
},
});The mobile.preferredOpenLink option is checked before falling back to browser-based deeplink methods, making it the recommended approach for React Native applications.
const provider = client.getProvider();
// Send transaction
const txHash = await provider.request({
method: 'eth_sendTransaction',
params: [
{
from: accounts[0],
to: '0x...',
value: '0x...',
},
],
});
// Call contract method
const result = await provider.request({
method: 'eth_call',
params: [
{
to: '0x...',
data: '0x...',
},
],
});Check out the playground examples for a complete React implementation.
This package is part of the MetaMask Connect monorepo. From the repo root:
# Run linting
yarn workspace @metamask/connect-evm run lint
# Run type checking
yarn workspace @metamask/connect-evm run check
# Format code
yarn workspace @metamask/connect-evm run format:fix
# Run tests
yarn workspace @metamask/connect-evm run testThis package is written in TypeScript and includes full type definitions. No additional @types package is required.
Factory function to create a new MetaMask Connect EVM instance.
| Option | Type | Required | Description |
|---|---|---|---|
dapp.name |
string |
Yes | Name of your dApp |
api.supportedNetworks |
Record<Hex, string> |
Yes | Map of hex chain IDs to RPC URLs |
dapp.url |
string |
No | URL of your dApp |
dapp.iconUrl |
string |
No | Icon URL for your dApp |
ui.headless |
boolean |
No | Run without UI (for custom QR implementations) |
ui.preferExtension |
boolean |
No | Prefer browser extension over mobile (default: true) |
ui.showInstallModal |
boolean |
No | Show installation modal for desktop |
mobile.preferredOpenLink |
(deeplink: string, target?: string) => void |
No | Custom deeplink handler |
mobile.useDeeplink |
boolean |
No | Use metamask:// instead of universal links |
transport.extensionId |
string |
No | Custom extension ID |
transport.onNotification |
(notification: unknown) => void |
No | Notification handler |
eventHandlers |
Partial<EventHandlers> |
No | Event handlers for provider events |
debug |
boolean |
No | Enable debug logging |
Promise<MetamaskConnectEVM> - A fully initialized SDK instance.
const client = await createEVMClient({
dapp: { name: 'My DApp', url: 'https://mydapp.com' },
api: {
supportedNetworks: {
'0x1': 'https://mainnet.infura.io/v3/KEY',
'0x89': 'https://polygon-mainnet.infura.io/v3/KEY',
},
},
eventHandlers: {
accountsChanged: (accounts) => console.log('Accounts:', accounts),
chainChanged: (chainId) => console.log('Chain:', chainId),
},
debug: true,
});The main SDK class providing EVM connectivity.
Connects to MetaMask wallet.
Parameters
| Name | Type | Required | Description |
|---|---|---|---|
options.chainIds |
Hex[] |
No | Array of hex chain IDs to request permission for (defaults to ['0x1'] if not provided). Note: Ethereum mainnet (0x1) is always included in the permission request regardless of what is passed. The first entry in the array becomes the active chain returned by the call. |
options.account |
string |
No | Specific account address to connect |
options.forceRequest |
boolean |
No | Force a new connection request even if already connected |
Returns
Promise<{ accounts: Address[]; chainId: Hex }> - The connected accounts and active chain ID.
const { accounts, chainId } = await client.connect({
chainIds: ['0x1', '0x89'], // Ethereum Mainnet and Polygon
account: '0x...',
forceRequest: false,
});Connects and immediately signs a message using personal_sign.
Parameters
| Name | Type | Required | Description |
|---|---|---|---|
options.message |
string |
Yes | The message to sign after connecting |
options.chainIds |
Hex[] |
No | Hex chain IDs to request permission for (defaults to ['0x1']). The first entry becomes the active chain used for signing. |
Returns
Promise<string> - The signature as a hex string.
const signature = await client.connectAndSign({
message: 'Sign this message',
chainIds: ['0x1'],
});Connects and immediately invokes a method with specified parameters.
Parameters
| Name | Type | Required | Description |
|---|---|---|---|
options.method |
string |
Yes | The RPC method name to invoke |
options.params |
unknown[] | ((account: Address) => unknown[]) |
Yes | Method parameters, or a function that receives the connected account and returns params |
options.chainIds |
Hex[] |
No | Hex chain IDs to request permission for (defaults to ['0x1']). The first entry becomes the active chain used for the method call. |
options.account |
string |
No | Specific account to connect |
options.forceRequest |
boolean |
No | Force a new connection request |
Returns
Promise<unknown> - The result of the method invocation.
const result = await client.connectWith({
method: 'eth_sendTransaction',
params: (account) => [
{
from: account,
to: '0x...',
value: '0x1',
},
],
chainIds: ['0x1'],
});Disconnects all EVM (eip155) scopes from MetaMask and cleans up local state. This only revokes the EVM-specific scopes currently held in the session; it does not terminate the broader multichain session if non-EVM scopes are also active.
Parameters
None.
Returns
Promise<void>
await client.disconnect();Switches to a different chain. Will attempt to add the chain if not configured in the wallet.
Parameters
| Name | Type | Required | Description |
|---|---|---|---|
options.chainId |
Hex |
Yes | The hex chain ID to switch to |
options.chainConfiguration |
AddEthereumChainParameter |
No | Chain configuration to use if the chain needs to be added |
Returns
Promise<void>
await client.switchChain({
chainId: '0x89',
chainConfiguration: {
chainId: '0x89',
chainName: 'Polygon',
nativeCurrency: { name: 'MATIC', symbol: 'MATIC', decimals: 18 },
rpcUrls: ['https://polygon-rpc.com'],
},
});Returns the EIP-1193 provider instance.
Parameters
None.
Returns
EIP1193Provider - The EIP-1193 compliant provider.
const provider = client.getProvider();Returns the currently selected chain ID.
Parameters
None.
Returns
Hex | undefined - The currently selected chain ID as a hex string, or undefined if not connected.
const chainId = client.getChainId(); // e.g., '0x1'Returns the currently selected account.
Parameters
None.
Returns
Address | undefined - The currently selected account address, or undefined if not connected.
const account = client.getAccount(); // e.g., '0x...'| Property | Type | Description |
|---|---|---|
accounts |
Address[] |
Currently permitted accounts |
selectedAccount |
Address | undefined |
Currently selected account |
selectedChainId |
Hex | undefined |
Currently selected chain ID (hex) |
status |
ConnectionStatus |
Connection status ( 'loaded', 'pending', 'connecting', 'connected', 'disconnected') |
EIP-1193 compliant provider for making Ethereum JSON-RPC requests.
Makes an Ethereum JSON-RPC request.
Parameters
| Name | Type | Required | Description |
|---|---|---|---|
args.method |
string |
Yes | The RPC method name |
args.params |
unknown |
No | The method parameters |
Returns
Promise<unknown> - The result of the RPC call.
const result = await provider.request({
method: 'eth_getBalance',
params: ['0x...', 'latest'],
});Legacy method for JSON-RPC requests with callback support.
Parameters
| Name | Type | Required | Description |
|---|---|---|---|
request.method |
string |
Yes | The RPC method name |
request.params |
unknown |
No | The method parameters |
request.id |
number | string |
No | Request ID (defaults to 1) |
request.jsonrpc |
'2.0' |
No | JSON-RPC version |
callback |
JsonRpcCallback |
No | Optional callback function |
Returns
Promise<JsonRpcResponse> | void - Returns a promise if no callback is provided, otherwise void.
provider.sendAsync(
{ method: 'eth_accounts', params: [] },
(error, response) => {
if (error) console.error(error);
else console.log(response.result);
},
);Legacy synchronous-style method for JSON-RPC requests.
Parameters
| Name | Type | Required | Description |
|---|---|---|---|
request.method |
string |
Yes | The RPC method name |
request.params |
unknown |
No | The method parameters |
callback |
JsonRpcCallback |
Yes | Callback function to receive the response |
Returns
void
The provider extends EventEmitter and emits standard EIP-1193 events:
| Event | Payload | Description |
|---|---|---|
connect |
{ chainId: string } |
Emitted when connected |
disconnect |
- | Emitted when disconnected |
accountsChanged |
Address[] |
Emitted when accounts change |
chainChanged |
Hex |
Emitted when chain changes |
message |
{ type: string, data: unknown } |
Emitted for provider messages |
display_uri |
string |
Emitted with QR code URI for custom UI |
provider.on('accountsChanged', (accounts) => {
console.log('New accounts:', accounts);
});
provider.on('chainChanged', (chainId) => {
console.log('New chain:', chainId);
});
provider.on('display_uri', (uri) => {
// Display custom QR code with this URI
});| Property | Type | Description |
|---|---|---|
accounts |
Address[] |
Currently permitted accounts |
selectedAccount |
Address | undefined |
Currently selected account |
selectedChainId |
Hex | undefined |
Currently selected chain ID |
chainId |
Hex | undefined |
Alias for selectedChainId (legacy compatibility) |
Helper function to generate EVM Infura RPC URLs for common networks keyed by hex chain ID.
Parameters
| Name | Type | Required | Description |
|---|---|---|---|
infuraApiKey |
string |
Yes | Your Infura API key |
chainIds |
Hex[] |
No | Hex chain IDs to filter the output |
Returns
A map of hex chain IDs to Infura RPC URLs. See https://docs.metamask.io/services
import { getInfuraRpcUrls } from '@metamask/connect-evm';
// Get all supported Infura RPC URLs
const rpcUrls = getInfuraRpcUrls({ infuraApiKey: 'YOUR_INFURA_KEY' });
// Returns: { '0x1': 'https://mainnet.infura.io/v3/KEY', ... }
// Filter to specific chains
const filtered = getInfuraRpcUrls({
infuraApiKey: 'YOUR_INFURA_KEY',
chainIds: ['0x1', '0x89'],
});
// Returns: { '0x1': 'https://mainnet.infura.io/v3/KEY', '0x89': 'https://polygon-mainnet.infura.io/v3/KEY' }type EventHandlers = {
connect: (result: { chainId: Hex; accounts: Address[] }) => void;
disconnect: () => void;
accountsChanged: (accounts: Address[]) => void;
chainChanged: (chainId: Hex) => void;
displayUri: (uri: string) => void;
connectAndSign: (result: {
accounts: Address[];
chainId: Hex;
signResponse: string;
}) => void;
connectWith: (result: {
accounts: Address[];
chainId: Hex;
connectWithResponse: unknown;
}) => void;
};type AddEthereumChainParameter = {
chainId?: string;
chainName?: string;
nativeCurrency?: {
name?: string;
symbol?: string;
decimals?: number;
};
rpcUrls?: string[];
blockExplorerUrls?: string[];
iconUrls?: string[];
};This package is part of a monorepo. Instructions for contributing can be found in the monorepo README.
MIT