Alert Source Discuss
⚠️ Draft Standards Track: Interface

EIP-6963: Multi Injected Provider Discovery

Using window events to announce injected wallet providers

Authors Pedro Gomes (@pedrouid), Kosala Hemachandra (@kvhnuke), Richard Moore (@ricmoo), Gregory Markou (@GregTheGreek), Kyle Den Hartog (@kdenhartog), Glitch (@glitch-txs), Jake Moxey (@jxom), Pierre Bertet (@bpierre), Darryl Yeo (@darrylyeo)
Created 2023-05-01
Discussion Link https://ethereum-magicians.org/t/eip-6963-multi-injected-provider-interface-aka-mipi/14076
Requires EIP-1193

Abstract

An alternative discovery mechanism to window.ethereum for EIP-1193 providers which supports discovering multiple injected wallet providers in a web page using Javascript’s window events.

Motivation

Currently, wallet providers that offer browser extensions must inject their Ethereum providers (EIP-1193) into the same window object window.ethereum; however, this creates conflicts for users that may install more than one browser extension.

Browser extensions are loaded in the web page in an unpredictable and unstable order, resulting in a race condition where the user does not have control over which wallet provider is selected to expose the Ethereum interface under the window.ethereum object. Instead, the last wallet to load usually wins.

This results not only in a degraded user experience but also increases the barrier to entry for new browser extensions as users are forced to only install one browser extension at a time.

Some browser extensions attempt to counteract this problem by delaying their injection to overwrite the same window.ethereum object which creates an unfair competition for wallet providers and lack of interoperability.

In this proposal, we present a solution that focuses on optimizing the interoperability of multiple wallet providers. This solution aims to foster fairer competition by reducing the barriers to entry for new wallet providers, along with enhancing the user experience on Ethereum networks.

This is achieved by introducing a set of window events to provide a two-way communication protocol between Ethereum libraries and injected scripts provided by browser extensions thus enabling users to select their wallet of choice.

Specification

The key words “MUST”, “MUST NOT”, “REQUIRED”, “SHALL”, “SHALL NOT”, “SHOULD”, “SHOULD NOT”, “RECOMMENDED”, “MAY”, and “OPTIONAL” in this document are to be interpreted as described in RFC 2119.

Definitions

Wallet: A user agent that manages keys and facilitates transactions with Ethereum.

Decentralized Application (DApp): A web page that relies upon one or many Web3 platform APIs which are exposed to the web page via the Wallet.

Provider Discovery Library: A library or piece of software that assists a DApp to interact with the Wallet.

Provider Info

Each wallet provider will be announced with the following interface EIP6963ProviderInfo. The values in the EIP6963ProviderInfo MUST be used as follows:

  • uuid - a globally unique of the wallet provider that MUST be (UUIDv41 compliant) to uniquely distinguish different EIP-1193 provider sessions. The cryptographic uniqueness provided by UUIDv41 guarantees that two independent EIP6963ProviderInfo objects can be separately identified.
  • name - A human-readable local alias of the wallet provider to be displayed to the user on the DApp. (e.g. DopeWalletExtension or AwesomeWallet)
  • icon - A URI2 pointing to an image. Icon images MUST be square with 96x96px minimum resolution
/**
 * Represents the assets needed to display a wallet
 */
interface EIP6963ProviderInfo {
  uuid: string;
  name: string;
  icon: string;
}

Images/Icons

// TODO for @kdenhartog: This section should be updated to require support of SVGs instead of arbitrary data URIs or URLs that point to images.

A uri encoded image was chosen to enable flexibility for multiple protocols for fetching and rendering icons, for example:

# svg (data uri)
data:image/svg+xml,<svg version="1.1" xmlns="http://www.w3.org/2000/svg" width="32px" height="32px" viewBox="0 0 32 32"><circle fill="red" cx="16" cy="16" r="12"/></svg>
# png (data uri)
data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAUAAAAFCAYAAACNbyblAAAAHElEQVQI12P4//8/w38GIAXDIBKE0DHxgljNBAAO9TXL0Y4OHwAAAABJRU5ErkJggg==
# png (ipfs uri)
ipfs://QmZo7gsostaUdrV1peFz4cX6Z2TgriFJJP4VahG5knVm29
# webp (http uri)
https://ethereum.org/static/f541df14fca86543040c113725b5bd1a/99bcf/metamask.webp

Additionally the image must be squared with 96x96px minimum resolution. Image format is recommended to be lossless like PNG and WebP or alternatively vectorial like SVG. We strongly discourage lossy formats like JPG/JPEG.

Provider Detail

The EIP6963ProviderDetail is used as a composition interface to announce a wallet provider and and related metadata about the wallet provider. The EIP6963ProviderDetail MUST contain an info property of type EIP6963ProviderInfo and a provider property of type EIP1193Provider defined by EIP-1193.

interface EIP6963ProviderDetail {
  info: EIP6963ProviderInfo;
  provider: EIP1193Provider;
}

Window Events

In order to prevent provider collisions, the DApp and the Wallet are expected to emit an event and instantiate an eventListener to discover the various Wallets. This forms an Event concurrency loop.

Since the DApp code and Wallet code aren’t guaranteed to run in a particular order, the events are designed to handle such race conditions.

To emit events both DApps and Wallets MUST use the window.dispatchEvent function to emit events and MUST use the window.addEventListener function to observe events. There are two Event interfaces used for the DApp and Wallet to discover each other.

Announce and Request Events

The EIP6963AnnounceProviderEvent interface MUST be a CustomEvent object with a type property containing a string value of eip6963:announceProvider and a detail property with an object as it’s value of type EIP6963ProviderDetail. The EIP6963ProviderDetail object MUST be frozen by calling Object.freeze() on the value of the detail property.

// Announce Event dispatched by a Wallet
interface EIP6963AnnounceProviderEvent extends CustomEvent {
  type: "eip6963:announceProvider";
  detail: EIP6963ProviderDetail;
}

The EIP6963RequestProviderEvent interface MUST be an Event object with a type property containing a string value of eip6963:requestProvider.

// Request Event dispatched by a DApp
interface EIP6963RequestProviderEvent extends Event {
  type: "eip6963:requestProvider";
}

The Wallet MUST announce to the DApp the EIP6963AnnounceProviderEvent via a window.dispatchEvent() function call. The Wallet MUST add an EventListener to catch an EIP6963RequestProviderEvent dispatched from the DApp. This EventListener MUST use a handler that will re-dispatch an EIP6963AnnounceProviderEvent. This re-announcement by the Wallet is useful for when a Wallet’s initial Event announcement may have been delayed or fired before the DApp had initialized its EventListener. This allows the various Wallet Providers to react to the DApp without the need to pollute the window.ethereum namespace which can produce non-deterministic wallet behavior such as different wallets connecting each time.

The Wallet listens dispatches the "eip6963:announceProvider" event and listens to the "eip6963:requestProvider" event:

let info: EIP6963ProviderInfo;
let provider: EIP1193Provider;

const announceEvent: EIP6963AnnounceProviderEvent = new CustomEvent(
  "eip6963:announceProvider",
  { detail: Object.freeze({ info, provider }) }
);

// The Wallet dispatches an announce event which is heard by
// the DApp code that had run earlier
window.dispatchEvent(announceEvent);

// The Wallet listens to the request events which may be
// dispatched later and re-dispatches the `EIP6963AnnounceProviderEvent`
window.addEventListener("eip6963:requestProvider", () => {
  window.dispatchEvent(announceEvent);
});

The DApp MUST listen for the EIP6963AnnounceProviderEvent dispatched by the Wallet via a window.addEventListener() method. The DApp MUST dispatch the EIP6963RequestProviderEvent via a window.dispatchEvent() function call.

// The DApp listens to announced providers
window.addEventListener(
  "eip6963:announceProvider",
  (event: EIP6963AnnounceProviderEvent) => {}
);

// The DApp dispatches a request event which will be heard by 
// Wallets' code that had run earlier
window.dispatchEvent(new Event("eip6963:requestProvider"));

The DApp MAY also keep track of the various EIP6963ProviderDetail announced by the various wallets so that if the user wishes to utilize a different Wallet the DApp can immediately send transactions to the new Wallet. Otherwise, the DApp MAY re-initiate the wallet discovery flow via dispatching a new EIP6963RequestProviderEvent Event.

The described orchestration of events guarantees that the DApp is able to discover the Wallet, regardless of which code executes first, the Wallet code or the DApp code.

Rationale

Previous proposal introduced mechanisms that relied on a single window object that could be overridden by multiple parties. Therefore using an event-based approach we avoid the race conditions and potential attacks by making the communication available both ways in a way that doesn’t have potential namespace collisions.

To follow the Javascript event name conventions, the names are written in present tense and are prefixed with the standard number (eip6963).

Interfaces

Standardizing a provider info interface (EIP6963ProviderInfo) allows determining the necessary information to populate a wallet selection popup. This is particularly useful for DApps and Ethereum libraries they might depend on such as Web3Modal, RainbowKit, Web3-Onboard, ConnectKit, etc.

Regarding the announced provider interface (EIP6963ProviderDetail) it was important to leave the EIP-1193 provider interface untouched for backwards compatibility therefore it’s exposed in parallel. However, just as it is today there is no guarantee that this backwards compatible method will result in the user selected wallet being chosen.

Backwards Compatibility

This EIP doesn’t require supplanting window.ethereum, so it doesn’t directly break existing applications that cannot update to this method of Wallet discovery. However, it is RECOMMENDED DApps implement this EIP and SHOULD disable window.ethereum usage to ensure discovery of multiple Wallet Providers. Similarly, Wallets SHOULD keep compatibility of window.ethereum to ensure backwards compatibility for DApps that have not implemented this EIP.

Reference Implementation

Wallet Provider

Here is a reference implementation for an injected script by a wallet provider to support this new interface in parallel with the existing pattern.

function onPageLoad() {
  let provider: EIP1193Provider;

  window.ethereum = provider;

  function announceProvider() {
    const info: EIP6963ProviderInfo = {
      uuid: "350670db-19fa-4704-a166-e52e178b59d2",
      name: "Example Wallet",
      icon: "https://wallet.example.org/icon.png",
    };
    window.dispatchEvent(
      new CustomEvent("eip6963:announceProvider", {
        detail: { info, provider },
      })
    );
  }

  window.addEventListener(
    "eip6963:requestProvider",
    (event: EIP6963RequestProviderEvent) => {
      announceProvider();
    }
  );

  announceProvider();
}

DApp implementation

Here is a reference implementation for a DApp to display and track multiple wallet providers that are injected by browser extensions.

function onPageLoad(): EIP6963ProviderDetail[] => {

  window.addEventListener(
    "eip6963:announceProvider",
    (event: EIP6963AnnounceProviderEvent) => {
      providers.push(Object.freeze(event.detail));
    }
  );

  window.dispatchEvent(new Event("eip6963:requestProvider"));
}

Security Considerations

The security considerations of EIP-1193 apply to this EIP.

The use of SVG images introduces a cross-site scripting risk as they can include JavaScript code. Applications and libraries must render SVG images using the <img> tag to ensure no JS executions can happen.

// TODO for @kdenhartog: Add a privacy section about Wallet fingerprinting and how Wallets should not announce themselves until the DApp has dispatched the request provider Event and gathered consent from the user that they wish to announce themselves to the DApp.

Copyright and related rights waived via CC0.

  1. {
        "author": [
            {
                "given": "Paul J.",
                "family": "Leach"
            },
            {
                "given": "Rich",
                "family": "Salz"
            },
            {
                "given": "Michael H.",
                "family": "Mealling"
            }
        ],
        "collection-title": "Request for Comments",
        "DOI": "10.17487/RFC4122",
        "type": "book",
        "id": "rfc4122",
        "citation-label": "rfc4122",
        "issued": {
            "date-parts": [
                [2005, 7]
            ]
        },
        "number-of-pages": "32",
        "publisher": "RFC Editor",
        "title": "A Universally Unique IDentifier (UUID) URN Namespace",
        "URL": "https://www.rfc-editor.org/info/rfc4122"
    }
    

     2

  2. {
        "author": [
            {
                "given": "Tim",
                "family": "Berners-Lee"
            },
            {
                "given": "Roy T.",
                "family": "Fielding"
            },
            {
                "given": "Larry M",
                "family": "Masinter"
            }
        ],
        "collection-title": "Request for Comments",
        "DOI": "10.17487/RFC3986",
        "type": "book",
        "id": "rfc3986",
        "citation-label": "rfc3986",
        "issued": {
            "date-parts": [
                [2005, 1]
            ]
        },
        "number-of-pages": "61",
        "publisher": "RFC Editor",
        "title": "Uniform Resource Identifier (URI): Generic Syntax",
        "URL": "https://www.rfc-editor.org/info/rfc3986"
    }
    

Citation

Please cite this document as:

Pedro Gomes (@pedrouid), Kosala Hemachandra (@kvhnuke), Richard Moore (@ricmoo), Gregory Markou (@GregTheGreek), Kyle Den Hartog (@kdenhartog), Glitch (@glitch-txs), Jake Moxey (@jxom), Pierre Bertet (@bpierre), Darryl Yeo (@darrylyeo), "EIP-6963: Multi Injected Provider Discovery [DRAFT]," Ethereum Improvement Proposals, no. 6963, May 2023. [Online serial]. Available: https://eips.ethereum.org/EIPS/eip-6963.