How to handle networks in your app

When you build an app connected to Ethereum network, you provide a list of allowed network which users can to connect to. But how do you check that they are connected to the right network ? By default the RainbowKit lib let's you configure the network like this :

const { chains, provider } = configureChains(
  [arbitrum],
  [
    alchemyProvider({ apiKey: process.env.ALCHEMY_ID as string }),
    publicProvider(),
  ]
)

const { connectors } = getDefaultWallets({
  appName: 'Connect Wallet App',
  chains,
})

And after when you switch to a different network from metamask the default ConnectButton turned red and diplays Wrong network to the user. That's great, but this doesn't prevent the user from using the app with the wrong network. We will see how to do this in this post.

We will build a really simple app that display the user AETH balance on arbitrum network, and display an error message when the user switch to the wrong network. This is the final result

User not connected screenshot User connected to the right network User connected to the wrong network

Check the network with wagmi

As always wagmi has everything we need to check the network. We will use the useNetwork hook to get the current network and check if it's supported or not.

import { useNetwork } from 'wagmi'

const { chain } = useNetwork()

if (chain?.unsupported) {
  return <p className="font-bold text-red-500">Unsupported Network</p>
}

The App structure

Like always we will use a next application with RainbowKit wagmi and ether.js. I will skip the setup part as we have seen many time in previous posts.

The application will have a layout component that will display the header and the main content. To check the network on every page without having to write the same code on every page we will use Higher Order Component (HOC) to wrap the page component. It will display an error message if the user is not connected to the right network.

Create components/Layout.tsx :

import { ConnectButton } from '@rainbow-me/rainbowkit'
import React from 'react'

const Layout = ({
  children,
}: {
  children?: React.ReactNode
}): React.ReactElement => (
  <>
    <nav className="flex items-center justify-between bg-blue-400 py-4 px-12 text-white">
      <p className="text-2xl font-bold">MyArbApp</p>
      <ConnectButton />
    </nav>
    <main className="flex flex-1 flex-col items-center justify-center space-y-2 px-12 pt-12 text-center">
      {children}
    </main>
  </>
)

export default Layout

And next create hoc/withChainControl.tsx :

import Layout from '../components/Layout'
import { useIsMounted } from '../hooks/useIsMounted'
import { useNetwork } from 'wagmi'

export function withChainControl<T extends Record<string, unknown>>(
  WrappedComponent: React.ComponentType<T>
) {
  // Try to create a nice displayName for React Dev Tools.
  const displayName =
    WrappedComponent.displayName || WrappedComponent.name || 'Component'

  // Creating the inner component. The calculated Props type here is the where the magic happens.
  const ComponentWithTheme = (
    props: Omit<T, keyof Record<string, unknown>>
  ) => {
    const mounted = useIsMounted()
    const { chain } = useNetwork()

    if (!mounted) {
      return null
    }

    // props comes afterwards so the can override the default ones.
    return (
      <Layout>
        {chain?.unsupported ? (
          <p className="font-bold text-red-500">
            Unsupported Network. Please change network.
          </p>
        ) : (
          <WrappedComponent {...(props as T)} />
        )}
      </Layout>
    )
  }

  ComponentWithTheme.displayName = `withTheme(${displayName})`

  return ComponentWithTheme
}

In the HOC we use the useIsMounted to fix the issue wagmi and SSR. And we use the useNetwork hook to get the current chain. Next it render the Layout component and display an error message if the network is not supported otherwise it render the wrapped component.

Wrap the page component

Now that we have the HOC we can use it to wrap the page component on every that we want to check the network. In this example we will wrap the pages/index.tsx component.

import { useAccount, useBalance, useNetwork } from 'wagmi'

import { ConnectButton } from '@rainbow-me/rainbowkit'
import Head from 'next/head'
import type { NextPage } from 'next'
import { withChainControl } from '../hoc/withChainControl'

const Home: NextPage = () => {
  const { isConnected, address } = useAccount()
  const { data, isFetching } = useBalance({ address })
  const { chain } = useNetwork()

  if (!isConnected) {
    return <ConnectButton />
  }

  return (
    <div>
      <Head>
        <title>Create Next App</title>
        <link rel="icon" href="/favicon.ico" />
      </Head>

      <div>
        <h1>{`Welcome ${address}! `}</h1>
        {isFetching ? (
          <p>Loading...</p>
        ) : (
          <>
            <h2>{`You have ${data?.formatted} ${data?.symbol} on ${chain?.name}`}</h2>
            <p>{`${data?.value && data.value.toNumber() > 0 ? '😃' : '🥲'}`}</p>
          </>
        )}
      </div>
    </div>
  )
}

export default withChainControl(Home)

Now if the user is connected to the wrong network he will see an error message this prevent him to use the app. You can get the code here.