'Managed to connect with Metamask in my website but I can't show the balance

I managed to connect with MetaMask via my application using the following code:

import React, { useEffect, useState } from "react";
import Web3 from "web3";
import styles from "./MetamaskAuthStyle.css";

function isMobileDevice() {
  return "ontouchstart" in window || "onmsgesturechange" in window;
}

async function connect(onConnected) {
  if (!window.ethereum) {
    alert("Get MetaMask!");
    return;
  }

  const accounts = await window.ethereum.request({
    method: "eth_requestAccounts",
  });

  onConnected(accounts[0]);
}

async function checkIfWalletIsConnected(onConnected) {
  if (window.ethereum) {
    const accounts = await window.ethereum.request({
      method: "eth_accounts",
    });

    if (accounts.length > 0) {
      const account = accounts[0];
      onConnected(account);
      return;
    }

    if (isMobileDevice()) {
      await connect(onConnected);
    }
  }
}

// async function getBalance(userAddress) {
//   console.log(web3.eth.getBalance(userAddress));
//   return web3.eth.getBalance(userAddress);
// }

export default function MetaMaskAuth({ onAddressChanged }) {
  const [userAddress, setUserAddress] = useState("");
  let web3: Web3 = new Web3();

  useEffect(() => {
    checkIfWalletIsConnected(setUserAddress);
  }, []);

  useEffect(() => {
    console.log(web3.eth.getBalance(userAddress));
    onAddressChanged(userAddress);
  }, [userAddress]);

  return userAddress ? (
    <div>
      Connected with <Address userAddress={userAddress} />
      <p>Balance: </p>
    </div>
  ) : (
    <Connect setUserAddress={setUserAddress} />
  );
}

function Connect({ setUserAddress }) {
  if (isMobileDevice()) {
    const dappUrl = "metamask-auth.ilamanov.repl.co"; // TODO enter your dapp URL. For example: https://uniswap.exchange. (don't enter the "https://")
    const metamaskAppDeepLink = "https://metamask.app.link/dapp/" + dappUrl;
    return (
      <a href={metamaskAppDeepLink}>
        <button className={styles.button}>Connect to MetaMask</button>
      </a>
    );
  }

  return (
    <button className={styles.button} onClick={() => connect(setUserAddress)}>
      Connect to MetaMask
    </button>
  );
}

function Address({ userAddress }) {
  return (
    <span className={styles.address}>
      {userAddress.substring(0, 5)}…
      {userAddress.substring(userAddress.length - 4)}
    </span>
  );
}

I'm pretty new in this domain and I want to find out how can I display the balance of the logged in user and display a disconnect button, so the user can log off. I already tried making a getBalance function but the I got the following error:

Error: Provided address is invalid, the capitalization checksum test failed, or it's an indirect IBAN address which can't be converted



Solution 1:[1]

Here is an approach on how to connect your React app to Metamask via Web3.js. The main idea is to use React hooks to wrap around the intended web3.js functions. The following shows both (1) how to get the current account's balance and (2) a balance of any specified account.

First lets create a custom React hook useWeb3.js in a separate file and put all Metamask/web3 connection logic there. This hook is as follows:

import { useState } from 'react';
import Web3 from 'web3';

function useWeb3(setIsLoading, setErrorMessage) {
  // web3 instance
  const [provider, setProvider] = useState(null);
  // active account
  const [account, setAccount] = useState('');
  // connect this function to a button click event
  const connect = async () => {
    try {
      setIsLoading(true);
      setErrorMessage('');
      // ensure Metamask is installed
      if (!window.ethereum) throw new Error('You should enable Metamask');
      // show Metamask prompt
      await window.ethereum.request({ method: 'eth_requestAccounts' });
      // connect Metamask to web3.js and get a web3 provider instance
      const web3 = new Web3(window.ethereum);
      setProvider(web3);
      // refresh account on change
      window.ethereum.on('accountsChanged', (accounts) =>
        setAccount(accounts[0]),
      );
      // retrieve Metamask accounts from web3
      const accounts = await web3.eth.getAccounts();
      setAccount(accounts[0]);
    } catch (error) {
      setErrorMessage(error.message);
    } finally {
      setIsLoading(false);
    }
  };
  // connect this function to a disconnect button
  const disconnect = () => {
    setProvider(null);
    setAccount('');
  };
  return { connect, disconnect, provider, account };
}

export default useWeb3;

Note that it's convenient to move metamask/web3 connection code into its own custom hook, for reuse in multiple components or parts of the app.

To dynamically observe balance information, we can create a useBalance() hook. Here is an example implementation which consumes the above useWeb3() hook:

import { useEffect, useState } from 'react';

function useBalance(web3, account, setLoading, setErrorMessage) {
  const [balance, setBalance] = useState('?');
  useEffect(() => {
    if (web3 && account) {
      (async () => {
        try {
          setLoading(true);
          setErrorMessage('');
          // Will convert an upper or lowercase Ethereum address to a checksum address.
          // https://web3js.readthedocs.io/en/v1.2.11/web3-utils.html#tochecksumaddress
          const correctedAccount = web3.utils.toChecksumAddress(account);
          const balanceInWei = await web3.eth.getBalance(correctedAccount);
          setBalance(
            // convert balance from wei to ether
            Number(web3.utils.fromWei(balanceInWei, 'ether')).toFixed(2),
          );
        } catch (error) {
          setErrorMessage(error.message);
          setBalance('?');
        } finally {
          setLoading(false);
        }
      })();
    }
  }, [web3, account, setLoading, setErrorMessage]);
  return balance;
}

export default useBalance;

Then we can connect to a real blockchain to see if everything works. To add connections to the RSK network, use the configuration in Metamask as described here.

Now in App.js we can put all the hooks together to retrieve balances.

Bonus example

This is another component which contains a text field where a user can paste any address from the RSK explorer and see its balance. It also makes use of the above useWeb3() hook.

import { useState } from 'react';
import useWeb3 from './hooks/useWeb3';
import useBalance from './hooks/useBalance';
import './App.css';

function App() {
  // loading status
  const [isLoading, setIsLoading] = useState(false);
  // error messages
  const [errorMessage, setErrorMessage] = useState('');
  // get active account and balance data from useWeb3 hook
  const {
    connect,
    disconnect,
    provider,
    account: activeAccount,
  } = useWeb3(setIsLoading, setErrorMessage);
  // get active account balance from useBalance hook
  const activeBalance = useBalance(
    provider,
    activeAccount,
    setIsLoading,
    setErrorMessage,
  );

  // random non-empty account from RSK explorer https://explorer.rsk.co/
  const [customAcount, setCustomAccount] = useState(
    '0xC2a41f76CaCFa933c3496977f2160944EF8c2de3',
  );
  // get balance of the custom account
  const customBalance = useBalance(
    provider,
    customAcount,
    setIsLoading,
    setErrorMessage,
  );

  return (
    <div className="App">
      {/* instantiate web3 only after a user clicks the button */}
      {/* avoid doing it automatically */}
      {!provider ? (
        <button onClick={connect}>Connect to MetaMask</button>
      ) : (
        <>
          <p>Connected with {activeAccount}</p>
          <p>My balance: {activeBalance} RBTC</p>

          {/* let a user enter any address and see its balance */}

          <p>Check RSK account:</p>
          <input
            type="text"
            value={customAcount}
            onChange={(e) => setCustomAccount(e.target.value)}
            style={{ width: '25rem' }}
          />
          <p>
            <a href={`https://explorer.rsk.co/address/${customAcount}`}>
              RSK account
            </a>
            {' balance:'}
            {customBalance} RBTC
          </p>
          <button onClick={disconnect}>Disconnect</button>
        </>
      )}
      {/* show loading and error statuses */}
      {isLoading && <p>Loading...</p>}
      {errorMessage && <p>{errorMessage}</p>}
    </div>
  );
}

export default App;

Connected with 0x1926617E081D3a670b3553FB0ba75180Bc01b215

My balance: 0.00 RBTC

Check RSK account:

0xC2a41f76CaCFa933c3496977f2160944EF8c2de3
RSK account balance:10.53 RBTC

[Disconnect]

If you would like to try out a working example, check out demo-code-snippets/web3-react-show-balance.

Solution 2:[2]

Update the default dappUrl to your project URL:

const dappUrl = "metamask-auth.ilamanov.repl.co"

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 Aleks Shenshin
Solution 2 Richard Smith