Use EthersJS To Connect To MetaMask & Read From + Write To A Contract

Learn The Equivalent Window.Ethereum RPC Requests In Ethers.JS

Manny
6 min readDec 8, 2022
Use Just Ethers.JS To Interact With Your MetaMask Wallet & RPC Requests To The Blockchain

Why EthersJS?

One of the main reasons why to use Ethers.JS is because it aims to be the complete library that handles and interacts with the blockchain. It creates an abstraction and gives a bunch of other tools to make interacting with EVM-compatible chains easier.

Ethers.JS is still also a major library used with other even more absracted libraries like WAGMI.

I will be one to admit, the first time I saw the documentation from EthersJS (and I still feel this way), I felt overwhelmed. My hope with this article is to help give some more clarity to specific functions and how to use EthersJS. I also hope that the team behind EthersJS starts implementing more code examples.

https://docs.ethers.io/v5/

What We’re Building

The good news is that we’re going to be building off my previous article’s code but implementing all the functions that were originally done with window.ethereum with ethers.js.

https://github.com/codingwithmanny/vanillajs-wallet-connection

Function Equivalents

Taking a look from the previous article and the code, we’ll go through each of these functions.

Setting Up The Provider

The first thing we’ll one to do is establish a app-wide variable for our instantiated provider.

// Constants
// ========================================================
/**
* Main provider we'll be using throughout the application
*/
let ETHERSJS_PROVIDER = null;

// ...

// Initial Script Loaded On Window Loaded
// ========================================================
/**
* Init
*/
window.onload = async () => {
// ..

// Check if browser has wallet integration
if (typeof window?.ethereum !== "undefined") {
// BEFORE = window.ethereum
// Did not need to do this as window.ethereum was an Injected Provider

// AFTER = ethers.js
ETHERSJS_PROVIDER = new ethers.providers.Web3Provider(window.ethereum);

// ...
}

// ...

}

All Our Functions

We’re going to jump through each function and their equivalent in ethers.js in the context of the VanillaJS Wallet Connection code being refactored.

Connecting A Wallet

When you want to connect your wallet to the current site and add it to the the wallets list of Connected Sites.

// Constants
// ========================================================
/**
* To keep track of which wallet is connected throughout our app
*/
let WALLET_CONNECTED = '';

// ...

try {
// BEFORE - window.ethereum
// const accounts = await window.ethereum.request({ method: 'eth_requestAccounts' });

// AFTER = ethers.js
const accounts = await ETHERSJS_PROVIDER.send("eth_requestAccounts");

WALLET_CONNECTED = accounts[0];

// ...
} catch (error) {
// ...
}

// ..

Retrieve Network Chain ID

Get the current network chain ID of the current connected wallet via its injected provider, which is window.ethereum.


// ...

try {
// BEFORE - window.ethereum
// const chainId = await ethereum.request({ method: 'eth_chainId' });

// AFTER = ethers.js
// const chainId = await ETHERSJS_PROVIDER.send('eth_chainId');
// OR
const { chainId } = await ETHERSJS_PROVIDER.getNetwork();
onChainChanged(chainId);

// ...
} catch (error) {
// ...
}

// ..

Retrieve Wallet Permissions

If the wallet is already connected to the current site, check if there are already existing permissions that have been given from the current connected wallet.

// ...


// Check if browser has wallet integration
if (typeof window?.ethereum !== "undefined") {

// ...

// BEFORE - window.ethereum
// const hasWalletPermissions = await window.ethereum.request({ method: 'wallet_getPermissions' });

// AFTER = ethers.js
// Check if already connected with the number of permissions we have
const hasWalletPermissions = await ETHERSJS_PROVIDER.send('wallet_getPermissions');
console.log({ hasWalletPermissions });

// ...
}

// ...

Switch To Specific Network

Prompt the current connected wallet to switch to a specific chain id.

// Constants
// ========================================================

// ...

/**
* Required chain to interact with contract
*/
const CHAIN_ID_REQUIRED = 80001; //Mumbai

// ...

try {
// BEFORE - window.ethereum
// await window.ethereum.request({ method: 'wallet_switchEthereumChain', params: [{ chainId: `0x${CHAIN_ID_REQUIRED.toString(16)}` }], })

// AFTER = ethers.js
await ETHERSJS_PROVIDER.send('wallet_switchEthereumChain', [{ chainId: `0x${CHAIN_ID_REQUIRED.toString(16)}` }]);
// And you will have to get the current provider again
ETHERSJS_PROVIDER = new ethers.providers.Web3Provider(window.ethereum);
} catch (error) {
console.log({ error });
}

// ...

Contract RPC Read Request

Prompt the current wallet connected with the injected provider to perform a read request to a contract.

// ...

try {
// Setup Interface + Encode Function
const GetGreeting = CONTRACT_ABI.find(i => i.name === 'getGreeting');
const interface = new ethers.utils.Interface([GetGreeting]);
const encodedFunction = interface.encodeFunctionData(`${GetGreeting.name}`);
console.log({ encodedFunction });

// BEFORE - window.ethereum
// const result = await window.ethereum.request({
// method: 'eth_call', params: [{
// to: CONTRACT_ON_CHAINS[CHAIN_CONNECTED.id],
// data: encodedFunction
// }]
// });

// AFTER = ethers.js
// const result = await ETHERSJS_PROVIDER.send(
// 'eth_call',
// [
// {
// to: CONTRACT_ON_CHAINS[CHAIN_CONNECTED.id],
// data: encodedFunction
// }
// ]
// );
// OR
const result = await ETHERSJS_PROVIDER.call({
to: CONTRACT_ON_CHAINS[CHAIN_CONNECTED.id],
data: encodedFunction
});

// ...
} catch (error) {
console.log({ error });

// ...
}

// ...

Contract RPC Write Request

Prompt the current wallet connected with the injected provider to perform a write request to a contract with a specific string payload.

// ...

try {
// Setup Interface + Encode Function
const SetGreeting = CONTRACT_ABI.find(i => i.name === 'setGreeting');
const interface = new ethers.utils.Interface([SetGreeting]);
const encodedFunction = interface.encodeFunctionData(`${SetGreeting.name}`, [greeting]);
console.log({ encodedFunction });

// BEFORE - window.ethereum
// const result = await window.ethereum.request({
// method: 'eth_sendTransaction',
// params: [{
// from: WALLET_CONNECTED,
// to: CONTRACT_ON_CHAINS[CHAIN_CONNECTED.id],
// data: encodedFunction
// }]
// });

// AFTER = ethers.js
// const result = await ETHERSJS_PROVIDER.send(
// 'eth_sendTransaction',
// [{
// from: WALLET_CONNECTED,
// to: CONTRACT_ON_CHAINS[CHAIN_CONNECTED.id],
// data: encodedFunction
// }]
// );
// OR
const signer = await ETHERSJS_PROVIDER.getSigner();
const result = await signer.sendTransaction({
to: CONTRACT_ON_CHAINS[CHAIN_CONNECTED.id],
data: encodedFunction
});

// ...
} catch (error) {
console.log({ error });

// ...
}

// ...

Event On Network Chain ID Changed

An event hook that is trigged when the current wallet connected changes their network id within their wallet.

// ...

// Check if browser has wallet integration
if (typeof window?.ethereum !== "undefined") {

// ...

// BEFORE - window.ethereum
// window.ethereum.on('chainChanged', onChainChanged);

// AFTER = ethers.js
ETHERSJS_PROVIDER.provider.on('accountsChanged', onAccountsChanged);

// ...
}

// ...

Event On Wallet Account Changed

An event hook that is trigged when the current wallet connected changes to another wallet address that is going to be or is already connected.

// ...

// Check if browser has wallet integration
if (typeof window?.ethereum !== "undefined") {

// ...

// BEFORE - window.ethereum
// window.ethereum.on('accountsChanged', onAccountsChanged);

// AFTER = ethers.js
ETHERSJS_PROVIDER.provider.on('accountsChanged', onAccountsChanged);

// ...
}

// ...

Demo

Here is a demo of the application working.

NOTE: You’ll need MetaMask installed with your Chrome browser.

Full Code

If you want to see the full source code, check out the following repository.

Where To Go From Here?

Want to dive deeper into more RPC requests, with different networks, and even try it with a local network? Take a look at this comprehensive repository that has more wallet interactions:

I should be writing more articles on how a frontend can interact with a wallet and different EVM chains, so make sure to follow my account.

If you got value from this, please give it some love by sharing it, and please also follow me on twitter (where I’m quite active) @codingwithmanny and instagram at @codingwithmanny.

--

--

Manny

DevRel Engineer @ Berachain | Prev Polygon | Ankr & Web Application / Full Stack Developer