Introduction

A simple guide on how to switch to a custom RPC when interacting with Metamask APIs.

This tutorial will show you how to to switch to Polygon Network in a Reactjs driven dapp. This guide assumes a fair familiarity with modern Reactjs and modern JavaScript.

We will:

  • Add Onboarding / Install MetaMask extension.
  • Switch to Polygon Chain programmatically.
  • Send MATIC Token from MetaMask.(will provide a separate tutorial for this).

All project code can be found here: https://github.com/naftalimurgor/polygon-metamask

Create a new application

We will use create-react-app to create a new project. To create a new React application, run:

npx create-react-app polygon

Switching from Ethereum to Polygon Chain in MetaMask

You can get obtain some MATIC Tokens here https://faucet.matic.network/

Step one: Adding MetamaskOnboarding

This is a library provided by Metamask to enable a dapp to “onboard” new users by kickstarting the installation of MetaMask browser extension using just a few lines of code. Add @metamask/onboarding dependency to your project

$ cd polygon/
$ npm install @metamask/onboarding
  1. Import MetamaskOnboarding at the top level of App.tsx:
import MetaMaskOnboarding from '@metamask/onboarding'

Ideally, we would like to have a button that when a user clicks it initiates a connection to the app if users have MetaMask installed or initiate the onboarding process: 2. Declare constants to hold various button labels during the wallet connect or installation interaction.

const ONBOARD_TEXT = 'Click here to install MetaMask!'
const CONNECT_WALLET = 'Connect Wallet'
  1. Create a ref to store an instance of MetamaskOnboarding inside component i.e App.tsx (if using TypeScript):
const App = () => {
  ....
  const onboarding = React.useRef<MetaMaskOnboarding>()
  ...
}
  1. Create a useEffect hook to create an instance of MetamaskOnboarding:
  useEffect(() => {
    if (!onboarding.current) {
      onboarding.current = new MetaMaskOnboarding()
    }
  }, [])
  1. Create another hook to check that is triggered to check if a user has MetaMask browser extension installed, if installed, “stop” the onboarding process:
  useEffect(() => {
    if (MetaMaskOnboarding.isMetaMaskInstalled()) {
      setButtonText(CONNECT_WALLET)
      onboarding.current?.stopOnboarding()
    } else {
      setButtonText(ONBOARD_TEXT)
    }
  }, [])
  1. Finally: We create an onClick function to that we will attach to a click button event:
  const onClick = async () => {
    if (MetaMaskOnboarding.isMetaMaskInstalled()) {
      try {
        let accounts = await window.ethereum.request({ method: 'eth_requestAccounts' })
        handleAccounts(accounts)
      } catch (e) {
        console.log(e)
      }
    } else {
      // opens a new tab on the browser to install extension from the web extension store
      onboarding.current?.startOnboarding()
    }
  }

Step two: Switch to an Ethereum-compatible chain

In this step, we add functionality to switch to Polygon chain using @web3-react/injected-connector Create two Objects to hold the parameters, for connecting to Mainnet or Testnet networks:

File constants.ts

// Polygon Mainnet Params
export const POLYGON_MAINNET_PARAMS = {
  chainId: '0x89', // 137
  chainName: 'Polygon Mainnet',
  nativeCurrency: {
    name: 'MATIC Token',
    symbol: 'MATIC',
    decimals: 18
  },
  rpcUrls: ['https://rpc-mainnet.matic.quiknode.pro'],
  blockExplorerUrls: ['https://polygonscan.com/']
}

// Polygon Testnet params
export const POLYGON_TESTNET_PARAMS = {
  chainId: '0x13881', // 8001
  chainName: 'Mumbai',
  nativeCurrency: {
    name: 'MATIC Token',
    symbol: 'MATIC',
    decimals: 18
  },
  rpcUrls: ['https://matic-mumbai.chainstacklabs.com/'],
  blockExplorerUrls: ['https://mumbai.polygonscan.com/']
}
  1. Add @web3-react/injected-connector dependency to the project using yarn or npm
npm install @web3-react/injected-connected
  1. Import InjectedConnector class inside App.tsx:
import { InjectedConnector } from '@web3-react/injected-connector'
  1. Declare an object with a property supportedChainIds, this property holds an array of chainIds in decimal for your network, Mainnet and Testnet:
const abstractConnectorArgs = {
  supportedChainIds: [137, 80001]
}

Instantiate InjectedConnector and pass in the above object:

// an instance of InjectedConnect for retrieving the provider once injected
const injected: InjectedConnector = new InjectedConnector(abstractConnectorArgs)

Inside app.tsx, create a function that will be attached to a button click event: In App.tsx

  async function addPolygonNetwork() {
    try {
      const provider = await injected.getProvider()
      // rpc request to switch chain to an ethereum compatible chain
      await provider.request({ method: 'wallet_addEthereumChain', params: [POLYGON_TESTNET_PARAMS] })
      
    } catch (e) {
      setFlashMsg('Failed to switch to Polygon chain, Please check your internet connect reconnect again')
      console.log(e)
    }
  }

We then use the above function inside a component like:

<Navbar
  buttonText={buttonText}
  onClick={() => onClick()}
  switchNetwork={() => addPolygonNetwork()} 
/>

Final code

import React, { useState, useEffect } from 'react'
import Web3 from 'web3'
import { InjectedConnector } from '@web3-react/injected-connector'
import MetaMaskOnboarding from '@metamask/onboarding'
import './App.css'

import { Navbar } from './components/Navbar'
import { POLYGON_TESTNET_PARAMS } from './constants'
const ONBOARD_TEXT = 'Click here to install MetaMask!'
const CONNECT_WALLET = 'Connect Wallet'

declare var window: any

const abstractConnectorArgs = {
  supportedChainIds: [137, 80001]
}
const injected: InjectedConnector = new InjectedConnector(abstractConnectorArgs)

const App = () => {
  // onboarding button text
  const [buttonText, setButtonText] = useState<string>('')

 
  const [account, setCurrentAccount] = useState<string>('')
  const onboarding = React.useRef<MetaMaskOnboarding>()
  const [flashMessage, setFlashMsg] = useState<string>('')


  // create an instance fo MetamaskOnboarding class when component mounts for the first time
  useEffect(() => {
    if (!onboarding.current) {
      onboarding.current = new MetaMaskOnboarding()
    }
  }, [])

  // check for if user has metamask extension already installed on their browser
  useEffect(() => {
    if (MetaMaskOnboarding.isMetaMaskInstalled()) {
      setButtonText(CONNECT_WALLET)
      onboarding.current?.stopOnboarding()
    } else {
      setButtonText(ONBOARD_TEXT)
    }
  }, [])



  // https://eips.ethereum.org/EIPS/eip-3085
  // Custom networks for Ethereum compatible chains can be added to Metamask
  async function addPolygonNetwork() {
    try {
      const provider = await injected.getProvider()
      // rpc request to switch chain to an ethereum compatible chain
      await provider.request({ method: 'wallet_addEthereumChain', params: [POLYGON_TESTNET_PARAMS] })

    } catch (e) {
      setFlashMsg('Failed to switch to Polygon chain, Please check your internet connect reconnect again')
      console.log(e)
    }
  }

  // connect initialize onboarding or connect wallet
  const onClick = async () => {
    if (MetaMaskOnboarding.isMetaMaskInstalled()) {
      try {

        let accounts = await window.ethereum.request({ method: 'eth_requestAccounts' })
        console.log(accounts)
      } catch (e) {
        console.log(e)
      }
    } else {
      // opens a new tab to the <chrome | firefox> store for user to install the MetaMask browser extension
      onboarding.current?.startOnboarding()
    }
  }
  
 
  return (
    <div className="container-fluid">
      <Navbar
        buttonText={buttonText}
        onClick={() => onClick()}
        switchNetwork={() => addPolygonNetwork()} />


      <div className="alert alert-warning alert-dismissible fade show" role="alert">
        <strong>Please switch to Polygon Network first before sending Matic. Switch from the <em>Menu</em></strong>
        <button type="button" className="btn-close" data-bs-dismiss="alert" aria-label="Close"></button>
      </div>

      <div className="container">
        {flashMessage && <div className="alert alert-warning alert-dismissible fade show" role="alert">
          <strong>{flashMessage}</strong>
          <button type="button" className="btn-close" data-bs-dismiss="alert" aria-label="Close"></button></div>}
      </div>

      <div className="container mb-16 mt-32">
        <div className="row">
          <div className="col-md-5 mx-auto" style={{ minHeight: '200px' }}>
            <h1 className="mx-auto">Connect Accounts:</h1>
            
          </div>
        </div>
      </div>
      <div className="container">
        <div className="row mt-16">
          <div className="col-md-5 mx-auto">
            <form onSubmit={handleSubmit}>
              <div className="form-group mb-2">
                <label htmlFor="">Paste in Recepient address</label>
                <input type="text"
                  onChange={onAdressChanged}
                  className="form-control" name="" id="" aria-describedby="helpId" placeholder="" />
              </div>
              <div className="form-group mt-4">
                <label htmlFor="">amount of Matic to send:</label>
                <input type="text"
                  onChange={onAmountChanged}
                  className="form-control" name="" id="" aria-describedby="helpId" placeholder="" />
              </div>
              <button type="submit" className="sbtn-primary" disabled={recepientAddress && amount ? false : true}>Send</button>

            </form>


          </div>
        </div>
      </div>
    </div >
  )
}

export default App

Note:

In case of any errors,

  • Try switching to another provider as the current rpc could be “busy” or takes longer to connect. Change the rpcUrls in the constants.ts to other active rpcs for the respective chainName
  • Try using Hexadecimal for chainIds instead of decimal.

Step 3: Sending MATIC(work in progress)

We’d want to be able to send MATIC Token(s) after switching to Polygon from our website/app. Stay tuned, will release a complete tutorial on how to go about in a seperate tutorial.

Further reading: https://eips.ethereum.org/EIPS/eip-3085

Fork and play around with the full example here: https://github.com/naftalimurgor/polygon-metamask