ReactJS and Metamask integration

By Driblinho | DevAndTrade | 18 May 2021


The last time I start learning some react and metamask integration for my side project. After that, I prepare a basic example of this integration.
The main functionality of this example is:

  • The connecting site to metamask 
  • Detect network 
  • Detect account and network change

In further parts we will need some react library:

To install these dependencies use the command:

yarn add web3 @metamask/detect-provider bootstrap react-bootstrap  

In this tutorial, I want to prepare a simple react js application integrate with a cryptocurrency wallet. To create an application a use ReactJS and react-bootstrap.

All tutorial parts will be stored on GitHub repository: @Driblinho/0x-react-tutorial.
To create an application I use facebook/create-react-app starter

npx create-react-app 0x-react-tutorial cd 0x-react-tutorial

In further parts of the tutorial, I try to explain the functioning of the application.

 

Part 1 - Prepare a simple template for an application.

To check locally working application you can clone it from the repository and go to branch part-1-template:

git clone https://github.com/Driblinho/0x-react-tutorial
git checkout --track origin/part-1-template
yarn install​

To run app execute:

yarn start

In this part, I create a simple layout using a bootstrap component and some basic functionality. The layout you can check in src/App.js. Functionality create at this moment is SignIn and SignOut method bind to navbar buttons and changing isLogged state. Another component that is useful in the next part of the tutorial is Alert for users displayed in the right corner. I create a Message component using the Alert component from react-bootstrap:

<Alert variant={props.variant ? props.variant : 'dark'} onClose={close} dismissible>
  <Alert.Heading>{props.head}</Alert.Heading>
    <p>
      {props.body}
    </p>
</Alert>

For store message I create state :

 const [messages, setMessage] = useState([{...}])

In this state, I store objects with message and color variant of message.

{
    head : "User Rejected Request", 
    body: 'Please connect to MetaMask.', 
    variant: 'info'
}

Inside App template, all message is displayed on top of the layout using simple CSS code.

<div className="message-list" >
    {
        messages.map((item,i) => (
            <Message head={item.head} body={item.body} variant={item.variant} id={i} key={i} />
        ))
    }
</div>

You can see how the app work at that point of tutorial on the gif below:   2e4a5a3388c428aa563271146e1c9b77927ee35dbb5d11531f457da98b4d6bf5.gif  

Part 2 - Integrate Metamask witch application.

In this part of the tutorial, I prepare integration with metamask. The main functionality in this part are:

  • Connect app witch metamask
  • Detect network change
  • Detect account change

To check the app from part two you must change branch and run the application:  

git checkout --track origin/part-2-metamask
yay start

 

Part 2.1 Metamask connect

Before start interact with the metamask app check that has access to the provider by using a detector from @metamask/detect-provider

const provider = await detectEthereumProvider()

by checking provider variable inside if statement can detect metamask. To connect metamask with the app and get the user address I execute method ConnectWallet() inside SignIn when method return address was displayed message for the user.  

const SignIn = async () => {
      //Detect Provider
      const provider = await detectEthereumProvider()
      const web3 = new Web3(provider)

      if(!provider) {

        setMessage(messages => [...messages, {head : "Wallet not found", body: `Please install MetaMask!`, variant: 'warning'}])

      } else {

        const address = await ConnectWallet()
        if (address)
          setMessage(messages =>[...messages, {head : "User Login", body: `addres: ${address}`, variant: 'success'}])

      }
      
  }

All connect logic is inside the below method:

const ConnectWallet = async () => {

    console.log("Try Connect");

    try {
      await window.ethereum.enable();

      const id = await window.ethereum.request({ method: 'eth_chainId' })
      setCurrentChainID(() => parseInt(id, 16))

      const accounts = await window.ethereum.request({ method: 'eth_requestAccounts' })
      setIsLogged(true)
      setCurrentAccount(accounts[0])
      return accounts[0]

    } catch(err) {
        if (err.code === 4001) {
          // EIP-1193 userRejectedRequest error
          // If this happens, the user rejected the connection request.
          console.log('Please connect to MetaMask.')
          setMessage(messages =>[...messages, {head : "User Rejected Request", body: 'Please connect to MetaMask.', variant: 'info'}])

        } else if(err.code === -32002) {
          console.log('Please unlock MetaMask.')
          setMessage(messages =>[...messages, {head : "User Request Pending", body: 'Please unlock MetaMask and try agin.', variant: 'info'}])
        } else {
          console.error(err);
          setMessage(messages =>[...messages, {head : "Error", body: err.message, variant: 'info'}])
        }

    }
    
  }

 

MetaMask injects a global API into websites visited by its users at window.ethereum. This API allows websites to request users' Ethereum accounts, read data from blockchains the user is connected to, and suggest that the user sign messages and transactions. Ethereum Provider API

   

ConnectWallet method use injected window.ethereum inside try...catch statement to enable wallet next using ethereum.request and methods eth_chainId,eth_requestAccounts application save in state chainID and user account address. Application inside catch has simple error management. App display custom message for error code 4001 and -32002 otherwise presents standard error message.

  • 4001 - User rejected the connection request
  • -32002 - Metamask was locked by password

 

Part 2.2 Detect network change

In this section, I describe how applications manage used networks. 2a39ea1e55f33d870b02d6924fb04e3f825eae3c06900af33c7b25ad03f3fd71.gif For represent, chain in-app was created component Chain.

const Chain = (props) => {

    const chainId = props.chainId

    let chainLogo
    let variant
    let chainName

    switch (chainId) {
      case 1: //ETH
          chainLogo = ChainLogo.eth
          variant = "light"
          chainName = "Ethereum Network"
        break;
      case 56: //BNB
          chainLogo = ChainLogo.bnb
          variant = "secondary"
          chainName = "Binance Smart Chain"
        break;
      case 128: //HT
          chainLogo = ChainLogo.ht
          variant = "light"
          chainName = "Heco"
        break;
      case 100: //xDai
          chainLogo = ChainLogo.xdai
          variant = "light"  
          chainName = "xDai Stable Chain"
        break;
      case 137: //Polygon
          chainLogo = ChainLogo.polygon
          variant = "light"
          chainName = "Polygon Network"
        break;
      default: // Unknown network
          chainLogo = ChainLogo.unknown
          variant = "light"
          chainName = "Unknown network?"
        break;
    }

    return(
      <OverlayTrigger
        key="left"
        placement="left"
        overlay={
          <Tooltip id={`tooltip-left`}>
            {chainName}
        </Tooltip>
        }
      >
        <Button variant={variant} >
          <img class="lazyload" data-src={chainLogo} width={14} alt={chainName} />
        </Button>
      </OverlayTrigger>
    )
  }


A component represents networks by id:

  • ETH - 1
  • BNB - 56
  • HT(Heco) - 128
  • xDai - 100
  • Polygon - 137

To detect chain is used event chainChanged inside react hook useEffect.

useEffect(() => {
    ...
    window.ethereum.on('chainChanged', (_chainId) => {
      console.log(_chainId);
      setCurrentChainID(() => parseInt(_chainId, 16))
    });

  }, []);

When the event was fired update currentChainID state witch was used by Chain component to display the proper chain logo. 119db249ec66d1775fc878b4f92865a5c421e57a2de994ed420e9ce01a10dd03.png

Below gif example of a working application:

550dc23183e400d51b08d1d26eedf0dee858cf6fdffec9c86456f6ade67b7be2.gif

Future

In the future part of the tutorial, I plan to present some backend integration with metamask.

Ty for reading.

Referrals: 

Instant cross-chain crypto swaps ETH, BSC, HECO
Trade 24/7 Stocks, Crypto, and Forex on Morpher chain - Free 50 MPH
Maiar EGLD, BNB, ETH Wallet
CoinList - pre-sales auctions
Ern free CHSB - SwissBorg
Participate in Bifrost vsKSM #Mintdrop and win a huge $BNC airdrop.

   

 

 

How do you rate this article?


3

1


DevAndTrade
DevAndTrade

Post about crypto and programing

Send a $0.01 microtip in crypto to the author, and earn yourself as you read!

20% to author / 80% to me.
We pay the tips from our rewards pool.