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
- 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'
- 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>()
...
}
- Create a
useEffect hook
to create an instance of MetamaskOnboarding:
useEffect(() => {
if (!onboarding.current) {
onboarding.current = new MetaMaskOnboarding()
}
}, [])
- 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)
}
}, [])
- 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/']
}
- Add
@web3-react/injected-connector
dependency to the project usingyarn
ornpm
npm install @web3-react/injected-connected
- Import InjectedConnector class inside
App.tsx
:
import { InjectedConnector } from '@web3-react/injected-connector'
- 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
forchainIds
instead ofdecimal
.
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