Introduction
Ethers.js is the most popular JavaScript library for interacting with the Ethereum blockchain. It enables developers to read on-chain data, call smart contracts, and send transactions in a simple and secure way. Unlike Web3.js, Ethers.js emphasizes code readability and security. In 2026, it remains essential for any modern dApp. This tutorial guides you step by step from installation to sending a transaction. You will learn how to connect a provider, read a balance, and interact with an ERC-20 contract. Each step includes complete, functional code you can copy and paste directly.
Prerequisites
- Node.js 18 or higher
- Basic knowledge of JavaScript/TypeScript
- A wallet like MetaMask with test ETH (Sepolia)
Project Installation
npm init -y
npm install ethersThis command initializes a Node.js project and installs the latest version of Ethers.js. Always verify that you are using version 6 to benefit from the latest security and performance improvements.
Connecting to the Provider
import { ethers } from 'ethers';
const provider = new ethers.JsonRpcProvider('https://rpc.sepolia.org');
const network = await provider.getNetwork();
console.log('Connecté au réseau:', network.name);This code creates a connection to an Ethereum node via JSON-RPC. The provider allows reading blockchain data without a private key. Always use a public testnet RPC for your first attempts.
Reading an Address Balance
import { ethers } from 'ethers';
const provider = new ethers.JsonRpcProvider('https://rpc.sepolia.org');
const address = '0x742d35Cc6634C0532925a3b844Bc454e4438f44e';
const balance = await provider.getBalance(address);
console.log('Solde:', ethers.formatEther(balance), 'ETH');The getBalance method returns the balance in wei. The formatEther function automatically converts it to ETH for human readability. This is the first useful operation in any dApp.
Interacting with an ERC-20 Contract
import { ethers } from 'ethers';
const provider = new ethers.JsonRpcProvider('https://rpc.sepolia.org');
const abi = ['function balanceOf(address) view returns (uint256)'];
const contract = new ethers.Contract('0x1f9840a85d5aF5bf1D1762F925BDADdC4201F984', abi, provider);
const balance = await contract.balanceOf('0x742d35Cc6634C0532925a3b844Bc454e4438f44e');
console.log('Tokens:', ethers.formatUnits(balance, 18));Only the minimal required ABI is provided. This reduces the attack surface and improves readability. The contract is read-only thanks to the provider.
Listening to a Transfer Event
import { ethers } from 'ethers';
const provider = new ethers.JsonRpcProvider('https://rpc.sepolia.org');
const abi = ['event Transfer(address indexed from, address indexed to, uint256 value)'];
const contract = new ethers.Contract('0x1f9840a85d5aF5bf1D1762F925BDADdC4201F984', abi, provider);
contract.on('Transfer', (from, to, value) => {
console.log(`Transfert de ${from} vers ${to} : ${ethers.formatUnits(value, 18)} UNI`);
});Listening to events allows reacting in real time to transfers. This pattern is fundamental for dashboards and notifications in dApps.
Best Practices
- Always use environment variables for private keys and RPC URLs
- Validate addresses with ethers.isAddress before any operation
- Handle network errors with try/catch and timeouts
- Use a single provider connection pattern throughout the application
- Prefer view functions over expensive on-chain calls
Common Errors to Avoid
- Forgetting to convert units (wei ↔ ether) with formatEther or parseEther
- Using a provider without a signer for transactions that require a wallet
- Ignoring promise rejections during network calls
- Hardcoding contract addresses without verifying the network
Going Further
Discover our complete training courses on blockchain and Web3 development at Learni Group.