Code Monkey home page Code Monkey logo

providers's Introduction

MetaMask Providers

The Ethereum provider object injected by MetaMask into various environments. Contains a lot of implementation details specific to MetaMask, and is probably not suitable for out-of-the-box use with other wallets.

The BaseProvider implements the Ethereum JavaScript provider specification (EIP-1193), but must be modified by a sub-class in order to function. StreamProvider is such a sub-class, which synchronizes its state and marshals JSON-RPC messages via a duplex stream. MetamaskInpageProvider further extends StreamProvider to support legacy provider interfaces in addition to EIP-1193, and is used to instantiate the object injected by MetaMask into web pages as window.ethereum.

Usage

import { initializeProvider } from '@metamask/providers';

// Create a stream to a remote provider:
const metamaskStream = new LocalMessageDuplexStream({
  name: 'inpage',
  target: 'contentscript',
});

// this will initialize the provider and set it as window.ethereum
initializeProvider({
  connectionStream: metamaskStream,
});

const { ethereum } = window;

Types

Types are exposed at index.d.ts. They require Node.js EventEmitter and Duplex stream types, which you can grab from e.g. @types/node.

Do Not Modify the Provider

The Provider object should not be mutated by consumers under any circumstances. The maintainers of this package will neither fix nor take responsbility for bugs caused by third parties mutating the provider object.

Contributing

Setup

  • Install Node.js version 16
    • If you are using nvm (recommended) running nvm use will automatically choose the right node version for you.
  • Install Yarn v1
  • Run yarn setup to install dependencies and run any requried post-install scripts
    • Warning: Do not use the yarn / yarn install command directly. Use yarn setup instead. The normal install command will skip required post-install scripts, leaving your development environment in an invalid state.

Testing and Linting

Run yarn test to run the tests once. To run tests on file changes, run yarn test:watch.

Run yarn lint to run the linter, or run yarn lint:fix to run the linter and fix any automatically fixable issues.

Release & Publishing

The project follows the same release process as the other libraries in the MetaMask organization:

  1. Create a release branch
    • For a typical release, this would be based on main
    • To update an older maintained major version, base the release branch on the major version branch (e.g. 1.x)
  2. Update the changelog
  3. Update version in package.json file (e.g. yarn version --minor --no-git-tag-version)
  4. Create a pull request targeting the base branch (e.g. main or 1.x)
  5. Code review and QA
  6. Once approved, the PR is squashed & merged
  7. The commit on the base branch is tagged
  8. The tag can be published as needed

providers's People

Contributors

bitpshr avatar danfinlay avatar danjm avatar darkwing avatar dependabot[bot] avatar frederikbolding avatar github-actions[bot] avatar gudahtt avatar jack-works avatar jiexi avatar jpuri avatar kumavis avatar legobeat avatar louis-kupo avatar majorlift avatar metamaskbot avatar mrtenz avatar rekmarks avatar ryanml avatar shanejonas avatar whymarrh avatar zouhangwithsweet 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  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

providers's Issues

Correctly represent connectivity

According to EIP-1193 (permalink), the provider is "connected" when it can "service RPC requests to at least one chain". Currently the inpage provider doesn't make much of an attempt to guarantee this. Instead, 'connectivity' effectively represents whether or not the stream between the provider and MetaMask is active.

Certainly if the provider can't communicate with MetaMask, it can't service RPC requests either. But there remain many cases when the stream is live and the provider is "connected" but servicing RPC requests is impossible (e.g. when MetaMask is in the process of connecting to a network).

HELP - Can the port stream based provider work in a tab application loaded by the extension?

Can the port stream based provider work in a tab application loaded by the extension?

i.e. when a background script opens a chrome extension app in a tab - can the chrome extension app connect to MetaMask?

I am not talking about content scripts. I am talking about a chrome extension app opening in a new tab.

The reason I wanted to connect to the web browser version of metamask in the tab was so that it is associated with the extension url:

chrome-extension://oahhaapnfpkfnbgeopliehfgenifngde/tab.html#register

and not the url the content script happens to be on.

If it is not possible to connect to Meta Mask in a chrome extension tab app, then is there another way to connect the meta mask browser wallet to a domain url like:

chrome-extension://oahhaapnfpkfnbgeopliehfgenifngde/tab.html#register

I am super struggling finding the answers to this so if anyone can help me I will be super happy.

Thanks for your time.

Refactor @metamask/providers and make it use #hashFunctions for private methods

This is a proposal to do refactoring of the @metamask/providers StreamProvider and make its private methods and properties use #hash approach if possible. That way it would not be exposing methods and properties within its prototype chain which can be a security concern.

It is discovered that harden function from Secure EcmaScript which is used in Snaps and LavaMoat, is freezing some parts of the stream which makes it impossible to work. Because of that, a special way of using Proxy was introduced. By having a real private methods and properties it might be easier to secure this type of issues in the future.

Provider emits warning: ObjectMultiplex - orphaned data for stream "publicConfig"

I'm using createExternalExtensionProvider() to create a Provider to interact with MetaMask from another browser extension. This works well, but a side-effect is that a lot of warnings are logged to the console:

ObjectMultiplex - orphaned data for stream "publicConfig"

I looked into this, and my understanding is as follows:

  • The communication streams between the in page/extension provider and MetaMask itself multiplex multiple named streams over a single connection using ObjectMultiplex from @metamask/object-multiplex
  • The warning occurs when an ObjectMultiplex stream receives a message for a named substream that has not been created, so such messages will be ignored.
  • The MetaMask extension itself is sending messages to the Provider I create for the publicConfig named substream
    • I can see such messages being received from the Port opened to communicate with MetaMask from my extension
    • It's not that some legacy thing in my extension is using the publicConfig stream.
  • MetaMask's own injected contentscript ignores messages for the publicConfig substream, so web pages using MetaMask's provider don't show these warnings: https://github.com/MetaMask/metamask-extension/blob/c40dbb1ec72b91827debb2fffba4097a838f21c7/app/scripts/contentscript.js#L254
  • The StreamProvider in this repo that creates the ObjectMultiplex doesn't ignore publicConfig messages:
    const mux = new ObjectMultiplex();

Should StreamProvider ignore these messages itself, like MetaMask's contentscript does? I was tempted to open a PR to do this, but I don't have enough understanding of the legacy context around these messages to know if that makes sense.

It's not currently possible to customise the stream created by createExternalExtensionProvider() to filter these messages before they get to StreamProvider.

Split inpage-provider into multiple packages

This package should be split into the following packages, or at least classes:

  • A BaseProvider, implementing the EIP-1193 API and nothing else
  • A MetaMaskInpageProvider, for window environments
    • This class should be the only one that contains our legacy provider features.
  • An ExtensionProvider, for other extensions

For future environments, e.g. Snaps, we'll add a class/package in the future.

`publicConfigStore` property renamed in v4.0.0

The provider property publicConfigStore was renamed in #11 and released in v4.0.0 of the inpage provider. We hadn't realized at the time that dapps were using this property directly, and that this change would break dapps.

We should restore the old property name for now as an alias, and warn that the property will soon be removed. Ultimately we'll still need to remove it at some point, since we're planning on replacing the publicConfigStore altogether.

Provider instance is created even when MetaMask is not installed

I am using this package to work around a Firefox issue where the eth provider cannot be injected: MetaMask/metamask-extension#3133

On Chromium with no metamask extension installed, this package still creates a full provider object, though some of the properties are undefined.

I am just wondering what is the correct way to check if the provider is good to use. I am planning for now to check the value of chainId, but I don't know if this is actually a bug.

Here is the console.log output for the provider:

Proxy {_events: {…}, _eventsCount: 1, _maxListeners: 100, isMetaMask: true, _state: {…}, …}
[[Handler]]: Object
deleteProperty: () => true
__proto__: Object
[[Target]]: MetamaskInpageProvider
autoRefreshOnNetworkChange: false
chainId: undefined
enable: ƒ ()
isMetaMask: true
networkVersion: undefined
request: ƒ ()
selectedAddress: null
send: ƒ ()
sendAsync: ƒ ()
_events: {connect: ƒ}
_eventsCount: 1
_handleAccountsChanged: ƒ ()
_handleDisconnect: ƒ ()
_maxListeners: 100
_metamask: Proxy {isUnlocked: ƒ, requestBatch: ƒ, isEnabled: ƒ, isApproved: ƒ}
_publicConfigStore: ObservableStore {_events: {…}, _eventsCount: 1, _maxListeners: undefined, _state: {…}}
_rpcEngine: RpcEngine {_events: {…}, _eventsCount: 0, _maxListeners: undefined, _middleware: Array(3)}
_rpcRequest: ƒ ()
_sendSync: ƒ ()
_state: {sentWarnings: {…}, isConnected: true, accounts: undefined, isUnlocked: undefined}
_warnOfDeprecation: ƒ ()
_web3Ref: undefined
__proto__: SafeEventEmitter
[[IsRevoked]]: false

Incompatible types `MetaMaskInpageProvider` and `ethers.providers.Web3Provider()`

Hello, I installed the typings for Metamask provider from this @metamask/providers package. Since we allow multiple providers to be used, we wrap the provider into standardized Web3Provider from ethers package. However, I am unable to pass the MetaMaskInpageProvider to Web3Provider constructor like this:

import { ethers } from 'ethers'
import type { MetaMaskInpageProvider } from '@metamask/providers'
  
const setWalletProvider = async (newWalletProvider: MetaMaskInpageProvider): Promise<void> => {
    walletProvider.value = new ethers.providers.Web3Provider(newWalletProvider) // this throws
}

Error is following:

Argument of type 'MetaMaskInpageProvider' is not assignable to parameter of type 'ExternalProvider | JsonRpcFetchFunc'.   
Type 'MetaMaskInpageProvider' is not assignable to type 'ExternalProvider'.     
Types of property 'sendAsync' are incompatible.       
Type '(payload: JsonRpcRequest<unknown>, callback: (error: Error, result?: JsonRpcResponse<unknown>) => void) => void' is not assignable to type '(request: { method: string; params?: any[]; }, callback: (error: any, response: any) => void) => void'.         
Types of parameters 'payload' and 'request' are incompatible.           
Type '{ method: string; params?: any[]; }' is missing the following properties from type 'JsonRpcRequest<unknown>': jsonrpc, id

I can easily surpress the error by @ts-ignore, but do you think it would be possible to make the MetaMaskInpageProvider compatible with the ethers.providers.Web3Provider? Or should I rather post an issue to the ethers? Thanks!

Auto inject ethereum to active iframe when injected to top frame

Hi,

I noticed that metamask extension injects provider via inpage.js only to the top frame.
How then the iframes have window.ethereum if it's only injected in top frame?
When I try to, I only have window.ethereum in the top frame.

I'm using manifest v3 and my inpage script runs the same way as metamask's
and it does

  const provider = initializeProvider({
    jsonRpcStreamName: RPC_STREAM_NAME,
    connectionStream: myStream as Duplex,
    logger: console,
    shouldShimWeb3: true
  });

Thanks.

Better error message when calling `BaseProvider.request()` without `params`?

Environment:
Brave browser with MetaMask v9.8.4 extension installed. Running code in console


The following fails with a confusing error: MetaMask - RPC Error: Cannot read property 'length' of undefined

window.ethereum
  .request({
    method: "eth_blockNumber",
  })

However, this succeeds:

window.ethereum
  .request({
    method: "eth_blockNumber",
    params: [],
  })

Some methods like ethereum.request({ method: "eth_requestAccounts" }) don't require params to be set, so I spent an embarrassing of time trying to find out why one call worked and the other didn't.

Context:

I was trying to figure out why a convenience library was failing and figured that going down a level down to the RPC level might help. I tried the most basic RPC request I could think of (getting block number) when I got this error.

Possible solutions:

  1. One solution might be for the MetaMask to add params: [] when no params are specified.
  2. Another solution might be to add some more error checking to the BaseProvider request method?

Issue connecting Dapps to my forked & customized version of metamask.

Hi,
I forked the Metamask wallet repo, and have been adding some features I believe will make the application even more powerful.
I'm trying to connect to my updated version of the wallet using live Dapps but they can't seem to distinguish between the two wallets, when installed on the same browser.
I believe its a provider issue, and i've been working to find a way to distinguish the wallets as separate wallets to Dapps to no avail.
This is the metamask/provider package which handles the connection by Dapps to Metamask in this file Baseprovider.ts.

I found the handleConnect function inside, but can't seem to make sense of how it handles the connection by Dapps, and how I can edit it to suit my needs.
I'm hoping someone that better understands the project can point me to what I need to change to achieve this.
I’ll really appreciate any help.
Thanks.

`data` event missing deprecation warning

The data event warns upon use because it is deprecated, but the deprecation message itself is missing. Instead undefined is printed to the console as a warning.

Rename package

This package now contains a base 1193 provider, a window/DOM provider, and an external extension provider.

We should rename this package to @metamask/providers to reflect this.

Enable clone()

As identified in Web3 Issue 3573, when using both TruffleContract and web3 functions it is important that they have separate Provider objects.

In the browser, the injected web3 has a web3.currentProvider which is a MetamaskInpageProvider, but passing this same object to a TruffleContract's setProvider function seems likely to produce issues like that one identified in web3.

To address this, the MetamaskInpageProvider might benefit from a clone() function producing another copy of the object, so that there can be independent queues serving overlapping sequences of requests from calls to the web3 object directly + calls to TruffleContract objects.

chainChanged emits on page load

Describe the bug
Sometimes chainChanged message emits on every page load. So if I have this line in my code: ethereum.on('chainChanged', (_chainId) => window.location.reload()); then user gets endless page refresh loop.

To Reproduce
I couldn't reproduce this, but one of our user shared his screen and I saw this endless page refresh loop. I added console.log('chainChanged'); to that event callback, and console showed this log every time before page refreshed.

Expected behavior
chainChanged event should be sent only on chain change, not on page load

Browser details

  • OS: OS X
  • Browser: last Chrome
  • MetaMask Version: 8.0.8

failed to parse source map

`Failed to parse source map from '/home/haseeb/work/migration/node_modules/@metamask/providers/src/BaseProvider.ts' file: Error: ENOENT: no such file or directory, open '/home/haseeb/work/migration/node_modules/@metamask/providers/src/BaseProvider.ts'

Failed to parse source map from '/home/haseeb/work/migration/node_modules/@metamask/providers/src/MetaMaskInpageProvider.ts' file: Error: ENOENT: no such file or directory, open '/home/haseeb/work/migration/node_modules/@metamask/providers/src/MetaMaskInpageProvider.ts'

Failed to parse source map from '/home/haseeb/work/migration/node_modules/@metamask/providers/src/extension-provider/createExternalExtensionProvider.ts' file: Error: ENOENT: no such file or directory, open '/home/haseeb/work/migration/node_modules/@metamask/providers/src/extension-provider/createExternalExtensionProvider.ts'

Failed to parse source map from '/home/haseeb/work/migration/node_modules/@metamask/providers/src/index.ts' file: Error: ENOENT: no such file or directory, open '/home/haseeb/work/migration/node_modules/@metamask/providers/src/index.ts'

Failed to parse source map from '/home/haseeb/work/migration/node_modules/@metamask/providers/src/initializeInpageProvider.ts' file: Error: ENOENT: no such file or directory, open '/home/haseeb/work/migration/node_modules/@metamask/providers/src/initializeInpageProvider.ts'

Failed to parse source map from '/home/haseeb/work/migration/node_modules/@metamask/providers/src/messages.ts' file: Error: ENOENT: no such file or directory, open '/home/haseeb/work/migration/node_modules/@metamask/providers/src/messages.ts'

Failed to parse source map from '/home/haseeb/work/migration/node_modules/@metamask/providers/src/shimWeb3.ts' file: Error: ENOENT: no such file or directory, open '/home/haseeb/work/migration/node_modules/@metamask/providers/src/shimWeb3.ts'

Failed to parse source map from '/home/haseeb/work/migration/node_modules/@metamask/providers/src/siteMetadata.ts' file: Error: ENOENT: no such file or directory, open '/home/haseeb/work/migration/node_modules/@metamask/providers/src/siteMetadata.ts'

Failed to parse source map from '/home/haseeb/work/migration/node_modules/@metamask/providers/src/utils.ts' file: Error: ENOENT: no such file or directory, open '/home/haseeb/work/migration/node_modules/@metamask/providers/src/utils.ts'`

also when i check the .js.map files in the dist folder i realized the source path is incorrect e.g all the source paths are directing to src folder but in reality there is no src folder and no .ts file
e.g {"version":3,"file":"ObjectMultiplex.js","sourceRoot":"","sources":["../src/ObjectMultiplex.ts"],

Events don't work

I use the inpage-provider with the "extension-port-stream" package at my extension's index page.
The application with the provider can't get subscription events (for example at newBlockHeaders, or receive transaction receipts after send).

this.web3.client.eth.subscribe('newBlockHeaders', (error, result) => {
      if (error) {
        console.error("Error happened at new block", error);
        return;
      }
    })
    .on("data", function(blockHeader){
      console.log("blockHeader", blockHeader);
    });

It shows some errors because of wallet_sendDomainMetadata, unexcepted account change
and also the eventemitter memory leak, but the provider also seems initialized correct way, also I can send transactions, etc.
Screenshot from 2020-02-06 19-01-25
Screenshot from 2020-02-06 17-46-03
Screenshot from 2020-02-06 17-46-14

Init process:

    const metamaskPort = chrome.runtime.connect("nkbihfbeogaeaoehlefnkodbefgpgknn");
    const pluginStream = new PortStream(metamaskPort);
    let provider = new MetamaskInpageProvider(pluginStream);
    console.log("provider", provider);
    this.web3.client = new Web3(provider);

version: 4.0.4 (npm)
web3: 1.2.6
browser: Chrome 80.0.3987.87

Retrieve RPC URL the user is using

Hey All,

I have a question about whether or not it is possible to retrieve the RPC URL the user currently has for a particular chain ID?

I've seen a couple of similar questions but no concrete answers so my current knowledge leads to think there is no simple way to get this information from Metamask.

https://ethereum.stackexchange.com/questions/91111/recover-rpc-url-in-metamask?noredirect=1&lq=1
https://ethereum.stackexchange.com/questions/69782/get-rpc-url-ip-of-metamasks-current-provider?noredirect=1&lq=1

The reason I am looking for something like this is because when we ask a user to add a new chain configuration using wallet_addEthereumChain , if the RPC endpoint/node provider we initially supplied stops working then we would like to be able to check this against some other providers, inform users about this and request they find a new public RPC endpoint etc.

Thanks!

Richard

Protect internals of the provider classes using hash names

Currently in snaps-monorepo we wrap the Ethereum provider in a Proxy, to prevent access to internal fields. A better alternative would be to protect these fields in the first place using hash names, which are not accessible in any way.

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.