ChainId vs NetworkId? How do they differ on Ethereum?

Pedro Gomes
4 min readJul 3, 2019

--

Back in December last year, I had hit way too many road blocks while building WalletConnect. Since its inception I’ve learned a lot from the feedback of several projects building on Ethereum and realized that I had to completely re-write its architecture. Thus I started from a blank page and re-wrote it from scratch.

One of my main goals was to find the lowest common denominator between all Dapp-Wallet communication and I quickly realized that it was the accounts array and the active chainId for the session. This however contradicted the design of most, if not all, Dapps. We’d grown used to tracking the networkId and not the chainId, but why did I find the chainId to be necessary instead?

The reason was that it was needed to sign transactions. As part of the EIP-155 which standardizes how clients handle transaction replay attack protection defines the V parameter, part of the transaction signature, to be calculated from the chainId.

But why do we even need transaction replay attack protection? Say we have a Dapp requesting the Wallet to sign a transaction to transfer 1 ETH on Ropsten. Once you sign it, nothing was stopping the Dapp to misuse and broadcast it on Mainnet instead thus effectively stealing 1 ETH with actual value. That’s exactly why the chainId was introduced to protect from and it was introduced right after the “The DAO” fork when Ethereum and Ethereum Classic split.

However until then we only had the networkId to track which network/chain was active but the truth is that networkId is used for the peer-to-peer communication between nodes and it doesn’t actually concern about the integrity of transactions of the blockchain. Fortunately the networkId has always been the same as the chainId so there hasn’t been any reports of invalid transactions due to Wallets signing transactions using the networkId.

This however caused a huge technical debt in the ecosystem where we’ve been misusing the wrong variable to sign transactions until now. This was also amplified by the inability for a long time to query the chainId from the node’s API. It was only until a year after the EIP-155, that the EIP-695 introduced the JSON-RPC method `eth_chainId` which allowed Dapps to track the chainId.

You may be thinking: Is this really a problem? Are there any chains with different networkId and chainId? That’s exactly what I wanted to find out around the same time I was re-writing WalletConnect so I started gathering lists that were scattered throughout Github of active EVM chains and their respective chainId and networkId. Around the time I contacted Ligi to add the list to a repository as part of the Ethereum Lists organization on Github.

Since then a lot of contributors have helped to grow this list and it has even been added as reference to the EIP-155. There are now listed 48 different EVM chains (including testnets) plus we even came across a chainId conflict which we ended up resolving the dispute on Github by identifying which chain had the earliest genesis block and requesting the other to change their chainId. This was a pretty interesting side effect of what started as a curious pursuit on my end .

However are there any chains with unmatched ids? As matter of fact 13 of those 48 chains have unmatched ids and you can see them listed bellow.

You can check all chains on the chainId.network website generously put together by Boris and I’ve also recently published an NPM package that allows you to easily fetch EVM chains by chainId and query their metadata.

I hope that this article sheds some light on the importance of Dapps to move away from networkId and start using chainId instead. As new standards are created like `eth_signTypedData` (EIP-712) which rely on this variable, we should look into improving our best practices and start thinking about Dapps not just as Ethereum apps but as multi-EVM apps.

--

--