Skip to main content

Handling metamask on web

ยท 5 min read
Hyoungsung Kim

In this post, I present how to handle Metamask on the web with interactive examples using React components. I hope it is helpful to someone.

The purposes of this post are as follows:

  • Show how to attach Metamask on the web
  • Show how to make a transaction using Metamask on the web
  • Show how to deploy smart contract(minting coin) using Metamask on the web

This post is based on this resource of Metamask docs.

For all source code of this page, please visit my github.

Dependinciesโ€‹


Connect Metamask on Webโ€‹


  • In this subchapter, we show how to Connect Metamask.

The web clients do not want to let the host store their private key of the Ethereum wallet. Namely, the host must Connect Ethereum provider for their service. The Metamask is one of the most famous Ethereum providers, so I will present how to Connect the Metamask on the web as an Ethereum provider with interactive examples.

The Metamask provides how to connect Metatmask and attach on the web. However, to fully utilize Metamask for smart contracts, I will present how to connect using Etheres.js

The goal of this subchapter is to connect Metamask and show your address. If you clicked Connect Metamask, your Metamask would request connecting to this page.

User account:
Let's begin generating `Connenct Metamask` components like the above example.

First, we need to define connect class to connect Metamask using Etheres.js

connect.ts
import { ethers } from 'ethers';

export class Connect {
private provider: ethers.providers.Web3Provider;
private signer: ethers.providers.JsonRpcSigner;

constructor(externalProvider: ethers.providers.ExternalProvider) {
this.provider = new ethers.providers.Web3Provider(externalProvider);
this.signer = this.provider.getSigner();
}

getProvider(): ethers.providers.Web3Provider {
return this.provider;
}

getSigner(): ethers.providers.JsonRpcSigner {
return this.signer
}
}

There are two variables provider and signer. The provider denotes the Ethereum provider like Metamask. If a client connects their Ethereum provider, such as Metamask, to the web, a host can get a client's information as knonwn as signer.

baseComponents.tsx
import { Connect } from '../src/connect';

declare global {
interface Window {
ethereum: any;
}
}

interface ButtonProps extends React.ButtonHTMLAttributes<HTMLButtonElement> {
display: string;
}

let connect: Connect;
let provider: ethers.providers.Web3Provider;

function ConnectAccountButton(props: ButtonProps): JSX.Element {
let {display, onClick, ...htmlButtonProps}: ButtonProps = props;

let userAccountComponent: HTMLSpanElement;
let userAccount: string;

const clickHandler = async () => {
connect = new Connect(window.ethereum);
provider = connect.getProvider();

await provider.send("eth_requestAccounts", []);
const signer = connect.getSigner();
userAccount = await signer.getAddress();
userAccountComponent = document.getElementById("userAccount") as HTMLSpanElement;
userAccountComponent.textContent = `User account: ${userAccount}`;

console.log("Account:", userAccount);
}

return (
<div>
<button {...htmlButtonProps} onClick={clickHandler}>{display}</button>
<div>
<span id="userAccount">{`User account: `}</span>
</div>
</div>
)
}

(I just defined ButtonProps to use the Button component easily in mdx file. Thus, you don't need to use it for your usage)

In above code, we need to note the clickHandler callback function. By this function, we can connect Metamask on the web.

connect = new Connect(window.ethereum);
provider = connect.getProvider();

await provider.send("eth_requestAccounts", []);

We need to initialize the Connect class first using window.ethereum. This process lets Ethers.js know which provider will be used for the Ethereum provider.

We have installed Metamask as a browser extension as dependencies, so window.ethereum denotes that Metamask will be used as Ethereum provider.

After finishing connect, we can call Ethereum JSON-RPC API and Metamask API.

Thus, by using the eth_requestAccounts, we can request permission to connect your Metamask

If the provider succeeded in calling the eth_requestAccounts API, we would see the below popup, which requests permission.

connect

After connection, we can get an address of a client using signer, which is derived by the provider.

const signer = connect.getSigner();
userAccount = await signer.getAddress();

Finally, we succeeded in connecting Metamask on the web!

Send ETH to accountโ€‹


0.0001 ETH = 5AF3107A4000 WEI

Smart contractโ€‹


  • ERC-20
  • ERC-721
  • ERC-1155

ERC-20โ€‹

  • Deploy smart contract to mint ERC20 token
  • Send ERC20 token to other account

Contractโ€‹

erc20.sol
//SPDX-License-Identifier: Unlicense
pragma solidity ^0.8.0;

import "@openzeppelin/contracts/token/ERC20/ERC20.sol";

contract Minting is ERC20 {
constructor(
uint256 totalSupply_,
string memory name_,
string memory symbol_) ERC20(name_, symbol_) {
_mint(msg.sender, totalSupply_);
}
}
Click deploy


ERC-721โ€‹

  • Deploy smart contract
  • Mint ERC721 token using metadata

Contractโ€‹

erc721.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

import "@openzeppelin/contracts/token/ERC721/extensions/ERC721URIStorage.sol";
import "@openzeppelin/contracts/utils/Counters.sol";

contract MyNFT is ERC721URIStorage {
using Counters for Counters.Counter;
Counters.Counter private _tokenIds;

constructor(string memory _name, string memory _symbol) ERC721(_name, _symbol) {}

function mintNFT(address _to, string memory _tokenURI) public returns (uint256) {
_tokenIds.increment();

uint256 newNFTId = _tokenIds.current();
_mint(_to, newNFTId);
_setTokenURI(newNFTId, _tokenURI);

emit Transfer(msg.sender, _to, newNFTId);

return newNFTId;
}

function tokenURI(uint256 tokenId) public view override (ERC721URIStorage) returns (string memory) {
return super.tokenURI(tokenId);
}

function _burn(uint256 tokenId) internal override (ERC721URIStorage) {
super._burn(tokenId);
}
}
Click deploy