MetaMask / metamask-mobile

Mobile web browser providing access to websites that use the Ethereum blockchain
https://metamask.io
Other
2.04k stars 1.06k forks source link

Ethereum provider injection randomly fails on Android #1975

Closed arnoudcommandeur closed 3 years ago

arnoudcommandeur commented 3 years ago

Describe the bug When I open my DApp, the web3 provider is not always injected. It doesn't make sence I use @metamask/detect-provider or plain window.ethereum call.

To Reproduce Steps to reproduce the behavior

  1. Start DApp
  2. Check if Web3 is injected via:
async function testEthereum() {

  const provider = await detectEthereumProvider()

  if (provider) {
    alert('YES');
  } else {
    alert('ERROR!');
      setTimeout(testEthereum, 3000); // 3 seconds
  }
}

I also tried adding an eventlistener, but this doesn't' work as well:

      window.addEventListener('ethereum#initialized', App.initWeb3, {
        once: true,
      });

In my HTML file I added:

<script src="https://unpkg.com/@metamask/detect-provider/dist/detect-provider.min.js"></script>

  1. Sometimes the web3 is not available. After refreshign the page, sometimes it works, sometimes it does not work.

Expected behavior I expect the provider is always available.

Smartphone (please complete the following information):


to be added after bug submission by internal support / PM Severity

rekmarks commented 3 years ago

Ref: #1931, #1956

KarlWerft commented 3 years ago

Hi rekmarks,

did you solve the metamask detection problem already? I have the same issue. Sometime the provider is detected, sometimes not, actually more often not.

This really completely stops any possible mobile usage of the dapp. I am totally frustrated.

arnoudcommandeur commented 3 years ago

Hi @KarlWerft , unfortunately I haven't found a solution yet. It is really frustrating!

KarlWerft commented 3 years ago

Maybe setting a higher timeout within the options of detectEthereumProvider? Do you know if detectEthereumProvider excately return null or something else when it is not working? The biggest probleem is that I cannot debug the mobile app. Maybe we could use a simple javascript alert to check, what is happening excatly? Unfortunately I cannot even call the dapp from my handy via WLAN intranet, because of ssl certificate issues. WLAN is no localhost :-(

rekmarks commented 3 years ago

Hello, I'm sorry you are experiencing this problem. We believe that this is a problem with the Android browser/WebView implementation.

@KarlWerft, are you also on Android?

KarlWerft commented 3 years ago

yes. Might a higher timeout help linke detectEthereumProvider({timeout: 6000}) ?

KarlWerft commented 3 years ago

like not linke

rekmarks commented 3 years ago

We believe that, in these cases, the injection is failing completely, and that no timeout will help. That said, it can't hurt to try! Can you try a 10000 timeout?

KarlWerft commented 3 years ago

i will try it as soon as I will be back at my development machine.

arnoudcommandeur commented 3 years ago

I have tried different approaches. My conclusion is that web3 sometimes is not injected at all, dispite all the ways you can detect web3. When it is not injected, I tried detection after a few seconds, but unfortunately without any result.

KarlWerft commented 3 years ago

The timeout had no positive effect. I read somewhere that a excately similar sounding problem occured when using the react module serviceworker. Did ever read about this?

arnoudcommandeur commented 3 years ago

Hi @KarlWerft I have no experience with react.

KarlWerft commented 3 years ago

React has nothing to do with the effect it seems. What version of Android did you experience the web3 problem with? Was Android 9? Would be an idea to see if different Versions behave differently or if different webkits do.

arnoudcommandeur commented 3 years ago

Hi @KarlWerft i'm using Android 10

KarlWerft commented 3 years ago

We believe that, in these cases, the injection is failing completely, and that no timeout will help. That said, it can't hurt to try! Can you try a 10000 timeout?

Hi rekmarks, I still don't have any solution for using metamask mobile on Android devices. Do you have any idea what I could do for a work around? it would be pretty sad if the only solution would be to restrict usage to apple devices, wouldn't it? Are you planning at MetaMask to deal with the problem in any way?

rekmarks commented 3 years ago

Hi @KarlWerft, we are troubleshooting this internally in an attempt to find a solution. The issue only appears to affect a minority of Android devices, and we're not sure why. I unfortunately cannot give an estimate for how long it will take at this time, but rest assured that it's one of our top priorities.

arnoudcommandeur commented 3 years ago

If there is anything I can do to solve this issue, please let me know!

shendel commented 3 years ago

Hi @KarlWerft, we are troubleshooting this internally in an attempt to find a solution. The issue only appears to affect a minority of Android devices, and we're not sure why. I unfortunately cannot give an estimate for how long it will take at this time, but rest assured that it's one of our top priorities.

if it helps. I noticed that the 'ethereum#initialized' event is triggered before reloading the page - if you select "reload" in the menu, then the debug alert is triggered a split second before reloading

shendel commented 3 years ago

@rekmarks , @omnat any ideas exists?? i se you are prepare v1.0.6, v1.0.7, but no more info about this bug.... One week left....

shendel commented 3 years ago

@rekmarks , @omnat - can you setup your cli for build dev apk version? https://github.com/MetaMask/metamask-mobile/issues/1226

shendel commented 3 years ago

@rekmarks , @omnat - on real device - same picture WTF!?? https://www.youtube.com/watch?v=TiJM5_nopUs

your self-written test does not work in your application

shendel commented 3 years ago

Real device - android 8.1, Emulator 9.x, and @arnoudcommandeur with android 10 - your build-in dBrowser dont work

omnat commented 3 years ago

Hi @shendel, we didn't work on this issue yet. Check what's in the latest releases here: https://github.com/MetaMask/metamask-mobile/releases

EvilJordan commented 3 years ago

FYI, this is also a problem on iOS. Even the most basic page, with zero functionality, just detecting if there is a provider, fails.

rekmarks commented 3 years ago

That's the first we've heard about this occurring on iOS. @EvilJordan, can you please provide further details in a new issue?

EvilJordan commented 3 years ago

Sure!

Try this in MM Mobile's Browser and you'll get an "undefined":

<!doctype html>
<html>
<head>
    <script>
        window.addEventListener('load', async () => {
            alert(window.ethereum);
        });
    </script>
</head>
<body>
    Hello!
</body>
</html>
rekmarks commented 3 years ago

Ah, we expect window.ethereum to be undefined in that case. If you run that event handler on a timeout of 1-2 seconds, it should be defined. This issue is about the provider never being injected.

Please see this page in our documentation for more details: https://docs.metamask.io/guide/mobile-best-practices.html#provider-availability

EvilJordan commented 3 years ago

Don't mean to clog up the issue, but this does nothing. Doesn't even fire alerts in MM mobile:

<!DOCTYPE html>
<html>

<head>
    <script>
        if (window.ethereum) {
            handleEthereum();
        } else {
            window.addEventListener('ethereum#initialized', handleEthereum, {
                once: true,
            });

            // If the event is not dispatched by the end of the timeout,
            // the user probably doesn't have MetaMask installed.
            setTimeout(handleEthereum, 3000); // 3 seconds
        }

        function handleEthereum() {
            const {
                ethereum
            } = window;
            if (ethereum && ethereum.isMetaMask) {
                alert('Ethereum successfully detected!');
                // Access the decentralized web!
            } else {
                alert('Please install MetaMask!');
            }
        }
    </script>
</head>

<body>
    Hello!
    <script>
        window.addEventListener('load', async () => {
            setTimeout(handleEthereum, 3000);
        });
    </script>
</body>

</html>
EvilJordan commented 3 years ago

MY BAD. My mistake. It DOES fire alerts, but always, "Please install Metamask!"

rekmarks commented 3 years ago

Hm, in that case, something else is wrong. Please open a new issue.

shendel commented 3 years ago

I think, bug in https://github.com/MetaMask/inpage-provider, witch used by https://github.com/MetaMask/mobile-provider/

https://github.com/MetaMask/mobile-provider/blob/master/src/content-script/inpage-bundle.js updated 4 mounth ago

rekmarks commented 3 years ago

@shendel, can you expand on what you think the bug is? There is to my knowledge nothing in inpage-provider that could cause the provider to never be injected as we see here.

rekmarks commented 3 years ago

To expand on that point, inpage-provider exports the function initProvider, which initializes the provider and sets it as window.ethereum. However, the provider script injection is handled by the React Native WebView component, as you can see here.

In reviewing how we handle script injection on Android, it appears that this is due to an outstanding problem with the React Native component. See this and later comments for https://github.com/react-native-webview/react-native-webview/pull/1099.

shendel commented 3 years ago

@rekmarks you're wasting my time (again) i tested 1.0.2 - bug 1.0.4 - bug 1.0.5 - bug 1.0.6 - bug 1.0.7 - bug I did not find more apk in the public domain (I looked well) And the bug is not only on android (https://github.com/MetaMask/metamask-mobile/issues/2015) - on iOS too.... If you make a build for developers (and will not force them to eliminate dependencies, sign something there and other heresy that 90% do not need to know), then maybe I'll spend my time again.

I have no desire, no time to engage in spiritualism, to strain my intuition again - I pointed out a possible (99%) problem If you respected the developers, and made a separate apk for them, then m would not waste your time, and we (third-party developers) would help you

How me debug my app (https://github.com/MetaMask/metamask-mobile/issues/1226) - spend two days of my life to understand that this is not my bug ??

shendel commented 3 years ago

Last apps - metamask image OperaMobile image TrustWallet image CoinBase image

K.O. - Your App (Metamask-mobile) with Your test (dApp) image "Click here to install metamask" - Really?? (https://github.com/MetaMask/metamask-mobile/issues/2015) image

shendel commented 3 years ago

@rekmarks bug there - https://github.com/MetaMask/mobile-provider/blob/master/src/content-script/inpage-bundle.js

rekmarks commented 3 years ago

@shendel, I am sorry that you have had a frustrating experience debugging this issue. We are well aware that the MetaMask Mobile developer experience can leave much to be desired. When we leave something in a state that doesn't meet your — and not to mention our own — expectations, it is because we were forced to prioritize other things. We have talked about releasing an APK in the past, and I will make sure we reevaluate our ability to release one ASAP.

As to your specific problem, whatever is happening in #2015, it is not the same issue as this. Nor is the bug in inpage-provider or mobile-provider. When I go to to https://swaponline.github.io on MetaMask Mobile on iOS, your connection logic works perfectly.

If you review the links in my previous comment, you will see that there is an outstanding issue with react-native-webview where the browser injection logic fails under certain circumstances on Android. I only discovered that outstanding issue this morning, and shared it immediately here.

I am afraid that there is nothing anyone can do until a fix is merged on react-native-webview, or we discover some other workaround. We will update this issue as soon as we learn more.

101001000 commented 3 years ago

Same problem with pocophone F1 android 9, 1/5 times it connects, I have to reload it all the time

treeder commented 3 years ago

@rekmarks I don't think this is specific to React, the same thing happens with Flutter apps too: https://github.com/MetaMask/metamask-mobile/issues/1956

rekmarks commented 3 years ago

Ah, I see what you mean @treeder, but I'm referring to react-native-webview, which is used by the MetaMask Mobile browser.

Edit: In other words, I'm saying that the bug is in the MetaMask Mobile browser, albeit due to a third-party library.

treeder commented 3 years ago

Oh I see, thanks for clarifying. 👍

shendel commented 3 years ago

@rekmarks, Any progress or ideas on how to solve this? Do you understand that waiting for a solution from the developers react-native-webview can take infinity time (forever)? For example - in wallet-connect it was (https://github.com/WalletConnect/walletconnect-monorepo/issues/384), is, and it will probably be a problem ... But its developers just threw a bone for other developers and closed issue without fixing this error ... May be use some hard-fix?? I recently updated the device - on Huawei Y6s (Andoid 9 - updates from November - there is a same problem)

andrepimenta commented 3 years ago

We have identified the problem, the injection is happening sometimes before the document is loaded which causes an error. We are currently actively working on this and we may have a fix for this soon.

open-contracts commented 2 years ago

I'm having this problem. The 'ethereum#initialized' event is not triggered on Android inside the metamask app.

var provider = null;
var user = null;
var contract = null;

if (window.ethereum) {
  setup();
} else {
  window.addEventListener('ethereum#initialized', setup, {
    once: true,
  });
  setTimeout(setup, 30000); // 30 seconds in which nothing happens on android
}

function setup() {
  $('#network').html("starting connection..."); // this is only displayed after 30 seconds
  const {ethereum} = window;
  ethereum.on('chainChanged', (_chainId) => window.location.reload());
  const newAccounts = ethereum.request({method: 'eth_requestAccounts'});
  provider =  new ethers.providers.Web3Provider(ethereum, 'any');
  provider.getNetwork().then((chain) => {$('#network').html(chain.name);});
  //++ const openProvider = new opencontracts.providers.Web3Provider(provider);
  user = provider.getSigner();
}
sikandarhayatkhan commented 2 years ago

Hello, so I have recently encountered this issue 'ethereu#intialized' event is not triggered by metamask on android, this issue is related to the head section of your app, your head section should be empty e.g if you are using react or HTML your head section only contain title nothing else if you are placing style tag in it remove it then try it. it will work fine.

jamesmorgan commented 2 years ago

Hello, so I have recently encountered this issue 'ethereu#intialized' event is not triggered by metamask on android, this issue is related to the head section of your app, your head section should be empty e.g if you are using react or HTML your head section only contain title nothing else if you are placing style tag in it remove it then try it. it will work fine.

Do you me literally nothing? as in you cant load scripts, meta or styles in your head to get MM to work on android?

sikandarhayatkhan commented 2 years ago

Hello, so I have recently encountered this issue 'ethereu#intialized' event is not triggered by metamask on android, this issue is related to the head section of your app, your head section should be empty e.g if you are using react or HTML your head section only contain title nothing else if you are placing style tag in it remove it then try it. it will work fine.

Do you me literally nothing? as in you cant load scripts, meta or styles in your head to get MM to work on android?

Yeah, this is weird but after spending hours this is the only way that works every time.

jamesmorgan commented 2 years ago

It worked. I am using nuxt ssr - looking at the source generated we have the following opening element <head > instead of <head>.

I changed the default template (which appears to leave a trailing space) and it worked ... amazing! No other changes to the head section other than removing this trailing space - main problem was Android Chrome did not work on MM mobile.

So many hours lost looking at this issue thank you very much. @sikandarhayatkhan 🙏🏻

andrewrothman commented 2 years ago

Hi all. I'm still unable to access window.ethereum on Android, even without any <head> at all: https://github.com/MetaMask/metamask-mobile/issues/3258

I'd be very grateful if anyone sees anything wrong with my approach or could use any more info. Thanks so much.

TrueCarry commented 2 years ago

It worked. I am using nuxt ssr - looking at the source generated we have the following opening element <head > instead of <head>.

I changed the default template (which appears to leave a trailing space) and it worked ... amazing! No other changes to the head section other than removing this trailing space - main problem was Android Chrome did not work on MM mobile.

So many hours lost looking at this issue thank you very much. @sikandarhayatkhan 🙏🏻

Got same issue. Looks like source located here https://github.com/MetaMask/metamask-mobile/blob/52636fb44347b68da791cf45dd9f40de31b79270/patches/react-native-webview%2B11.13.0.patch#L272 Maybe someone can implement better <head> parsing?

Maybe parse for </head>?