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:
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. 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.
Below gif example of a working application:
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.