The Blockchain has brought us the promise of a decentralized world, where intermediaries can be eliminated, and transactions can be conducted in a trustless manner. Ethereum, in particular, has become the world's largest blockchain ecosystem, with a vast number of decentralized applications being built on top of it. But if you’re a website or decentralized app developer, you have to adjust and figure out how to interact with the blockchain. Enter Ethers - an open-source library that simplifies the process of interacting with the Ethereum blockchain, or in the words of its creators - “a simple, compact and complete library for all your Ethereum needs”
In this blog post, we will take a look at the problem that Ethers solves, and how it enables developers to create decentralized applications (dapps) that run on Ethereum.
History of Ethers
Ethers was created by a developer named Richard Moore in 2016, and to this day, Richard Moore still maintains the library. Previously, the most widely used Ethereum library was web3.js. However, it has a larger bundle size of 1.4MB compared to Ethers’ 400KB, and is arguably less user-friendly for reasons that will be discussed in more detail below.
Understanding the problem that Ethers solves
Ethers can be tricky to understand because the Web 3.0 tech stack is subtly different from the typical Web 2.0 tech stack. In Web 2.0, websites (almost) always host data in private servers and databases. To perform common operations like fetching information or authenticating a user, a website (the client) must call a website’s custom backend. In Web 3.0, important data is likely to exist on a public blockchain (like Ethereum), where data can be read by anyone that can query one of the blockchain’s nodes.
At first glance, it may seem that fetching data from a public and open source might be easier than fetching data from a private server. After all, there are no authentication requirements if the data is public and the ways to query the data are standardized. However, while the interface for querying Ethereum nodes is standardized (nodes expose a JSON-RPC API), there is no standard SDK to execute these queries. As a developer, doing anything from reading the balance of an Ethereum address to transferring ERC20 tokens requires writing code that ultimately calls these JSON-RPC endpoints, but the Ethereum Foundation does not maintain code that lets you make these calls ergonomically in the language of your choice.
Let’s take the `eth_sendTransaction` endpoint, which is used to send and hopefully execute an Ethereum transaction. The example parameters for this call in the Ethereum documentation are the following:
Using Ethereum’s JSON-RPC API directly would be extremely laborious because of the need to form these parameters correctly and also send the request to the correct endpoint.
A final point is that writes are significantly harder because writing to the Ethereum blockchain requires the signing of messages using wallet private keys. Cryptography of this type is unwieldy to build yourself.
The Solution: Ethers
Enter Ethers (alternatively named Ethers.js). Ethers simplifies Ethereum development by enabling developers to interact ergonomically with Ethereum nodes.
Let’s walk through some common use cases. To motivate the use cases, our goal will be to write client-side code that sends 0.1 ETH to a user of our website and then prompts the user to mint an NFT from a collection called “GalaxyEraNFT”.
Ethers.js is an NPM package. To install, in your project directory, run:
Or `yarn add ethers` if you prefer `yarn`.
To import, add the following to your imports:
Connecting to an Ethereum node
We want to send the user some ETH, but first, we need to get access to an Ethereum node. Recall that Ethereum nodes are not very easy to run yourself (they require several terabytes of storage and a stable network connection). As a developer, you will likely use a service such as Alchemy to get easy access to a performant Ethereum node. (Note that Alchemy’s SDK is a superset of Ethers.js. It offers some additional APIs, like an API to fetch and display NFTs).
Ethers’s approach to interacting with the blockchain involves two distinct entities, a wallet, and a provider.
- Wallet - responsible for signing transactions using a private key
- Provider - connects to the blockchain network, verifies the current state, and sends transactions.
By separating the wallet and provider, developers have more flexibility. For example, they can use Alchemy as a provider and Metamask as a wallet.
Sending ETH to the user
Sending ETH to the user will require you to use a wallet in your possession. To do so, let’s instantiate a wallet and connect it to `provider`. Afterwards, we will be able to send ETH to the user.
Note that Ethers.js enables you to easily instantiate a wallet and connect it to a `provider` so that the wallet can interact with an Ethereum node. Furthermore, there are methods on `wallet` to ergonomically send ETH.
Connecting to the user’s wallet
So far, our code has all been in a dapp’s backend services, where we used a secret funding wallet’s private key, an Ethereum node, and knowledge of a user’s wallet address, to generously fund the user’s wallet. Now, we must turn our attention to our dapp’s client, where the user will connect their wallet. (In this example, we use Metamask, but read below on how Dynamic can help power multiple wallet)
Note that we connected to `window.ethereum`, which is where wallets such as Metamask are stored in the browser.
Also note that chronologically, we would not have known the user’s wallet address until this step. However, for the sake of a better explanation of the concept of an Ethers.js `Provider`, we assumed we knew the address in the previous section.
The last note - promise! If you are curious how Metamask is able to function as a `provider`, it is because Metamask is connected to an Ethereum node that it can use to interact with the Ethereum blockchain - after all, that is the basic functionality of a wallet.
Minting the NFT
Our user’s wallet is now connected to our dapp and funded their own wallet. It is now time to prompt the user to mint an NFT. Let’s use this very simple NFT contract below.
First, we will instantiate the contract, assuming we have generated and imported its application binary interface (ABI) ahead of time.
Next, we will prompt the user to mint the NFT by attempting to call the `mint` function, which will launch a Metamask transaction.
The user will see a Metamask screen pop up to approve the transaction and mint an NFT.
Tada! We have gone through the full process of sending a user ETH using our own wallet, connecting the user’s wallet, and then prompting the user to mint a simple NFT. In doing so, we have shown much of Ethers.js’ core functionality.
We’re done with our example, and while we’ve covered most of Ethers.js’ main functionalities, Ethers carry a lot more in store.
- Ergonomic support of popular node providers such as Alchemy
- Lightweight logging functionality for telemetry
- A command line interface to interact with Ethereum from the shell
To explore Ethers.js in more details, visit the extensive documentation.
Ethers.js vs web3.js
As previously mentioned, in Ethers.js, there is a separate concept of a `Provider`, which is used for all interaction with Ethereum nodes, and `Signer` (such as `Wallet`) which is used whenever a private key must be used to sign messages. The primary distinction between Ethers.js and Web3.js is that in Web3.js, all methods are called from a single object `web3`, whether you are trying to simply query Ethereum or sign messages. The pattern that Ethers.js uses lends itself to better security because of the clearer separation of concerns.
Recent npm trends also show Ethers.js gaining momentum over web3.js.
Expanding Ethers.js's React support with Wagmi
While Ethers.js is a powerful library for interacting with the Ethereum network, it can still be verbose and cumbersome for certain tasks. wagmi.sh is an open-source library that provides an even higher-level abstraction for common tasks in Ethereum development. In particular, it is a collection of React hooks that simplifies the process of building and deploying Ethereum-based applications. These hooks enable developers to interact with Ethereum smart contracts, handle transactions, and manage user accounts, all without the need for extensive boilerplate code. wagmi uses Ethers.js under the hood to offer a more developer-friendly interface
- Interacting with smart contracts: wagmi.sh allows developers to read and write data from Ethereum smart contracts with minimal hassle.
- Managing user accounts and authentication: Developers can easily handle user account creation, balance checking, and authentication using WAGMI.sh hooks.
- Handling transactions: WAGMI.sh simplifies the process of creating, signing, and sending transactions on the Ethereum network.
Example: reading data from a smart contract:
Example 2: Managing user accounts and authentication:
A new contender called Viem
While a huge stride forward, ethers.js as well as web3.js, have their own set of flaws, such as developer experience, stability, bundle size, and performance, and those have not gone unnoticed. In early 2023, the creators of wagmi announced an alternative to ethers.js - Viem, focused on reliability, efficiency, and excellent developer experience. Still early in its development, Viem holds the promise for a true alternative to ethers.js.
Extending support beyond Metamask with Dynamic
Ethers.js opens the door to interacting with EVM networks, but does not on its own assist in interacting with the myriad of wallets available - whether they are injected, mobile, embedded or others.
Dynamic, a powerful web3 auth developer platform, sets to tackle exactly that - abstracting away wallet-based auth from developers using ethers with a flexible developer SDK. Dynamic offers smart and beautiful login flows for crypto-native users, simple onboarding flows for everyone else, and powerful developer tools that go beyond authentication. If you’re using ethers.js, we recommend exploring Dynamic - you can get started here. (btw - you can also use Dynamic together with wagmi using a simple connector).