Connecting to and using an ethersjs provider to query a blockchain
Published
What is ethersjs?
ethersjs is a Javascript library made for interacting with Ethereum and other EVM-based blockchain like BNB chain, Polygon or Avalanche. It’s pretty full featured and can be used both in Node and in the browser.
ethers js vs web3js
You’ll often see web3js pop up as one possible library for web3 development, so let’s shortly compare ethersjs and web3js.
While they fundamentally provide the same thing, ethersjs is written in Typescript, giving you much appreciated type safety, out of the box tree-shaking for a reduced bundle size and built-in access to public nodes.
I personally recommend using ethersjs over web3js as I’ve had many bundling issues in the past when using web3js in conjunction with any framework using Vite or Rollup underneath, including Sveltekit and VueJS. I believe that type safety is something expected by many developers today and that we will keep seeing usage of ethersjs grow compared to web3js.
What is an ethersjs provider?
For ethersjs, a Provider is a read-only connection to the blockchain. This connection is then able to query the blockchain directly, allowing you to access blocks, transaction details, individual accounts or query smart contracts.
If you want to actually interact with the blockchain, for example by sending transactions, you’ll need to reach for a ethersjs signer.
Getting started with ethers
First off you’ll need to install the library using your preferred package manager:
# using npm
npm install --save ethers
# using yarn
yarn add ethers
# using pnpm
pnpm add ethers
We will be using a provider linked to the BNB network for the rest of this article. I’ve included an Ethereum RPC in comments in case you’re looking for one.
import { getDefaultProvider, JsonRpcProvider } from 'ethers';
// const ethereumRPC = 'https://eth.llamarpc.com';
const bnbRPC = 'https://bsc-dataseed1.binance.org/';
const provider = new JsonRpcProvider(bnbRPC);
How to use ethersjs with Metamask
If you are using ethersjs in the browser and if Metamask or another wallet is present in the user’s browser, you will be able to access it thanks to the global ethereum object.
import { BrowserProvider } from 'ethers';
const provider = new BrowserProvider(window.ethereum);
Note: if you are using Typescript, you can find Metamask’s types on github, here’s how to add them to your project:
# using npm
npm install --save-dev @metamask/types
# using yarn
yarn add --dev @metamask/types
# using pnpm
pnpm add -D @metamask/types
I recommend adding these types to your declaration file on the Window object so you won’t have to manually cast your variables.
A short Ethersjs tutorial
Let’s go over a few usecases for ethersjs:
Query an account’s balance
Let’s try and reach for an account’s native balance. Keep in mind that this is the native currency of the network and that it is different from most tokens you usually interact with. Those are ERC-20 smart contracts and follow a distinct flow.
const balance = await provider.getBalance('0x1ef191578335d9e8d241b27d8f32ecabc924c26a');
console.log(balance);
// prints 72171857697764604369n
If you’re new to the ecosystem, you’ll realize that the value you’ve obtained is different from what you would expect. What you’re getting is a value in wei and not in ether.
Dealing with Ethereum units
Here’s a table tracking ethereum units. You’ll most often be dealing with wei, gwei and ether. Converting between units is very much an important part of making a web3 dapp:
Name | Decimals |
---|---|
wei | 0 |
kwei | 3 |
mwei | 6 |
gwei | 9 |
szabo | 12 |
finney | 15 |
ether | 18 |
Let’s get back to our previous example and see how ethersjs lets us convert this into the expected ether unit.
import { formatEther, formatUnits } from 'ethers';
// Using the built-in formatEther
const formattedViaEther = formatEther(balance);
console.log(formattedViaEther); // prints 72.17857697764604369
// Doing it by hand
const formattedViaUnit = formatUnits(balance, 'ether');
const formattedViaDecimal = formatUnits(balance, 18);
console.log(formattedViaUnit); // prints 72.17857697764604369
console.log(formattedViaUnit == formattedViaDecimal); // prints true
Query a block
You can query a block either by specifying its height (called number in the ethersjs library) or its hash:
const blockHeight = 20000;
const blockViaHeight = await provider.getBlock(blockHeight);
const blockHash = '0x396fa6fdb10d5c3c0e93743020f36ea1f5a542b20d8ed8b08f51a99a8d7efc93';
const blockViaHash = await provider.getBlock(blockHash);
console.log(`Are these the same blocks: ${blockViaHeight.number === blockViaHash.number}`);
Or if you want to get the latest block:
const lastBlockHeight = await provider.getBlockNumber();
const lastBlock = await provider.getBlock(lastBlockHeight);
You can also get blocks starting from the latest one using negative numbers:
const lastBlockHeight = await provider.getBlockNumber();
const beforeLastBlock = await provider.getBlock(-1);
console.log(lastBlockHeight - beforeLastBlock.number === 1);
Query a transaction
Much like how we’ve been able to query blocks using their hash, we’re also able to query transactions using their own hashes:
const transaction = await provider.getTransaction(
'0x5cf8156e1849934dbb4b701ae6b9f5e2f509a554020d1d4f9e330998ff83dbf0'
);
console.dir(transaction);
console.log(transaction.to); // prints 0x55d398326f99059fF775485246999027B3197955
Ethersjs docs
The ethersjs documentation is available at the following links for the version 5 and the version 6.