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
npm install ethers
# or
yarn add ethers
# or
pnpm add ethersGetting Started
Connecting to Ethereum
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
// 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
// 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
const address = await wallet.getAddress();
const privateKey = wallet.privateKey;
const mnemonic = wallet.mnemonic.phrase;Signing Transactions
const tx = {
to: '0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb',
value: ethers.parseEther('1.0')
};
const sentTx = await wallet.sendTransaction(tx);
console.log(sentTx.hash);Signing Messages
// 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
// Get balance
const balance = await provider.getBalance('0x...');
// Format balance
const balanceInEth = ethers.formatEther(balance);
console.log(balanceInEth);Get Transaction
// 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
// 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
// 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
// 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
// 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
const abi = [...];
const address = '0x...';
const contract = new ethers.Contract(address, abi, provider);Read Functions
// 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
// 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
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
// 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
// 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
// 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
// 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
// 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
// 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
// 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.
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
// 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
const provider = new ethers.JsonRpcProvider('https://eth-mainnet.g.alchemy.com/v2/YOUR-KEY');WebSocket Provider
const provider = new ethers.WebSocketProvider('wss://eth-mainnet.g.alchemy.com/v2/YOUR-KEY');Browser Provider
const provider = new ethers.BrowserProvider(window.ethereum);Fallback Provider
const provider = new ethers.FallbackProvider([
new ethers.JsonRpcProvider(url1),
new ethers.JsonRpcProvider(url2),
new ethers.JsonRpcProvider(url3)
]);Error Handling
Handle errors gracefully.
Transaction Errors
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
- 1Always use async/await
- 2Handle errors properly
- 3Use BigNumber for large numbers
- 4Validate addresses before transactions
- 5Use appropriate gas strategies
- 6Listen to transaction events
- 7Use TypeScript for type safety
- 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.