Skip to content
All essays
WebMarch 21, 202414 min

Ethers.js Development Guide

Master Ethers.js for modern Ethereum and Web3 development

Ü
Ümit Uz
Mobile & Full Stack Developer

Ethers.js is a modern, lightweight JavaScript library for interacting with the Ethereum blockchain. It's designed to be compact and complete, making it an excellent choice for Web3 development.

Why Ethers.js?

Ethers.js offers several advantages over alternatives:

  • Smaller bundle size
  • More modern API design
  • Better TypeScript support
  • Simpler key management
  • Built-in ENS support
  • Extensive documentation

Installation

bash
npm install ethers
# or
yarn add ethers
# or
pnpm add ethers

Getting Started

Connecting to Ethereum

javascript
import { ethers } from 'ethers';

// Connect to provider
const provider = new ethers.JsonRpcProvider('https://eth-mainnet.g.alchemy.com/v2/YOUR-API-KEY');

// Connect to MetaMask
const provider = new ethers.BrowserProvider(window.ethereum);

// Connect to local node
const provider = new ethers.JsonRpcProvider('http://localhost:8545');

Network Information

javascript
// Get network
const network = await provider.getNetwork();
console.log(network.chainId); // 1 for Mainnet
console.log(network.name); // 'mainnet'

// Get block number
const blockNumber = await provider.getBlockNumber();

// Get gas price
const feeData = await provider.getFeeData();
console.log(feeData.gasPrice);

Wallets and Signers

Manage accounts and sign transactions.

Creating Wallets

javascript
// Create random wallet
const wallet = ethers.Wallet.createRandom();

// From private key
const wallet = new ethers.Wallet('0x...');

// From mnemonic
const wallet = ethers.Wallet.fromPhrase('twelve words here');

// With provider
const wallet = new ethers.Wallet('0x...', provider);

Wallet Information

javascript
const address = await wallet.getAddress();
const privateKey = wallet.privateKey;
const mnemonic = wallet.mnemonic.phrase;

Signing Transactions

javascript
const tx = {
    to: '0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb',
    value: ethers.parseEther('1.0')
};

const sentTx = await wallet.sendTransaction(tx);
console.log(sentTx.hash);

Signing Messages

javascript
// Sign message
const signature = await wallet.signMessage('Hello World');

// Sign typed data (EIP-712)
const domain = {
    name: 'MyApp',
    version: '1',
    chainId: 1,
    verifyingContract: '0x...'
};

const types = {
    Person: [
        { name: 'name', type: 'string' },
        { name: 'wallet', type: 'address' }
    ]
};

const value = {
    name: 'Alice',
    wallet: '0x...'
};

const signature = await wallet.signTypedData(domain, types, value);

Reading Blockchain Data

Query the blockchain efficiently.

Get Balance

javascript
// Get balance
const balance = await provider.getBalance('0x...');

// Format balance
const balanceInEth = ethers.formatEther(balance);
console.log(balanceInEth);

Get Transaction

javascript
// Get transaction by hash
const tx = await provider.getTransaction('0x...');

// Get transaction receipt
const receipt = await provider.getTransactionReceipt('0x...');

// Wait for transaction
const receipt = await provider.waitForTransaction('0x...');

Get Block

javascript
// Get latest block
const block = await provider.getBlock('latest');

// Get block with transactions
const block = await provider.getBlock('latest', true);

// Get block by number
const block = await provider.getBlock(12345678);

Query Logs

javascript
// Get logs
const logs = await provider.getLogs({
    address: '0x...',
    fromBlock: 0,
    toBlock: 'latest',
    topics: ['0x...']
});

// Filter logs
const filter = {
    address: '0x...',
    topics: [
        ethers.id('Transfer(address,address,uint256)')
    ]
};

const logs = await provider.getLogs(filter);

Unit Conversion

Ethers.js makes unit conversion easy.

Parsing Units

javascript
// Parse Ether
const amount = ethers.parseEther('1.0');
console.log(amount); // BigNumber

// Parse Wei
const amount = ethers.parseUnits('1000000000000000000', 'wei');

// Parse Gwei
const amount = ethers.parseUnits('1.5', 'gwei');

Formatting Units

javascript
// Format to Ether
const eth = ethers.formatEther(bigNumber);

// Format to specific units
const gwei = ethers.formatUnits(amount, 'gwei');

// Format to Wei
const wei = ethers.formatUnits(amount, 'wei');

Smart Contracts

Deploy and interact with smart contracts.

Contract Instance

javascript
const abi = [...];
const address = '0x...';

const contract = new ethers.Contract(address, abi, provider);

Read Functions

javascript
// Call view function
const value = await contract.balanceOf('0x...');

// Call with parameters
const result = await contract.transfer('0x...', 100);

// Call multiple
const [balance, owner] = await Promise.all([
    contract.balanceOf('0x...'),
    contract.owner()
]);

Write Functions

javascript
// Connect wallet
const contract = new ethers.Contract(address, abi, wallet);

// Send transaction
const tx = await contract.transfer('0x...', 100);

// Wait for confirmation
const receipt = await tx.wait();

// With options
const tx = await contract.transfer('0x...', 100, {
    gasLimit: 100000,
    value: ethers.parseEther('1.0')
});

Deploy Contract

javascript
const abi = [...];
const bytecode = '0x...';

const factory = new ethers.ContractFactory(abi, bytecode, wallet);

// Deploy with constructor arguments
const contract = await factory.deploy('arg1', 'arg2');

// Wait for deployment
await contract.waitForDeployment();

const address = await contract.getAddress();

Contract Events

javascript
// Query past events
const filter = contract.filters.Transfer('0x...', null);
const events = await contract.queryFilter(filter);

// Listen to events
contract.on('Transfer', (from, to, value, event) => {
    console.log(from, to, value);
});

// Listen once
contract.once('Transfer', (from, to, value) => {
    console.log('Transfer occurred');
});

// Remove listener
contract.off('Transfer', listener);

Event Filters

javascript
// All Transfer events
const filter = contract.filters.Transfer();

// Filter by from address
const filter = contract.filters.Transfer('0x...');

// Filter by to address
const filter = contract.filters.Transfer(null, '0x...');

// Filter by indexed parameters
const filter = contract.filters.Transfer(
    '0x...', // from
    '0x...'  // to
);

Utilities

Address Utilities

javascript
// Validate address
const isValid = ethers.isAddress('0x...');

// Get address from private key
const address = ethers.getAddress('0x...');

// Compute address
const address = ethers.getCreateAddress({ from: '0x...', nonce: 0 });

// Compute contract address from create2
const address = ethers.getCreate2Address(
    from,
    salt,
    initCodeHash
);

Hash Functions

javascript
// Keccak256
const hash = ethers.keccak256('0x...');

// Solidity keccak256
const hash = ethers.solidityPackedKeccak256(
    ['uint256', 'string'],
    [123, 'hello']
);

// SHA256
const hash = ethers.sha256('0x...');

// RIPEMD160
const hash = ethers.ripemd160('0x...');

ABI Encoding

javascript
// Encode parameters
const encoded = ethers.AbiCoder.defaultAbiCoder().encode(
    ['uint256', 'string'],
    [123, 'hello']
);

// Decode parameters
const decoded = ethers.AbiCoder.defaultAbiCoder().decode(
    ['uint256', 'string'],
    encoded
);

// Encode function call
const iface = new ethers.Interface(abi);
const callData = iface.encodeFunctionData('transfer', [
    '0x...',
    100
]);

// Decode function result
const decoded = iface.decodeFunctionResult(
    'transfer',
    returnData
);

Gas and Fees

Manage gas costs effectively.

Estimate Gas

javascript
// Estimate gas for transaction
const gasEstimate = await provider.estimateGas({
    to: '0x...',
    value: ethers.parseEther('1.0')
});

// Estimate gas for contract call
const gasEstimate = await contract.transfer.estimateGas(
    '0x...',
    100
);

Fee Data

javascript
// Get fee data
const feeData = await provider.getFeeData();

console.log(feeData.gasPrice);
console.log(feeData.maxFeePerGas);
console.log(feeData.maxPriorityFeePerGas);

// Send with EIP-1559
const tx = await wallet.sendTransaction({
    to: '0x...',
    value: ethers.parseEther('1.0'),
    maxFeePerGas: feeData.maxFeePerGas,
    maxPriorityFeePerGas: feeData.maxPriorityFeePerGas
});

Multicall

Batch multiple calls into one.

javascript
const multicall = new ethers.Contract(
    multicallAddress,
    multicallAbi,
    provider
);

const calls = [
    [address1, iface.encodeFunctionData('balanceOf', ['0x...'])],
    [address2, iface.encodeFunctionData('balanceOf', ['0x...'])]
];

const results = await multicall.aggregate(calls);

// Decode results
const balance1 = iface.decodeFunctionResult(
    'balanceOf',
    results[1]
)[0];

ENS Support

Built-in Ethereum Name Service support.

Resolve Names

javascript
// Resolve name to address
const address = await provider.resolveName('vitalik.eth');

// Reverse lookup
const name = await provider.lookupAddress('0x...');

// Get resolver
const resolver = await provider.getResolver('vitalik.eth');

Providers

Different provider types for different use cases.

JSON-RPC Provider

javascript
const provider = new ethers.JsonRpcProvider('https://eth-mainnet.g.alchemy.com/v2/YOUR-KEY');

WebSocket Provider

javascript
const provider = new ethers.WebSocketProvider('wss://eth-mainnet.g.alchemy.com/v2/YOUR-KEY');

Browser Provider

javascript
const provider = new ethers.BrowserProvider(window.ethereum);

Fallback Provider

javascript
const provider = new ethers.FallbackProvider([
    new ethers.JsonRpcProvider(url1),
    new ethers.JsonRpcProvider(url2),
    new ethers.JsonRpcProvider(url3)
]);

Error Handling

Handle errors gracefully.

Transaction Errors

javascript
try {
    const tx = await contract.transfer('0x...', 100);
    await tx.wait();
} catch (error) {
    if (error.code === ethers.errors.CALL_EXCEPTION) {
        console.error('Call reverted');
    } else if (error.code === ethers.errors.REPLACEMENT_UNDERPRICED) {
        console.error('Replacement underpriced');
    }
}

Best Practices

  1. 1Always use async/await
  2. 2Handle errors properly
  3. 3Use BigNumber for large numbers
  4. 4Validate addresses before transactions
  5. 5Use appropriate gas strategies
  6. 6Listen to transaction events
  7. 7Use TypeScript for type safety
  8. 8Keep private keys secure

Conclusion

Ethers.js provides a modern, clean API for interacting with Ethereum. Its focus on small size, complete features, and excellent TypeScript support makes it ideal for modern Web3 development.

Master Ethers.js, and you'll have all the tools needed to build sophisticated decentralized applications. The library's design philosophy of simplicity and power makes it a joy to use for blockchain development.

Enjoyed this essay? Share it.

Related essays

Next essay
Sprint Planning Best Practices: Deliver Consistently