Ethers.js: Incorrect chain Id when instantiating ethers Provider from web3.currentProvider

Created on 2 Apr 2018  Â·  11Comments  Â·  Source: ethers-io/ethers.js

Metamask in the browser is pointing to the rinkeby network:

>> (new Web3(web3.currentProvider)).version.network
"4"

However, when calling new ethers.providers.Web3Provider(web3.currentProvider), the provider still thinks it is on homestead.

>> (new ethers.providers.Web3Provider(web3.currentProvider))
Object { chainId: 1, ensAddress: "0x314159265dD8dbb310642f98f50C066173C1259b", name: "homestead", testnet: false, _events: {}, resetEventsBlock: w(), url: "unknown", _web3Provider: {…}, … }

Function calls seem to work as intended (and referring to the correct network) in spite of this.

Also, when pointing provider to ganache:

provider = new ethers.providers.JsonRpcProvider()
< h {chainId: 1, ensAddress: "0x314159265dD8dbb310642f98f50C066173C1259b", name: "homestead", testnet: false, _events: {}, …}

Perhaps this is expected behaviour, though I would expect the testnet key to be true.

Thank you for this awesome library 🥇

discussion

Most helpful comment

Thinking more about this, what about a new method, for situations where you have a provider running and want to connect to it? Like:

JsonRpcProvider.connect(url).then(function(provider) {
});

which will do network discovery for you, asynchronously?

All 11 comments

I tried this code.

const ethers = require('ethers')
const Web3 = require('web3')
const eth1 = new ethers.providers.Web3Provider(new Web3('http://localhost:8545'))
console.log(eth1.chainId)
// 1
const eth2 = new ethers.providers.JsonRpcProvider('http://localhost:8545')
console.log(eth2.chainId)
// 1
const eth3 = new ethers.providers.EtherscanProvider(ethers.providers.networks.ropsten)
console.log(eth3.chainId)
// 3
const eth4 = new ethers.providers.Web3Provider(new Web3('http://localhost:8545'), ethers.providers.networks.ropsten)
console.log(eth4.chainId)
// 3
const eth5 = new ethers.providers.JsonRpcProvider('http://localhost:8545', ethers.providers.networks.ropsten)
console.log(eth5.chainId)
// 3

https://docs.ethers.io/ethers.js/html/api-providers.html
In the document, default network is homestead, so it looks like a correct behavior.

@ricmoo Why is it such a specification? I think that it can acquire networkid automatically.

Some type of providers can detect it (mainly JSON-RPC API nodes), but others cannot. For example, the Etherscan provider. And soon we will have a lot more backends to connect to.

The main problem is that the network needs to be known synchronously, which is not possible using a the JSON-RPC.

I am considering making the network an async process in the future, which would allow discovery, but this is. It backwards compatible and will not be available until ateast the next major version.

The main expectation of ethers though, is that you choose what network you want to connect to and it handles the rest.

When you instantiate a Provider, you should make sure to pass in the network you expect it to be:

const eth1 = new ethers.providers.Web3Provider(new Web3('http://localhost:8545'), ethers.providers.networds.rinkeby);

For higher level API, like ethers-app, this isn't necessary, because it creates a provider for you and does all the detection.

Thinking more about this, what about a new method, for situations where you have a provider running and want to connect to it? Like:

JsonRpcProvider.connect(url).then(function(provider) {
});

which will do network discovery for you, asynchronously?

I think I found a workaround, when using metamask. Metamask does provide the network id using this function call:

>>web3.currentProvider.publicConfigStore._state.networkVersion
"1"

or web3.version.network

This can then be mapped to the network name and passed into the ethers providers constructor as shown in a previous comment.

An asynchronous connect function would be handy but not necessary.

Otherwise, yes I see that there is no way for the providers constructor to have knowledge of whatever network the user is running. However, it does surprise me that the default values of the ethers.providers constructor will make assumptions about the network if it does not know for sure what the network is.

The default network is homestead. If you are connecting to another network, you must specify which network it is. Likewise, if you just ask to connect to Ethereum (i.e. providers.getDefaultNetwork()), it connects to homestead backends.

Closing this. In the next major version, I may make the network configuration asynchronous, which means this can be handled as part of the instantiation.

It may also make sense to add a connect method to JsonRpcProvider... I'll add a new task for that.

Maybe I'm seeing something wrong, but I think it's important to match the network provided by, for instance, a Web3 provider (MetaMask) when using Ethers. Right now, the best example I have (based on this thread) is:

// Get Web3 chain
const web3ChainId = Number(web3.currentProvider.publicConfigStore._state.networkVersion);

// Find a network with matching chainId (there could be multiple)
const network = Object.values(Ethers.providers.networks).find(({chainId}) => chainId === web3ChainId);

// Create provider with this network
const provider = new Ethers.providers.Web3Provider(web3.currentProvider, network);

The problem is there is no synchronous way to detect the chain ID inside a providers constructor.

This has been fixed though in v4, which allows subclasses to specify a ready function, so the network is populated asynchronously.

I’m hoping to get that version released to npm this week, in which case just passing in the Web3.currentProvider will just “do the right thing”, and the only option to override the network will be to disable EIP-155, so you shouldn’t need to ever manually detect it. :)

That would be awesome. Thanks for thinking this one through @ricmoo

Was this page helpful?
0 / 5 - 0 ratings