# Ethereum Provider API

Recommended Reading

We recommend that all web3 site developers read the Basic Usage section.

Upcoming Breaking Changes

On November 16, 2020, we are introducing changes that may break certain web3 sites. Please see the Upcoming Breaking Changes section for details.

MetaMask injects a global API into websites visited by its users at window.ethereum. This API allows websites to request users' Ethereum accounts, read data from blockchains the user is connected to, and suggest that the user sign messages and transactions. The presence of the provider object indicates an Ethereum user. We recommend using @metamask/detect-provider to detect our provider, on any platform or browser.

The Ethereum JavaScript provider API is specified by EIP-1193.

// this function detects most providers injected at window.ethereum
import detectEthereumProvider from '@metamask/detect-provider';
const provider = await detectEthereumProvider();
if (provider) {
  // From now on, this should always be true:
  // provider === window.ethereum
  startApp(provider); // initialize your app
} else {
  console.log('Please install MetaMask!');
}

# Table of Contents

# Upcoming Breaking Changes

Important Information

On November 16, 2020, we are making changes to our provider API that will be breaking for some web3 sites. These changes are upcoming, but you can prepare for them today. Follow this GitHub issue for updates.

All consumers of MetaMask's provider may be affected by the eth_chainId bug (see next subsection). Other than that, if you are new to using the provider, you do not have to worry about these changes, and can skip ahead to the next section.

# window.ethereum API Changes

At that time, we will:

  • Ensure that chain IDs returned by eth_chainId are not 0-padded
    • For example, instead of 0x01, we will always return 0x1, wherever the chain ID is returned or accessible.
    • Note that this only affects the default Ethereum chains, except Kovan, whose chain ID is formatted correctly (0x2a).
  • Stop emitting chainIdChanged, and instead emit chainChanged
  • Remove the following experimental methods:
    • ethereum._metamask.isEnabled
    • ethereum._metamask.isApproved
  • Remove the ethereum.publicConfigStore object
    • This object was, despite its name, never intended for public consumption. Its removal may affect those who do not use it directly, e.g. if another library you use relies on the object.

These changes may break your website. Please read our Migration Guide for more details.

# window.web3 Removal

TIP

If you do not use the window.web3 object injected by MetaMask, you will not be affected by these changes.

To understand why we're removing window.web3, please see this GitHub issue.

In the near future, we will:

  • Stop injecting window.web3 into web pages

If you rely on the window.web3 object currently injected by MetaMask, these changes will break your website. Please read our migration guide for more details.

# Basic Usage

For any non-trivial Ethereum web application — a.k.a. web3 site — to work, you will have to:

  • Detect the Ethereum provider (window.ethereum)
  • Detect which Ethereum network the user is connected to
  • Get the user's Ethereum account(s)

The snippet at the top of this page is sufficient for detecting the provider. You can learn how to accomplish the other two by reviewing the snippet in the Using the Provider section.

The provider API is all you need to create a full-featured web3 application.

That said, many developers use a convenience library, such as ethers and web3.js, instead of using the provider directly. If you are in need of higher-level abstractions than those provided by this API, we recommend that you use a convenience library.

# Chain IDs

WARNING

At the moment, the ethereum.chainId property and the chainChanged event should be preferred over the eth_chainId RPC method. Their chain ID values are correctly formatted, per the table below.

eth_chainId returns an incorrectly formatted (0-padded) chain ID for the chains in the table below, e.g. 0x01 instead of 0x1. See the Upcoming Breaking Changes section for details on when the eth_chainId RPC method will be fixed.

Custom RPC endpoints are not affected; they always return the chain ID specified by the user.

These are the IDs of the Ethereum chains that MetaMask supports by default. Consult chainid.network for more.

Hex Decimal Network
0x1 1 Ethereum Main Network (MainNet)
0x3 3 Ropsten Test Network
0x4 4 Rinkeby Test Network
0x5 5 Goerli Test Network
0x2a 42 Kovan Test Network

# Properties

# ethereum.isMetaMask

TIP

This property is not guaranteed to be correct for all providers. Non-MetaMask providers may also set this property to true.

true if the user has MetaMask installed.

# ethereum.chainId

WARNING

The value of this property can change at any time, and should not be exclusively relied upon. See the chainChanged event for details.

NOTE: See the Chain IDs section for important information about the MetaMask provider's chain IDs.

A hexadecimal string representing the current chain ID.

# ethereum.autoRefreshOnNetworkChange

WARNING

The value of this property will only affect MetaMask's behavior if window.web3 is accessed during the page lifecycle. When window.web3 is removed, either the effects of this property will change, or it will be removed.

As the consumer of this API, it is your responsbility to handle chain changes using the chainChanged event. We recommend reloading the page on chainChange unless you have good reason not to.

By default, this property is true.

If this property is truthy, MetaMask will reload the page in the following cases:

  • When the connected chain (network) changes, if window.web3 has been accessed during the page lifecycle
  • When window.web3 is accessed, if the connected chain (network) has changed during the page lifecycle

To disable this behavior, set this property to false immediately after detecting the provider:

ethereum.autoRefreshOnNetworkChange = false;

# Methods

# ethereum.isConnected()

TIP

Note that this method has nothing to do with the user's accounts.

You may often encounter the word "connected" in reference to whether a web3 site can access the user's accounts. In the provider interface, however, "connected" and "disconnected" refer to whether the provider can make RPC requests to the current chain.

ethereum.isConnected(): boolean;

Returns true if the provider is connected to the current chain, and false otherwise.

If the provider is not connected, the page will have to be reloaded in order for connection to be re-established. Please see the connect and disconnect events for more information.

# ethereum.request(args)

interface RequestArguments {
  method: string;
  params?: unknown[] | object;
}
ethereum.request(args: RequestArguments): Promise<unknown>;

Use request to submit RPC requests to Ethereum via MetaMask. It returns a Promise that resolves to the result of the RPC method call.

The params and return value will vary by RPC method. In practice, if a method has any params, they are almost always of type Array<any>.

If the request fails for any reason, the Promise will reject with an Ethereum RPC Error.

MetaMask supports most standardized Ethereum RPC methods, in addition to a number of methods that may not be supported by other wallets. See the MetaMask RPC API documentation for details.

# Example

params: [
  {
    from: '0xb60e8dd61c5d32be8058bb8eb970870f07233155',
    to: '0xd46e8dd67c5d32be8058bb8eb970870f07244567',
    gas: '0x76c0', // 30400
    gasPrice: '0x9184e72a000', // 10000000000000
    value: '0x9184e72a', // 2441406250
    data:
      '0xd46e8dd67c5d32be8d46e8dd67c5d32be8058bb8eb970870f072445675058bb8eb970870f072445675',
  },
];
ethereum
  .request({
    method: 'eth_sendTransaction',
    params,
  })
  .then((result) => {
    // The result varies by by RPC method.
    // For example, this method will return a transaction hash hexadecimal string on success.
  })
  .catch((error) => {
    // If the request fails, the Promise will reject with an error.
  });

# Events

The MetaMask provider implements the Node.js EventEmitter API. This sections details the events emitted via that API. There are innumerable EventEmitter guides elsewhere, but you can listen for events like this:

ethereum.on('accountsChanged', (accounts) => {
  // Handle the new accounts, or lack thereof.
  // "accounts" will always be an array, but it can be empty.
});
ethereum.on('chainChanged', (chainId) => {
  // Handle the new chain.
  // Correctly handling chain changes can be complicated.
  // We recommend reloading the page unless you have a very good reason not to.
  window.location.reload();
});

# connect

interface ConnectInfo {
  chainId: string;
}
ethereum.on('connect', handler: (connectInfo: ConnectInfo) => void);

The MetaMask provider emits this event when it first becomes able to submit RPC requests to a chain. We recommend using a connect event handler and the ethereum.isConnected() method in order to determine when/if the provider is connected.

# disconnect

ethereum.on('disconnect', handler: (error: ProviderRpcError) => void);

The MetaMask provider emits this event if it becomes unable to submit RPC requests to any chain. In general, this will only happen due to network connectivity issues or some unforeseen error.

Once disconnect has been emitted, the provider will not accept any new requests until the connection to the chain has been re-restablished, which requires reloading the page. You can also use the ethereum.isConnected() method to determine if the provider is disconnected.

# accountsChanged

ethereum.on('accountsChanged', handler: (accounts: Array<string>) => void);

The MetaMask provider emits this event whenever the return value of the eth_accounts RPC method changes. eth_accounts returns an array that is either empty or contains a single account address. The returned address, if any, is the address of the most recently used account that the caller is permitted to access. Callers are identified by their URL origin, which means that all sites with the same origin share the same permissions.

This means that accountsChanged will be emitted whenever the user's exposed account address changes.

TIP

We plan to allow the eth_accounts array to be able to contain multiple addresses in the near future.

# chainChanged

WARNING

NOTE: See the Chain IDs section for important information about the MetaMask provider's chain IDs.

ethereum.on('chainChanged', handler: (chainId: string) => void);

The MetaMask provider emits this event when the currently connected chain changes.

All RPC requests are submitted to the currently connected chain. Therefore, it's critical to keep track of the current chain ID by listening for this event.

We strongly recommend reloading the page on chain changes, unless absolutely necessary not to.

ethereum.on('chainChanged', (_chainId) => window.location.reload());

# message

interface ProviderMessage {
  type: string;
  data: unknown;
}
ethereum.on('message', handler: (message: ProviderMessage) => void);

The MetaMask provider emits this event when it receives some message that the consumer should be notified of. The kind of message is identified by the type string.

RPC subscription updates are a common use case for the message event. For example, if you create a subscription using eth_subscribe, each subscription update will be emitted as a message event with a type of eth_subscription.

# Errors

All errors thrown or returned by the MetaMask provider follow this interface:

interface ProviderRpcError extends Error {
  message: string;
  code: number;
  data?: unknown;
}

The ethereum.request(args) method throws errors eagerly. You can often use the error code property to determine why the request failed. Common codes and their meaning include:

  • 4001
    • The request was rejected by the user
  • -32602
    • The parameters were invalid
  • -32603
    • Internal error

For the complete list of errors, please see EIP-1193 and EIP-1474.

TIP

The eth-rpc-errors package implements all RPC errors thrown by the MetaMask provider, and can help you identify their meaning.

# Using the Provider

This snippet explains how to accomplish the three most common requirements for web3 sites:

  • Detect the Ethereum provider (window.ethereum)
  • Detect which Ethereum network the user is connected to
  • Get the user's Ethereum account(s)
/*****************************************/
/* Detect the MetaMask Ethereum provider */
/*****************************************/
import detectEthereumProvider from '@metamask/detect-provider';
// this returns the provider, or null if it wasn't detected
const provider = await detectEthereumProvider();
if (provider) {
  startApp(provider); // Initialize your app
} else {
  console.log('Please install MetaMask!');
}
function startApp(provider) {
  // If the provider returned by detectEthereumProvider is not the same as
  // window.ethereum, something is overwriting it, perhaps another wallet.
  if (provider !== window.ethereum) {
    console.error('Do you have multiple wallets installed?');
  }
  // Access the decentralized web!
}
/**********************************************************/
/* Handle chain (network) and chainChanged (per EIP-1193) */
/**********************************************************/
// Normally, we would recommend the 'eth_chainId' RPC method, but it currently
// returns incorrectly formatted chain ID values.
let currentChainId = ethereum.chainId;
ethereum.on('chainChanged', handleChainChanged);
function handleChainChanged(_chainId) {
  // We recommend reloading the page, unless you must do otherwise
  window.location.reload();
}
/***********************************************************/
/* Handle user accounts and accountsChanged (per EIP-1193) */
/***********************************************************/
let currentAccount = null;
ethereum
  .request({ method: 'eth_accounts' })
  .then(handleAccountsChanged)
  .catch((err) => {
    // Some unexpected error.
    // For backwards compatibility reasons, if no accounts are available,
    // eth_accounts will return an empty array.
    console.error(err);
  });
// Note that this event is emitted on page load.
// If the array of accounts is non-empty, you're already
// connected.
ethereum.on('accountsChanged', handleAccountsChanged);
// For now, 'eth_accounts' will continue to always return an array
function handleAccountsChanged(accounts) {
  if (accounts.length === 0) {
    // MetaMask is locked or the user has not connected any accounts
    console.log('Please connect to MetaMask.');
  } else if (accounts[0] !== currentAccount) {
    currentAccount = accounts[0];
    // Do any other work!
  }
}
/*********************************************/
/* Access the user's accounts (per EIP-1102) */
/*********************************************/
// You should only attempt to request the user's accounts in response to user
// interaction, such as a button click.
// Otherwise, you popup-spam the user like it's 1999.
// If you fail to retrieve the user's account(s), you should encourage the user
// to initiate the attempt.
document.getElementById('connectButton', connect);
function connect() {
  ethereum
    .request({ method: 'eth_requestAccounts' })
    .then(handleAccountsChanged)
    .catch((err) => {
      if (err.code === 4001) {
        // EIP-1193 userRejectedRequest error
        // If this happens, the user rejected the connection request.
        console.log('Please connect to MetaMask.');
      } else {
        console.error(err);
      }
    });
}

# Experimental API

WARNING

There is no guarantee that the methods and properties defined in this section will remain stable. Use it at your own risk.

We expose some experimental, MetaMask-specific methods under the ethereum._metamask property.

# Experimental Methods

# ethereum._metamask.isApproved() (TO BE REMOVED)

DANGER

This will be removed in the future.

Please see the Using the Provider section for the recommended way of keeping track of user accounts.

ethereum._metamask.isApproved(): Promise<boolean>;

This method returns a Promise that resolves to a boolean indicating if the caller has access to user accounts.

# ethereum._metamask.isEnabled() (TO BE REMOVED)

DANGER

This will be removed in the future.

Please see the Using the Provider section for the recommended way of keeping track of user accounts.

ethereum._metamask.isEnabled(): boolean;

This method returns a boolean indicating if the caller has access to user accounts.

# ethereum._metamask.isUnlocked()

ethereum._metamask.isUnlocked(): Promise<boolean>;

This method returns a Promise that resolves to a boolean indicating if MetaMask is unlocked by the user. MetaMask must be unlocked in order to perform any operation involving user accounts. Note that this method does not indicate if the user has exposed any accounts to the caller.

# Legacy API

WARNING

You should never rely on any of these methods, properties, or events in practice.

This section documents our legacy provider API. MetaMask only supported this API before the provider API was standardized via EIP-1193 in 2020. Because of this, you may find web3 sites that use this API, or other providers that implement it.

# Legacy Properties

# ethereum.networkVersion (DEPRECATED)

WARNING

You should always prefer the chain ID over the network ID.

If you must get the network ID, use ethereum.request({ method: 'net_version' }).

The value of this property can change at any time.

A decimal string representing the current blockchain's network ID.

# ethereum.selectedAddress (DEPRECATED)

WARNING

Use ethereum.request({ method: 'eth_accounts' }) instead.

The value of this property can change at any time.

Returns a hexadecimal string representing the user's "currently selected" address.

The "currently selected" address is the first item in the array returned by eth_accounts.

# Legacy Methods

# ethereum.enable() (DEPRECATED)

Alias for ethereum.request({ method: 'eth_requestAccounts' }).

# ethereum.sendAsync() (DEPRECATED)

WARNING

Use ethereum.request() instead.

interface JsonRpcRequest {
  id: string | undefined;
  jsonrpc: '2.0';
  method: string;
  params?: Array<any>;
}
interface JsonRpcResponse {
  id: string | undefined;
  jsonrpc: '2.0';
  method: string;
  result?: unknown;
  error?: Error;
}
type JsonRpcCallback = (error: Error, response: JsonRpcResponse) => unknown;
ethereum.sendAsync(payload: JsonRpcRequest, callback: JsonRpcCallback): void;

This is the ancestor of ethereum.request. It only works for JSON-RPC methods, and takes a JSON-RPC request payload object and an error-first callback function as its arguments.

See the Ethereum JSON-RPC API for details.

# ethereum.send() (DEPRECATED)

WARNING

Use ethereum.request() instead.

ethereum.send(
  methodOrPayload: string | JsonRpcRequest,
  paramsOrCallback: Array<unknown> | JsonRpcCallback,
): Promise<JsonRpcResponse> | void;

This method behaves unpredictably and should be avoided at all costs. It is essentially an overloaded version of ethereum.sendAsync().

ethereum.send() can be called in three different ways:

// 1.
ethereum.send(payload: JsonRpcRequest, callback: JsonRpcCallback): void;
// 2.
ethereum.send(method: string, params?: Array<unknown>): Promise<JsonRpcResponse>;
// 3.
ethereum.send(payload: JsonRpcRequest): unknown;

You can think of these signatures as follows:

  1. This signature is exactly like ethereum.sendAsync()

  2. This signature is like an async ethereum.sendAsync() with method and params as arguments, instead of a JSON-RPC payload and callback

  3. This signature enables you to call the following RPC methods synchronously:

    • eth_accounts
    • eth_coinbase
    • eth_uninstallFilter
    • net_version

# Legacy Events

# close (DEPRECATED)

WARNING

Use disconnect instead.

ethereum.on('close', handler: (error: Error) => void);

# chainIdChanged (DEPRECATED)

WARNING

Use chainChanged instead.

Misspelled alias of chainChanged.

ethereum.on('chainChanged', handler: (chainId: string) => void);

# networkChanged (DEPRECATED)

WARNING

Use chainChanged instead.

Like chainChanged, but with the networkId instead. Network IDs are insecure, and were effectively deprecated in favor of chain IDs by EIP-155. Avoid using them unless you know what you are doing.

ethereum.on('networkChanged', handler: (networkId: string) => void);

# notification (DEPRECATED)

WARNING

Use message instead.

ethereum.on('notification', handler: (payload: any) => void);