EIP-5749: The 'window.evmproviders' object

The big problem is, that then three extensions will respond if you have three installed , and it will get very annoying to cancel to away.

From a UX perspective, it’s better if the user gets to select which extension he wants to use in a single window, or the app chooses extension

1 Like

This fingerprints users and violates their privacy. By having the dapp announce itself to the plugins first, we have a workflow that encourages user privacy protection by default rather than defaulting to a flow that violates user privacy.

That being said, if users are OK with sacrificing their privacy, they can simply tell their signer to auto-respond to dapp announcements so they don’t get a prompt/pop-up/whatever.


It is also worth noting that the extensions are loaded off local disk while dapps are loaded from remote sources. Also, not all dapps need access to a signer at the same point in their workflow. For example, one can browse peep.eth without signing in and the sign-in is triggered by a user interaction in the dapp and a dapp may not actually be prepared to receive extension announcements until sometime very late in the page’s lifecycle.

By having the dapp initiate the handshake (e.g., when a user clicks connect button), we can be relatively confident that the extensions are ready to handle the handshake, but if the extension initiates the handshake there is a good chance the dapp isn’t ready.

@MicahZoltu Don’t forget in Ethereum eco system wallets are not just signers. Wallets also provide access to the nodes. Sure a dapp can have it’s own connection to a node but that is not how it currently works.

At the same time going back to what I mentioned previously and what @frozeman said. It is going to be a disaster from a UX point of view when the user initiate a sign request, where 5 different wallets will popup with the same signing request.

Regarding fingerprinting, yea it definitely comes down to wallet settings where the user can decide at which point they want to let the app know.

That isn’t the UX. The UX is that the dapp broadcasts that it wants to know if any wallets are connected, and then each of the wallets can respond (or not!) The dapp can then present the list to the user, similar to today. Once the dapp has established a connection (or multiple!) it can then use that going forward with the same UX as today.

For a dapp, the UX doesn’t really change other than instead of listing only one of MM + Brave + Gnosis + other in the selection UI, it can list many.

I would really like to see this changed, but I’ve accepted it is out of scope for this discussion. I have my limits… :laughing:

I haven’t read the entire conversation but i try to summarise the gist of the problem, so we can find a good solution faster.

Some precursor:

  • When I proposed the initial ethereum provider (https://github.com/ethereum/interfaces/issues/16), there were not many wallets, mainly MetaMask, thats why the idea of multiple providers was not really considered. The idea of multiple chains was. But there was always the option to extend the options object with more properties like chainId
  • in the later EIP 1193 it was purposefully that the object exposed on window was not specified in the standard, to make multiple objects possible.
  • ideally the dapp shouldn’t be required to know which extension is used (therefore i was never a fan of the .isMetaMask); but since we have a lot of EVM chains and extensions, this need is now here. Going forward even more so, as extension will not be simple “wallets” with keys, but full on internet account tools
  • I chose initially the window.ethereum as window.web3 was already taken by the web3.js lib, and we needed to move away from injecting web3 the lib directly.
    I would prefer now the word window.web3Providers for example.
  • we are building a new browser extension for universal profiles (ERC725 smart contract based accounts), so we are exactly hitting this issue now (mainly relevant for the LUKSO blockchain initially, but later also for others).

The options:

  • using simply a certain type window.postMessage() message (with or without handshake request):
    • This would be clean
    • Issue is that you won’t know to which extension you are talking to
    • and multiple could react. leading to many window popping up (if the user has many extensions), which is bad UX
    • even if the dapp loads a provider lib, that knows how to talk through postMessage to a specific extension, the dapp doesnt know which ones exist, and would need to try/catch send messages, until the correct extension responds. If this message would contain network IDs etc like CAIP25 proposes, there could still be multiple extension that respond for the same chain.
      • requiring the lib to show a popup asking which extension that responded to use.
  • exposing an object like window.web3Providers (or as you propose @kvhnuke window.evmProviders)
    • advantage:
      • the dapp/user can choose as he can be asked which extension to use
      • dapps can choose extension, if they know a certain works well for a certain network
      • extensions don’t overwrite each other
    • disadvantage:
      • the object could be overwritten by a malicious extension (though the user will notice and deinstall this extension fast)
      • pollutes the window object
      • it would need a default provider, or also be present on the object as array keys, so window.web3Providers[0] can be chosen if the dapp doesnt want to chose/let the user chose
  • Concerning privacy:
    • this can be solved on the user side by choosing “This extension can only read the site data, if clicked”.
      • This could also work as a solution to choose which extenison to use, but its cumbersome; not the default setting in chrome; and when multiple extension are enabled for a website, you end up with the same issue; or need to close the tab to get to the initial state (where none is enabled)
    • identifying the user based on a set of extension installed
      • the dapp already has other means to identify users: ip, browser type/version, cookie; the set of extension installed will not increase identifiability significantly (as most users will have 1-2 common extensions max)
      • eth_requestAccounts solved already the address exposure issue, but even exposing an address, is no proof of ownership → for that we can use SIWE (In the future i would like to propose a new web3_auth two way signing protocol)

From a UX perspective:

  • the user wants to chose the extension thats being used (or default to the only one he has)
  • ideally one click, and a choice thats remembered/can be changed later
  • and dapp should be able to choose
  • fallback mechanism for a default one, ideally also chosen by the user at some point (in the browser settings?)

Given all of the above, i can see two ways:

  • define a window.postMessage() protocol like CAIP25, but one that responds not only with addresses and methods available. But extension name and/or ID, icon AND session ID, to be used when posting messages to the respective extension so that they are identifiable so the user can choose in a dapp/provider lib provided popup.
    • advantage: extensions cant mess with other extensions
    • disadvantage is that one post message call is required, with many responses (from all extensions that want to respond);
      • extensions can send fake answers in the name of other extensions
  • add window.web3Providers and let extension populate it themselves with the CAIP25 infos + name and id
    • advantage: “instantly” available (with MV3 a bit delayed)
    • disadvantage:
      • messes with the websites scripts loading, as it injects scripts
      • extensions can override providers of other extensions

After thinking about it I prefer a
window.postMessage() protocol with:

  1. initial handshake request answered by all extensions that support the requested chains (CAIP25 + extension name, id?, icon, session ID)
  2. every request following a standardised request body + session id (or just extension id??) → only the extension that have the right session id should answer.
    maybe something like this

Request

{
  type: "web3Request", // differentiate from other protocols that use window.postMessage
  version: "1.0.0",
  sessionId/extensionId/channelId: null, // empty in handshake request ; targets ALL extensions that want to answer
  requestBody: {
       "id": 1,
       "jsonrpc": "2.0",
       "method": "provider_authorization",
       "params": {
         "requiredNamespaces": { // I would rename these properties 
            "eip155": {
              "chains": ["eip155:1", "eip155:137"],
              "methods": ["eth_sendTransaction", "eth_signTransaction", "eth_sign", "get_balance", "personal_sign"],
            "events": ["accountsChanged", "chainChanged"]
          },
          "eip155:10": {
            "methods": ["get_balance"],
            "events": ["accountsChanged", "chainChanged"]
          },
          "cosmos": {
            ...
          }
        },
        "optionalNamespaces":{
          "eip155:42161": {
            "methods": ["eth_sendTransaction", "eth_signTransaction", "get_balance", "personal_sign"],
            "events": ["accountsChanged", "chainChanged"]
        },
        "sessionProperties": {
          "expiry": "2022-12-24T17:07:31+00:00", // better unix timestamp?
          "caip154-mandatory": "true"
        }         
      }
  }
}

Result

{
  type: "web3Response",
  version: "1.0.0",
  sessionId/extensionId: '1234565432', // each extension answer with their extension id/session id
  requestBody: {
       "id": 1,
       "jsonrpc": "2.0",
       "result": {
          // whatever goes here, like for a handshake, the extension name, id, icon, supported schemes/networks/chains
          ...
       }
  }
}

Benefits:

  • removes any injected object
  • lets dapps load any provider lib: basic one, one with modal to select extension, or whatever UX they like
  • clear direction to talk to any extension, and even have chainId/type selection possible

Question:

  • why sessions? ist a simple static channelId/extensionId enough?

(As for CAIP 25 i would rename some of the properties, to make it simpler and cleaner)

@kvhnuke @rekmarks @kdenhartog @MicahZoltu
What you guys think?

2 Likes

IIUC, what you have described here is almost exactly what I implemented in GitHub - Zoltu/ethereum-browser-sdk: An SDK for communicating between Ethereum dapps and Ethereum enabled browsers via events., so I agree with you on supporting that. It looks like you have a slightly different layout from me, but I’m not tightly married to what I built.

For the curious, you can see the protocol I came up with at https://github.com/Zoltu/ethereum-browser-sdk/blob/master/ethereum-browser-sdk/library/source/shared.ts (HotOstrich is a placeholder name for the first version of the protocol). I think the interesting bits that should be retained are:

  1. Protocol should be versioned, and version negotiation should happen during the handshake.
  2. There should be a mechanism for negotiating capabilities. We shouldn’t assume that every provider has the exact same set of capabilities.
  3. During the handshake the provider gives a name/icon so the dapp can present something useful to the user.

For features I think are nice but not critical: I think we should move away from using the Ethereum client JSON-RPC when communicating between dapp and wallets as it doesn’t serve our needs well (for example, the method signature is lost when submitting a transaction), but as long as we have version negotiation during handshaking we could address this later.

I dropped the idea because Chrome doesn’t show a dialog to choose the handler when opened, and the setting is buried deep in the menus. I’ve heard that may be changing though, so it might be worth revisiting the idea.

My main concern is window.postMessage has its own set of problems, right now it works as a broadcast. Which means, any and every extension can potentially listen to every message between dapp and wallet. I do understand this is how it works currently but I dont think this will be the case in the future specially with things like chrome.runtime.connect and chrome.runtime.sendMessage or even WebRTC. Bottom line is I dont think we should define the protocol how the dapp should communicate with the wallet. This is why Im a big proponent of injection since this will let wallet to decide how to communicate. That said, Im ok with using window.postMessage as the broadcast to let wallets inject that way wallets know when to inject or even have a popup to confirm injection.

This is no longer the case either MV3 recently added IMMEDIATE where itll inject the code immediately similar to MV2

The handshake announcement from the extension could include an encryption public key, that way all communication to the selected wallet is encrypted.

Can you go into more details what your thinking here is? Can iframe hosts listen on browser.runtime ports (not only extensions)? What would a WebRTC solution look like?

Now we have to add a whole another encryption layer to communication as well, at the same time this encryption public key needs to be random, otherwise anyone would know the decryption key. Also the dapps needs to generate an encryption key as well to receive the communication. Now we have to define what is the proper encryption type …etc. I strongly believe we shouldn’t decide how the communication should be handled from dapp to extension, specially we dont know which new more secure ways would be introduced in the future.

Like I mentioned before iframe wallets are not a priority at least for me based on the issues which I outlined earlier.
WebRTC solution is where the extension acts as a webrtc server and dapps can connect to it. This is just an example on different ways to communicate which are way more secure than window.postMessage

For me this itself a huge vulnerability with window.postMessage just imagine, you are on MEW and we decided to embed an iframe thinking it is safe however it turned out to be a bad actor, now the user will get a popup to sign a tx and since they are on MEW they thinks it is from MEW however, the tx is malicious

I think as an EIP our goal should be to standardize the methods which dapps needs to access to communicate rather than the actual communication protocol.

I’m strongly in agreement with not building ourselves into a corner which is why I advocate for a versioned protocol. That being said, we do need to standardize on something for the communication protocol as we want all dapps to be able to talk to all wallets. We could have multiple competing versions of this protocol and iterate on it over time but we need something for v1. My preference is to not include encryption in v1 and then work on a v2 that adds encryption. This is because I think the use case that would be protected here is fairly narrow (users with a malicious extension installed that is snooping on their wallet traffic) and the risks are somewhat low (snooping extension farms public user transaction that many wallets/pages/extensions are already farming anyway).

An iframe inside of MEW won’t have access to the window message bus of MEW. The iframe host has window message bus access for any iframes it hosts, and extensions have message bus access to all pages and iframes.

1 Like

I agree on the version, and I think something like encryption could be added later.

The good thing about my proposal is that you can do one initial call, receive a notification from each extension, And then the dapps extension lib (self loaded, it injected) could show a nice selection, modal, or simply connect to one if only one responded.
This gives a lot more flexibility on the dapp side, a lot more improvements overtime on the UI side, and the ability to access any extension directly without d the user having to click away every extensions modal. At the same time it prevents extensions from overriding each other (except they pretend being another one, which becomes very fast obvious, as to extensions, would respond with almost the same
Message. And the user wouldn’t notice it right away, when he clicks on an extension, I can, and another extension pops up. Like @MicahZoltu said malicious extensions are an edge case anyway)

The issue with not standardizing the way we communicate, is that you create a very difficult way for the apps to talk to extensions. Dapps should not be concerned to talk to a specific extension, they should ideally not care which extensions used as long as you user chose it.

The idea of the encryption key is great. And can be added a little later. Also new ways of communication can be added later including switching to webrtc, after the initial handshake notification.

In order to make a standard adopt, they cannot require too many massive changes. Otherwise no one will adopt it, except the change is specifically better.

the RPC might not be the best communication, but it’s good enough, and I don’t see any reason to change this now because there’s nothing significantly better right now.

So I would propose one step at a time, to switch to window post message. It’s already a huge one different changes can be discussed later in different discussions.

1 Like

@frozeman @kvhnuke I encourage you to join the Ethereum Wallets discord server as we have been having a lot of discussions on this topic over there. Ethereum Wallets I think it would really help to have you two join in those discussions. There is also a monthly AllWalletDevs voice call where we discuss this sort of thing regularly that would be great to get your input into!

I would, if my time allows that. But the link is expired.

Sorry, here is an update link that shouldn’t expire: Ethereum Wallets

Frame Wallet supports being connected to multiple chains now fwiw.

@frozeman @MicahZoltu did you guys make any progress in discord?
I am not a discord user so I wont be able to join, plus I believe discussions related to EIPs should happen at the discussion link. If you made any progress that could be converted to an EIP let me know if not I’d like to move forward with this EIP in the mean time. More complicated an EIP can get will only decrease the adoption process.

Also no matter which way we take, we still need same amount of permissions for chrome extensions

We have discussed all of this (plus other stuff) more, but I don’t think anyone in discord currently is strongly in favor of window.evmproviders so it is mostly just everyone discussing the viability of alternative options to that.

I do agree that any fruitful discussions that happen in Discord should eventually make their way into a broader and more publicly accessible discussion forum!

If you don’t want to join Discord but want to participate in the AllWalletDev community a bit more, you could perhaps attend the All Wallet Dev meetings. I think @SamWilsn is planning on moving them to Zoom (or similar) in the near future specifically so they are more accessible to people without Discord accounts.

I’m quite confident that this is not true. If no injection is happening (only winodw.postMessage), then the extension doesn’t need the ability to read/write to all pages (sudo).

{
  "author": "https://www.enkrypt.com",
  "homepage_url": "https://www.enkrypt.com",
  "version": "1.11.1",
  "name": "Enkrypt: Ethereum, Polkadot & Canto Wallet",
  "short_name": "Enkrypt",
  "permissions": [
    "storage",
    "unlimitedStorage",
    "notifications",
    "tabs",
    "clipboardRead",
    "clipboardWrite"
  ],
  "commands": {
    "_execute_action": {
      "suggested_key": {
        "windows": "Alt+Shift+E",
        "mac": "Alt+Shift+E",
        "chromeos": "Alt+Shift+E",
        "linux": "Alt+Shift+E"
      }
    }
  },
  "content_scripts": [
    {
      "matches": [
        "file://*/*",
        "http://*/*",
        "https://*/*"
      ],
      "js": [
        "scripts/contentscript.js"
      ],
      "run_at": "document_start",
      "all_frames": false
    },
    {
      "matches": [
        "*://connect.trezor.io/*/popup.html"
      ],
      "js": [
        "vendor/trezor-content-script.js"
      ],
      "run_at": "document_start"
    }
  ],
  "description": "Everything in the blockchain made easy",
  "icons": {
    "16": "assets/img/icons/icon16.png",
    "32": "assets/img/icons/icon32.png",
    "64": "assets/img/icons/icon64.png",
    "192": "assets/img/icons/icon192.png"
  },
  "manifest_version": 3,
  "action": {
    "default_icon": {
      "16": "assets/img/icons/icon16.png",
      "32": "assets/img/icons/icon32.png",
      "64": "assets/img/icons/icon64.png",
      "192": "assets/img/icons/icon192.png"
    },
    "default_title": "Enkrypt",
    "default_popup": "action.html"
  },
  "background": {
    "service_worker": "scripts/background.js"
  },
  "web_accessible_resources": [
    {
      "resources": [
        "scripts/inject.js",
        "scripts/*.js.map"
      ],
      "use_dynamic_url": false,
      "matches": [
        "http://*/*",
        "https://*/*"
      ]
    }
  ],
  "minimum_chrome_version": "95",
  "content_security_policy": {
    "extension_pages": "script-src 'self'; object-src 'self'"
  }
}

this is the current manifest for Enkrypt (with injection) which permission do you think we can remove?