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
refto store an instance of MetamaskOnboarding inside component i.e App.tsx (if using TypeScript):
const App = () => {
....
const onboarding = React.useRef<MetaMaskOnboarding>()
...
}
- Create a
useEffect hookto 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-connectordependency to the project usingyarnornpm
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
rpcUrlsin the constants.ts to other active rpcs for the respective chainName - Try using
HexadecimalforchainIdsinstead 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