EIP-5749: The 'window.evmproviders' object

Putting this first (out of order from your message) since I think the rest of the discussion is largely not relevant as long as we disagree on this point.

While you are correct that a tiny change is easier to get adopted than a large one, I think this is lost in the noise to the overall cost of an ecosystem wide breaking change. If we assign arbitrary units, getting the entire ecosystem to adopt any breaking change (e.g., epsilon sized) is say 1000 units of effort and a one line change is +1 units of effort and a big change like switching to window.postMessage is +10 units of work. Yes, it is 10x harder, but it is still insignificant compared to the cost of doing any change.

Your mentioning of decades old protocols I feel helps my argument here because it exemplifies just how amazingly hard it is to do any change. IPv6 is arguably one of the smallest possible breaking changes to a core internet protocol, just changing the size of a number essentially, yet it has taken ~25 years to get adopted and it still hasn’t supplanted IPv4. I really think we need to get this right rather than doing the smallest thing possible because it may take us years to get broad adoption of anything, and that thing should be provide the maximum utility as we may not get another chance to do a breaking change (the more Ethereum grows, the harder it will be).


Due to firewalls, this generally doesn’t work because users don’t run servers that can accept incoming HTTP connections. I have heard that there is a way to get two devices on the same internal network communicating (thus potentially avoiding NAT issues), but I’m not sure how realistic this is.

I would like more details on the downsides to iframe wallets you see. I have implemented one, and Gnosis has implemented one, and I haven’t run into any of the problems you mentioned here. The only problem we have both run into is the fact that dapps require an injected provider and so don’t work by default (you need an extension to do the injecting, or you need the dapp to support another mechanism of connecting to the host wallet).

What are the fishing vectors you see here?
What are the security vulnerabilities?
What are the performance problems?

For the UI issues I think perhaps you are imagining a visual iframe with the host all around it and it has its own scrollbars and whatnot? I’m envisioning something more like the host page just has a little toolbar on the top or bottom, or perhaps a floating tab on one of the sides but the app’s iframe is basically the entire window. In the one I wrote, it was a floating header bar that could be collapsed down to just a tab that you could click to bring it back, but the dapp got essentially 100% of the viewport to itself. Gnosis does have left and top bars, and I haven’t noticed any problems with it but perhaps I just haven’t used enough dapps to run into trouble?

This one is quite unfortunate, and frustrates me quite a bit. I don’t think dapps should be doing this (I am not convinced it offers meaningful protection for a dapp), but you are correct that some do it none the less.

I’m not sure which comment you are referring to specifically, but I believe I have already replied and mentioned that these things were already solved in my prototype SDK? I would be happy to discuss more, but I feel like it is out of scope of this specific discussion beyond just saying that it is a very solvable problem and one I believe is in fact already solved in my prototype (so not just theoretically solvable).

They literally cannot do this. This is exactly the problem I’m trying to describe. An iframe host cannot inject into or mutate the hosted iframe. This is a core browser security/sandbox thing that is very unlikely to go away. They cannot provide any variables to the dapp they have in an iframe. The only way they can communicate with the iframe dapp is via window.postMessage.

Meaning iframe host wallets like Gnosis, which can be hosted on IPFS (unlike a browser extension, which cannot be).

Users should be running their own node or using an embedded light client, but that is a separate problem that I am advocating for elsewhere.

The iframe host wallet behaves the same as an extension wallet. It would allow the user to set a JSON-RPC provider, or it would have an embedded light client, or it would provide a centralized RPC for the user.

I can get behind this if the extra work you are proposing can add additional advantages. We shouldn’t do extra work if the extra work can be boiled down to few lines. What you are proposing is only applicable to a very specific use case where 99.9% of the the current Ethereum users are not technical enough to handle [running nodes (either full/light), I understand the fact this proper decentralization however not good user experience].

I definitely do not agree with this, IPv6 is one of the biggest major changes internet protocol is going through. It might look simple but definitely not simple, they even had to abandon IPv5.

Firewalls blocking is not something we can fix with any standards. Firewalls can prevent users from visiting any site or running any node including Ethereum and even ipfs.

Iframe issue?
A simple google search on “Iframe vulnerabilities” will give you everything you need from clickjacking to phishing.
X-Frame-Options is a recomended header for you to follow for best security practices in web, thus preventing your site being embeded inside other sites. You should also check “why X-Frame-Options”.
At the same time, based on my understanding you do believe having scrollbars in a frame is ok. However, if you talk with any UI/UX person they will highly disagree with you.
So yea iframes have ton of issues and since this is EIP is not about that I wouldn’t get into those.

Like I mentioned before, I dont believe advocating for iframe wallets is the best way to propose your idea. I do believe the technical side of things that you achieved with your way, however it is hard to justify the extra work that goes into it. If you’d like iframes wallets to work then the easiest way would be asking web3onboarding libraries to change one line of code to something like this
const provider = parent.ethereum || window.ethereum or based on this EIP const providers = parent.evmproviders || window.evmproviders. This will let the iframe talk to the host frame.

Again, if you make your idea into EIP I would be more than happy to contribute

Hey @kvhnuke, I’m from the Coinbase Wallet team. What’s the status on this EIP? We’re very supportive of this idea and would be happy to support.

hey @jmcho
thank you for the support!
we are getting it merged to Last Call! https://github.com/ethereum/EIPs/pull/6018

First, off I want to thank the author for taking the time to try and address this concern. I believe this is a legitimate problem so it’s good that we’re taking the time to find a solution. However, I don’t think the way in which this is being done right now by shoving an EIP through the process is a good solution that will lead to actually solving the problem personally. In fact, I think it stands to cause additional harm and it’s only through coordination between a large number of wallet providers (beyond just the Ethereum ecosystem too) that will lead to solving this problem.

I’ll note 7 concerns I have with this current EIP:

  • This proposal as it stands pollutes the global namespace further
  • It doesn’t assign a specified date that window.ethereum should be expected to be deprecated
  • This is highly EVM specific when numerous wallets are moving to a multichain approach.
  • It only recommends a method for assigning keys in order to distinguish different chains.
    • What’s to stop another wallet from intentionally prototype polluting other keys in order to stop other wallets from being used?
    • How will a web3modal style library know that the key is associated with the appropriate wallet?
  • It doesn’t address fingerprinting concerns that would arise from websites being able to uniquely identify users based on the unique assortment of wallets they support.
  • It doesn’t comment in the security considerations section about how prototype pollution attacks are expected to be prevented here if a malicious wallet wanted to modify a transaction before it got sent to the wallet in order to
  • It doesn’t address compatibility concerns with other related EIPs such as EIP-5593 or CASA’s approach with CAIP-25

For these reasons Brave does not intend to support this at this point. From my understanding some other large wallets are also hesitant to support this too and will chime in here shortly. As it stands this proposal has not gathered appropriate consensus, has been rushed through the entire EIP status in under 2 months, and will create a greater fracture in wallet compatibility for the web3 ecosystem that will limit their ability to interoperably work with multiple wallets. By this I mean dApps will now have to question whether they should support window.evmprovider along with window.ethereum, window.solana, window.web3 if they’re a multichain dApp like Opensea.

At this point, I’d hardly consider this a “standard” as there’s not been appropriate discussion to confirm interopability, make sure time has been given for wallets to review this and provide feedback, and address legitimate concerns with this approach. At best, it’s a documentation of a direction “a few major wallets and major web3 onboarding libraries” have decided to go without appropriately gathering consensus. Looking at this thread so far it’s basically just been a discussion between the Author (@kvhnuke) and @MicahZoltu with one other wallet (coinbase - @jmcho) expressing support for it.

If this is the minimum bar that EIP editors feel is necessary in order to upgrade this EIP to the “final call” status that impacts interoperablity for the entire Ethereum and web3 ecosystem and delegitimizes the entire organisational process of EIPs and shows that EIPs are not intended to be for “gathering consensus and shipping code”. Is that really what we’re looking to achieve here?

Let’s slow down, properly alert other wallets to this EIP, get them to provide feedback, address concerns to gain consensus on an approach together and find a solution to this problem.

1 Like

Speaking for MetaMask, I second the reservations put forth by @kdenhartog / Brave, and we also have no intention of implementing this standard as written. While the problems noted by the EIP authors are real, we believe that injected providers are fundamentally flawed, and we should seek to abandon that practice and window.ethereum entirely. We encourage all wallets to invest our collective resources in provider standards that support non-EVM chains and do not rely on injection, such as CAIP-25 and its related standards (WIP).

In addition to @kdenhartog’s points, we oppose this EIP in its current form because:

  • Extensions must possess extremely broad powers over every webpage in the user’s browser in order to inject a script tag. The Google Chrome team recently fixed a bug such that it is now possible for an extension to receive messages from any webpage using chrome.runtime.connect and/or .sendMessage. This makes it relatively straightforward to abandon injection entirely, which is what MetaMask intends to do.
    • See the externally_connectable permission in the Chrome extensions documentation for more details. Valid matches patterns now includes http://*/* and https://*/*.
  • The EIP does not specify who will maintain the namespace for wallets, or in what manner they will do so.

Solving the problem of injection race conditions is fundamentally a social coordination problem for wallets. While this standard could help facilitate that coordination, we don’t believe that laying claim to another global name is the solution. We should instead invest our energies in provider standards that avoid injection entirely.

All of the above said, in the process of writing my objection, it does strike me that this EIP could be more palatable with some modifications. Rather than adding a property to window, we could define a property of window.ethereum that serves the same purpose as window.evmproviders, perhaps simply window.ethereum.providers. This would be pretty ugly, but so is window.ethereum itself, and this ought to be completely non-disruptive for both dapps and wallets. I’m curious what to hear what the authors, @kdenhartog, and others have to say about this idea.

1 Like

@kdenhartog and @rekmarks thank you for joining the discussion, I was trying to get as many wallets as possible to enter the discussion since we need a solution now and not in few years, specially since we had this issue for the past 6 years or so without anyone trying to solve it. We personally reached out to MM team multiple times however, up until now no one wanted to enter the discussion. Probably because they are the least affected wallet due to current issue. That said, I am glad you both joined.

Can you clarify? what is the disadvantage of adding a new variable to global namespace?

I don’t believe adding a specific date to deprecated window.ethereum will help the EIP at all, specially since it is a proposal and not a mandatory standard that everyone should follow. Also EIPs should pass the test of time, adding a date to an EIP is simply not necessary.

I don’t believe Ethereum Proposals should try to improve all other chains, and this is not going to be feasible. For example, Enkrypt is built to be multi-chain because of that we have to go over ton of other non-evm chains. There is no way we can limit them to following an Ethereum proposal or even a CAIP. Even if we try to do that, there is no reason for other chains to follow that.

UUID is assigned in this EIP is enforce the use of a single unique identifier for web3 providers. Since we are dealing with JS there is no way we can prevent some other extension from overriding an existing variable. That is what currently happening with window.ethereum every installed wallet is trying to fight for window.ethereum. I cant think of a way to make sure this doesn’t happen, again this is a JS issue that I don’t believe we will be able to address in an EIP

as I mentioned in a previous comment this still comes down to the wallet, wallet can decide when to inject. This is a wallet requirement rather than an EIP. I do believe wallets should inject only if the user wants to inject. For example by clicking the wallet icon on the browser, however persistent injection over the past 6 years made many libraries to automatically detect and wait for window.ethereum to be present at the DOM load, if it is not there those libraries will simply fail. That said, yea this still needs to be handled at wallet level. This EIP only address the namespace to follow during injection.

Again, this is not something we can address specially since it is JS. Any wallet can override the variable. However, I believe with what @rekmarks mentioned in his comment using chrome.runtime.connect might be a potential future solution. However, please see my below questions regarding that.

EIP-5593 does not interfere with this EIP, if anything we can extend EIP-5593 to support evmproviders rather than ethereum
CAIP-25 is a good first step trying to unify all blockchains, however success of this is yet to be determined. CAIP is clearly written by evm devs and didnt take into consideration lot of other types of chains mainly substrate ones. Like I mentioned before, trying to unify every single type of blockchain is not feasible, there is no way we can make every chain follow a certain set of guidelines.

There is no need for them to pick anything, they can support all of them at the same time, this is how it currently works. Maybe thats why I dont see the problem with this, what kind of problem do you see?

Actually, we reached out to all major wallets. I dont believe we reached out to brave as we didnt think you were affected. After waiting 2 months I decided to move the EIP to review process. There was no reason for me to wait any longer if no one has any other comments. Users were affected by this issue for over 6 years now and no other EIP even tried to address this issue.

Actually Im not sure whether you understand the EIP process, it is not the job of EIP reviewers to go through every single EIP to make sure it is valid, verified and all good. It is up the EIP author (in this case me) to make sure all of that. Also, once an EIP become final if does not mean everyone should follow that. EIPs are proposals to improve Ethereum. EIP-1: EIP Purpose and Guidelines

1 Like

Please see my above comment regarding this.

Thank you for bringing this to my attention, I was not aware of this. This will definitely improve the communication method from window to background. However, I do have couple of questions.

  1. Do you have a test implementation somewhere?
  2. This doesnt seem to be supported by firefox?
  3. What is process you are thinking of to let the dapp know which extensionIDs are available? specially during development extensionID changes alot and both chrome.runtime.connect and chrome.runtime.sendMessage requires an extension ID. Without that no dapps will be able to connect and we should have a way of letting dapps know which IDs are available.

No one needs to maintain the namespace, if someone needs to maintain a namespace for any EIP we cant make sure that maintainer will be present in 20 years. simply wallets needs to use their wallet name as the key in the evmproviders object

I am all ears for a solution, unfortunate truth is no one is proposing any :frowning:

Main reason why I dont like this approach is if for example there is wallet that doesnt follow the EIP they can replace the whole window.ethereum object and therefor remove all wallets under window.ethereum.providers

This idea with the scheme handlers is not bad. But it doesnt solve the problem to know which ones are available, and which extension to use for them (should there be multiple existing)

If you have a good idea, I would like to see a standard proposal that solves all these issues.

I think we have ruled out scheme handlers at this point for some reason, maybe because they only work the way we want in a subset of browsers or something?

The link I shared above has a handshake via window.postMessage that is initiated by the app and any extension can choose to respond to the handshake broadcast. This allows many extensions to all be setup and the individual extensions can decide on how to alert the user that “an app wants to connect, should I respond?”. This provides the user with privacy by default by not telling the app about any attached extensions until after the app has requested to connect and the extension has prompted the user.

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.