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
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
.