Git Product home page Git Product logo

detect-provider's Introduction

@metamask/detect-provider

A tiny utility for detecting the MetaMask Ethereum provider, or any provider injected at window.ethereum.

It has 0 dependencies and works out of the box in any modern browser, for synchronously and asynchronously injected providers.

Usage

Keep in mind that the providers detected by this package may or may not support the Ethereum JavaScript Provider API. Please consult the MetaMask documentation to learn how to use our provider.

Node.js

import { detectEthereumProvider } from '@metamask/detect-provider'

const provider = await detectEthereumProvider()

if (provider) {

  console.log('Ethereum successfully detected!')

  // From now on, this should always be true:
  // provider === window.ethereum

  // Access the decentralized web!

  // Legacy providers may only have ethereum.sendAsync
  const chainId = await provider.request({
    method: 'eth_chainId'
  })
} else {

  // if the provider is not detected, detectEthereumProvider resolves to null
  console.error('Please install MetaMask!')
}

HTML

<script src="https://unpkg.com/@metamask/detect-provider/dist/detect-provider.min.js"></script>
<script type="text/javascript">
  const provider = await detectEthereumProvider()

  if (provider) {
    // handle provider
  } else {
    // handle no provider
  }
</script>

Options

The exported function takes an optional options object. If invalid options are provided, an error will be thrown. All options have default values.

options.mustBeMetaMask

Type: boolean

Default: false

Whether window.ethereum.isMetaMask === true is required for the returned Promise to resolve.

options.silent

Type: boolean

Default: false

Whether error messages should be logged to the console. Does not affect errors thrown due to invalid options.

options.timeout

Type: number

Default: 3000

How many milliseconds to wait for asynchronously injected providers.

Advanced Topics

Synchronous and Asynchronous Injection

Providers can be either synchronously or asynchronously injected:

  • Synchronously injected providers will be available by the time website code starts executing.
  • Asynchronously injected providers may not become available until later in the page lifecycle.

The MetaMask extension provider is synchronously injected, while the MetaMask mobile provider is asynchronously injected.

To notify sites of asynchronous injection, MetaMask dispatches the ethereum#initialized event on window immediately after the provider has been set as window.ethereum. This package relies on that event to detect asynchronous injection.

Overwriting or Modifying window.ethereum

The detected provider object returned by this package will strictly equal (===) window.ethereum for the entire page lifecycle, unless window.ethereum is overwritten. In general, consumers should never overwrite window.ethereum or attempt to modify the provider object.

If, as a dapp developer, you notice that the provider returned by this package does not strictly equal window.ethereum, something is wrong. This may happen, for example, if the user has multiple wallets installed. After confirming that your code and dependencies are not modifying or overwriting window.ethereum, you should ask the user to ensure that they only have a single provider-injecting wallet enabled at any one time.

detect-provider's People

Contributors

bowensanders avatar dckesler avatar dependabot[bot] avatar github-actions[bot] avatar httpjunkie avatar legobeat avatar maui-r avatar mcmire avatar metamaskbot avatar rekmarks avatar renzholy avatar rubenswieringa avatar ryanml avatar whymarrh avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

detect-provider's Issues

Can't detect provider on Android Metamask browser

Hi, I built a DAPP with React and React router but I got problem on Android Metamask browser only.

When the page is loaded directly from the URL https://example.com/mint this library can't find any provider for my page.

However, this library will able to detect provider when I'm navigating from the main page https://example.com to https://example.com/mint

I hope someone has resolved this issue and is able to help out. Thanks in advance.

Make compatible with EIP-6963

It appears that EIP-6963 is headed for finalization. Once this EIP has been implemented in all MetaMask clients, this package should be updated as well. In particular, we should define a new export requestProvider (as implemented here), and deprecate (but not remove) the other exports / functionality of this package. Clients should continue to emit ethereum#initialized for backwards compatibility.

Linting process is outdated

In order to achieve standardization with other libraries, we should:

  • Update the set of ESLint config packages in dependencies to reflect the module template along with the ESLint config file
  • Update the lint and lint:fix package scripts to reflect the module template
  • Add missing package scripts lint:changelog, lint:dependencies, lint:eslint, lint:misc (use the module template)
  • Add missing dependencies eslint-config-prettier, eslint-plugin-prettier, prettier, prettier-plugin-packagejson

Build process is outdated

In order to achieve standardization with our other libraries, we should:

  • Remove browserify as a dependency
  • Add tsup as a dependency (copy version from module template)
  • Remove build.sh
  • Add the build:types package script (copy from module template)
  • Update the build package script to use tsup to compile the TypeScript code to JavaScript and tsc to create TypeScript declaration files.
  • Rename the typings field in package.json to types
  • Add exports field to package.json (copy from module template)
  • Add module field to package.json (copy from module template)
  • Add sideEffects field to package.json (copy from module template)
  • Remove trailing slash from dist/ in files field

What is the goal of this tool?

My understanding is that this tool is a bit of hand-holding for new MetaMask developers. It solves common struggle of getting the provider in the context of browser/server-side environments.

However, the naming is confusing because it sounds like it will tell me which wallet is injected into the browser. To add insult to injury, you return whether the wallet is MM, but don't do this for other wallets.

I would suggest that the goal of the tool is clarified, and either 1) the tool should work for and report on all wallets, or 2) the name be changed to something like getProviderso that option 1 can be developed elsewhere.

GitHub workflows are outdated

In order to align with our other libraries, we should ensure that the .github directory reflects the module template.

Note that before this ticket is completed, we will need to:

  • Add TypeDoc
  • Add NPM_TOKEN, PUBLISH_DOCS_TOKEN, and SLACK_WEBHOOK_URL as secrets

Failed to parse source map - Error: ENOENT: no such file or directory

I've added the dependency: npm i @metamask/detect-provider

But getting this error:

Failed to parse source map from '/home/pomatti/pjs/uniswap/uniswap-widget/node_modules/@metamask/detect-provider/src/index.ts' file: Error: ENOENT: no such file or directory, open '/home/pomatti/pjs/uniswap/uniswap-widget/node_modules/@metamask/detect-provider/src/index.ts'

Only thing I'm doing is importing it:

import detectEthereumProvider from '@metamask/detect-provider';

No idea why it is complaining:

image

Provider not found in metamask app

Dear team
I am using @metamask/provider to detect the provider in my webapp. Upon integration, I tested that it works fine on web (chrome extension) and iOS (metamask app). However, on android (metamask app), its not able to detect the provider. I've added alerts on the page as well to debug the same. On web, the alerts work fine. On android metamask app, it shows provider not found, please install metamask. I'm unable to understand why the same is not getting detected in android app. Please help here.

The url for my webapp - https://fervent-pike-7afa22.netlify.app

Readme's description of the behaviour of mobile apps.

README say

The MetaMask extension provider is synchronously injected, while the MetaMask mobile provider is asynchronously injected.

But #38 (comment) gave different answer.

Mobile is different because we don't provide MetaMask through a browser extension, we offer a separate app. Apps can't talk directly to each other, so if you have a dapp open in iOS Chrome, iOS Safari, Android Chrome, Android Firefox, etc., the site can't know that you have MetaMask installed, because no such script is injected on the page like it is in the browser on desktop.

I thought that the latter was probably correct and that the README (and MetaMask Docs) should be amended.

@metamask/detect-provider: Unable to detect window.ethereum on Android browsers and Metamask app

The detect provider is unable to find the provider in Chrome, Android browser & Metamask App on version 10 of Android. Same web app works fine on desktop. Code is from the example given on the metamask docs. No CSP are set. Website was built using Webflow plus custom JS script for Web3. I'm at a complete lose as to what is happening, seems to be the same on dapps like pancakeswap except in Metamask App itself, when I choose Metamask it tells me: provider not found :/

Accounts

Hello, when I use the second account of my metamask wallet, this second account is not considered in execution. This is the first account that is always taken in the execution

Reject instead of resolve(null) when `mustBeMetamask` is true

Firstly, thanks for your work on this project! ๐Ÿ™‚

Rationale

There are times when multiple wallet provider extensions are installed inside the users browser alongside MetaMask.
Some extensions play together nicely (for example Metamask & Coinbase) but some are simply overwriting the window.ethereum property with their own injection (for example Metamask & Frame Companion).

In these cases, i would like to be able to call await detectEthereumProvider({mustBeMetamask: true}) and get a rejection if no MetaMask was found at window.ethereum instead of a null resolution. This way we can handle this error in our dapps and provide some meaningful information to our users as to why they cannot connect.


Here is the diff that solved my problem:

diff --git a/src/index.ts b/src/index.ts
index 2141c3e..f3248ad 100644
--- a/src/index.ts
+++ b/src/index.ts
@@ -40,7 +40,7 @@ function detectEthereumProvider<T = MetaMaskEthereumProvider>({
 
   let handled = false;
 
-  return new Promise((resolve) => {
+  return new Promise((resolve, reject) => {
     if ((window as Window).ethereum) {
 
       handleEthereum();
@@ -72,13 +72,17 @@ function detectEthereumProvider<T = MetaMaskEthereumProvider>({
       if (ethereum && (!mustBeMetaMask || ethereum.isMetaMask)) {
         resolve(ethereum as unknown as T);
       } else {
+        const expectedInjectionMissmatch = mustBeMetaMask && ethereum;
 
-        const message = mustBeMetaMask && ethereum
+        const message = expectedInjectionMissmatch
           ? 'Non-MetaMask window.ethereum detected.'
           : 'Unable to detect window.ethereum.';
 
         !silent && console.error('@metamask/detect-provider:', message);
-        resolve(null);
+
+        if(expectedInjectionMissmatch){
+          reject(new Error(message))
+        }
       }
     }
   });
END

No provider selected for request eth_chainId when coinbase wallet extension is installed

Repro:
Install Coinbase Wallet

const prov: any = await detectEthereumProvider({ mustBeMetaMask: true });
const chainId: string = await prov.request({ method: 'eth_chainId' });
Error: No provider selected for request eth_chainId

From the docs:
This may happen, for example, if the user has multiple wallets installed. After confirming that your code and dependencies are not modifying or overwriting window.ethereum, you should ask the user to ensure that they only have a single provider-injecting wallet enabled at any one time.

Any solutions other than messaging?

@metamask/detect-provider: Unable to detect window.ethereum.

I'm building a hybrid app using Ionic.

When I serve the app in the browser (Chrome), it works fine asking for the Metamask authentication (opens Metamask as expected).

However when I build that for native Android (I'm using Capacitor), even though the app opens on my Android phone normally after I launch it from Android Studio, it does not ask me to authenticate with Metamask.

After doing some troubleshoot I attached to the app (via Chrome Device Inspect) running on my real phone which is plugged via USB in my computer, and this is what I get on the native app:

common.js:166 @metamask/detect-provider: Unable to detect window.ethereum.
handleEthereum @ common.js:166
23:50:21.508 vendor.js:86490 ERROR Error: Uncaught (in promise): TypeError: Cannot read properties of null (reading 'request')
TypeError: Cannot read properties of null (reading 'request')
    at src_app_home_home_module_ts.js:272:22
    at ZoneDelegate.invoke (polyfills.js:413:30)
    at Object.onInvoke (vendor.js:108502:25)
    at ZoneDelegate.invoke (polyfills.js:412:56)
    at Zone.run (polyfills.js:173:47)
    at polyfills.js:1331:38
    at ZoneDelegate.invokeTask (polyfills.js:447:35)
    at Object.onInvokeTask (vendor.js:108490:25)
    at ZoneDelegate.invokeTask (polyfills.js:446:64)
    at Zone.runTask (polyfills.js:218:51)
    at resolvePromise (polyfills.js:1268:35)
    at polyfills.js:1338:21
    at ZoneDelegate.invokeTask (polyfills.js:447:35)
    at Object.onInvokeTask (vendor.js:108490:25)
    at ZoneDelegate.invokeTask (polyfills.js:446:64)
    at Zone.runTask (polyfills.js:218:51)
    at drainMicroTaskQueue (polyfills.js:633:39)
    at ZoneTask.invokeTask (polyfills.js:533:25)
    at ZoneTask.invoke (polyfills.js:518:52)
    at data.args.<computed> (polyfills.js:3076:36)

What should I do in order to have the native app being able to authenticate with the Metamask installed app?

Thanks!

Its seems the files for latest version is missing

Module not found: Error: Can't resolve '@metamask/detect-provider' in '/Users/steve/sites/vue3-web3modal/src/Web3Modal/components/web3Modal'

Even After installation. Folder in node_modules is empty

Allow working with strict CSPs

TIL: Our current mobile script injection technique violates some CSPs!

I just got off a call with a team that currently can't use MetaMask because their page's CSP refuses to interact with an inline script.

It would help them if we also allowed connecting via a non-injected provider. Maybe we should set up detect-provider to bring its own inpage-provider, so it allows side-stepping this issue.

I'm reaching out to them to see what CSP this might be, I think a safely strict testing csp might be script-src: 'none'.

Unexpected behavior when there are multiple injected providers

Steps to reproduce

  1. Install & setup MetaMask
  2. Install & setup another injected wallet (e.g. CoinbaseWallet)
  3. const provider = await detectEthereumProvider({ mustBeMetaMask: true })
  4. await provider.request({ method: 'eth_requestAccounts' })

Expected behavior

  1. MetaMask wallet prompts for connection
  2. CoinbaseWallet does not prompt for connection

Actual behavior

  1. MetaMask wallet prompts for connection
  2. CoinbaseWallet prompts for connection

JSDocs aren't being generated/published

In order to align with our other libraries, we should:

  • Add typedoc as a dependency (use the same version as in the module template)
  • Add a build:docs package script (refer to the module template)
  • Add publish-docs, publish-main-docs, and publish-rc-docs GitHub workflows (see module template)

Unnecessary wait on desktop when not metamask installed

Hello, I'm wondering - if I understand this correctly, if there is a window.ethereum object on desktop, it's available synchronously on website load. But still, when you dont have metamask installed on the desktop, it still waits for the set timeout (3s by default). But this wait is not necessary in this situation on desktop, is it? Is it necessary on mobile, when the window.ethereum is injected asynchronously and it can take some time, right?

Have you considered adding there another check after first check on window.ethereum to check, if I am on desktop and if I was and initial window.ethereum check would return false, the detectEthereumProvider would resolve with null immediately? I can try submit a PR, if this makes sense and I understand this correctly. Thanks!:)

Does not detect ethereum provider if redirection happens into the metamask mobile browser

If a dApp redirects the user to the metamask mobile app using deep-link, then this library does not detect provider on page load,

const provider = await detectEthereumProvider();

if (provider) {
  // Detected
} else {
  // window.ethereum is undefined
}

When the dApp opens into the metamask mobile app browser, then always for the first time, it goes into the else condition and when page is reloaded, then it works fine.

Any suggestion is appreciated.
Thanks.

Error: invalid BigNumber

Hello, I can't solve this
Error: invalid BigNumber string (argument="value", value=" ", code=INVALID_ARGUMENT, version=bignumber/5.4.2)

MetaMask detect connection does not detect my mobile MetaMask App

const detectProv = async () => {
        const provider = await detectEthereumProvider()
        let web3 = new Web3(provider)
        let chainID = await web3.eth.getChainId()
        window.alert(chainID)

        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) {
                console.log('Ethereum successfully detected!');
                window.alert("detected")
                // Access the decentralized web!
            } else {
                console.log('Please install MetaMask!');
            }
        }
    }

I try to detect my MetaMask application as the provider via a web-site using detect-provider library. In the desktop, it works fine but when I try to detect the provider using the same website and connect it to via my phone, it does not detect any provider. Where am I wrong?

Property 'request' does not exist on type 'MetaMaskEthereumProvider'.ts(2339)

I am getting this using the request similar to the preview example:

 import detectEthereumProvider from "@metamask/detect-provider";

  const provider = await detectEthereumProvider({
    mustBeMetaMask: true,
  });

  if (provider) {
    const accounts = provider?.request?.({
          method: "eth_requestAccounts",
   });

    return accounts?.[0];
  };

Where is new declaration (.d.ts) file? (typescript import error)

Could not find a declaration file for module '@metamask/detect-provider'. '/Users/xxxx/GitHub/yyyy/celo-web-signer/node_modules/@metamask/detect-provider/index.js' implicitly has an 'any' type. Try npm install @types/metamask__detect-provider if it exists or add a new declaration (.d.ts) file containing declare module '@metamask/detect-provider';ts(7016)

Does not detect provider in Metamask browser with Nuxt

Provider is not detected in Metamask browser.

My application is built with Nuxt, but the problem persists even when using the CDN version of detect-provider, rather than the NPM package. I believe this approach should bypass any Nuxt-specific build issues.

I have tried increasing the timeout to 15 seconds.

Sometimes the provider is detected after refreshing the page, but this behavior is not consistent.

Test harness is outdated

To achieve consistency with our other libraries, we should

  • Convert the tests in this repo from Tape to Jest (also using TypeScript instead of JavaScript)
  • Add @types/jest, jest, jest-it-up, and ts-jest as dependencies (use same versions as in module template)
  • Update the test package script to run jest && jest-it-up instead of tape (copy from the module template)
  • Add a test:watch package script (copy from the module template)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.