Solana

Solana Beta is live. Try BoltRPC Solana endpoints free - start your trial now.

How to Connect to Ethereum with ethers.js v6: Complete Guide

Step-by-step guide to connecting to Ethereum using ethers.js v6. Covers HTTP provider, WebSocket provider, signing transactions, reading contracts and error handling.

BoltRPC
BoltRPC Team
8 min read
How to Connect to Ethereum with ethers.js v6: Complete Guide

How to Connect to Ethereum with ethers.js v6: Complete Guide

ethers.js is the most widely used JavaScript library for interacting with Ethereum and EVM-compatible blockchains. This guide covers everything you need to go from zero to a fully connected dApp using ethers.js v6, the current major version.

If you are coming from v5, the import style and some provider names have changed. This guide uses v6 throughout.


Prerequisites

  • Node.js 18+ installed
  • An RPC endpoint (HTTP and WebSocket URLs)
  • Basic JavaScript/TypeScript knowledge

Install ethers.js v6

npm install ethers

Verify you have v6:

npm list ethers
# Should show ethers@6.x.x

Connecting with an HTTP Provider

The JsonRpcProvider connects to any Ethereum node over HTTP. Use this for all read operations and transaction submission.

import { ethers } from 'ethers';

const provider = new ethers.JsonRpcProvider(
  'https://eu.endpoints.matrixed.link/rpc/ethereum?auth=YOUR_KEY'
);

// Verify connection
const blockNumber = await provider.getBlockNumber();
console.log('Current block:', blockNumber);

That’s it. You are connected. Every read operation on Ethereum now goes through this provider.

Reading a Wallet Balance

const address = '0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045'; // vitalik.eth

const balance = await provider.getBalance(address);
console.log('Balance (wei):', balance.toString());
console.log('Balance (ETH):', ethers.formatEther(balance));
// Balance (ETH): 1234.567890123456789

Getting the Current Gas Price

const feeData = await provider.getFeeData();
console.log('Max fee per gas:', ethers.formatUnits(feeData.maxFeePerGas, 'gwei'), 'gwei');
console.log('Max priority fee:', ethers.formatUnits(feeData.maxPriorityFeePerGas, 'gwei'), 'gwei');

Getting Block Data

const block = await provider.getBlock('latest');
console.log('Block number:', block.number);
console.log('Block timestamp:', new Date(block.timestamp * 1000).toISOString());
console.log('Gas used:', block.gasUsed.toString());

Reading Smart Contract State

To interact with a contract, you need the contract address and its ABI (Application Binary Interface).

// Minimal ERC-20 ABI — just the methods we need
const ERC20_ABI = [
  'function name() view returns (string)',
  'function symbol() view returns (string)',
  'function decimals() view returns (uint8)',
  'function totalSupply() view returns (uint256)',
  'function balanceOf(address owner) view returns (uint256)',
];

const USDC_ADDRESS = '0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48';

const usdcContract = new ethers.Contract(USDC_ADDRESS, ERC20_ABI, provider);

// Read contract state — these are eth_call under the hood
const name = await usdcContract.name();         // "USD Coin"
const symbol = await usdcContract.symbol();     // "USDC"
const decimals = await usdcContract.decimals(); // 6

const balance = await usdcContract.balanceOf('0xSomeAddress');
console.log('USDC balance:', ethers.formatUnits(balance, decimals));

Setting Up a Signer to Send Transactions

A provider is read-only. To sign and send transactions, you need a Signer. There are two common approaches:

Option 1: Private Key Signer (backend / scripts)

const PRIVATE_KEY = process.env.PRIVATE_KEY; // never hardcode this

const wallet = new ethers.Wallet(PRIVATE_KEY, provider);

console.log('Wallet address:', wallet.address);
console.log('Balance:', ethers.formatEther(await wallet.provider.getBalance(wallet.address)));

Option 2: Browser Wallet (MetaMask / frontend)

// In a browser environment
const browserProvider = new ethers.BrowserProvider(window.ethereum);

// Request wallet connection
await browserProvider.send('eth_requestAccounts', []);

const signer = await browserProvider.getSigner();
console.log('Connected address:', signer.address);

Sending ETH

const tx = await wallet.sendTransaction({
  to: '0xRecipientAddress',
  value: ethers.parseEther('0.01'), // 0.01 ETH
});

console.log('Transaction hash:', tx.hash);

// Wait for confirmation
const receipt = await tx.wait();
console.log('Confirmed in block:', receipt.blockNumber);
console.log('Gas used:', receipt.gasUsed.toString());
console.log('Status:', receipt.status === 1 ? 'Success' : 'Failed');

Writing to a Smart Contract

const ERC20_WRITE_ABI = [
  'function transfer(address to, uint256 amount) returns (bool)',
  'function approve(address spender, uint256 amount) returns (bool)',
];

const tokenContract = new ethers.Contract(TOKEN_ADDRESS, ERC20_WRITE_ABI, wallet);

// Send tokens
const amount = ethers.parseUnits('100', 6); // 100 USDC (6 decimals)

const tx = await tokenContract.transfer('0xRecipientAddress', amount);
console.log('TX hash:', tx.hash);

const receipt = await tx.wait();
console.log('Transfer confirmed in block:', receipt.blockNumber);

Estimating Gas Before Sending

// Estimate gas before sending — avoids out-of-gas failures
const gasEstimate = await tokenContract.transfer.estimateGas(
  '0xRecipientAddress',
  amount
);

// Add 20% buffer
const gasLimit = gasEstimate * 120n / 100n;

const tx = await tokenContract.transfer('0xRecipientAddress', amount, {
  gasLimit
});

Connecting to Multiple Chains

Same pattern, different endpoint URL:

const providers = {
  ethereum: new ethers.JsonRpcProvider(
    'https://eu.endpoints.matrixed.link/rpc/ethereum?auth=YOUR_KEY'
  ),
  arbitrum: new ethers.JsonRpcProvider(
    'https://eu.endpoints.matrixed.link/rpc/arbitrum?auth=YOUR_KEY'
  ),
  base: new ethers.JsonRpcProvider(
    'https://eu.endpoints.matrixed.link/rpc/base?auth=YOUR_KEY'
  ),
  polygon: new ethers.JsonRpcProvider(
    'https://eu.endpoints.matrixed.link/rpc/polygon?auth=YOUR_KEY'
  ),
};

// Read the same contract address on different chains
async function getBalanceOnAllChains(tokenAddress, abi, walletAddress) {
  const results = await Promise.all(
    Object.entries(providers).map(async ([chain, provider]) => {
      const contract = new ethers.Contract(tokenAddress, abi, provider);
      const balance = await contract.balanceOf(walletAddress);
      return { chain, balance };
    })
  );
  return results;
}

Adding a Fallback Provider

For production applications that need high availability:

const primary = new ethers.JsonRpcProvider(
  'https://eu.endpoints.matrixed.link/rpc/ethereum?auth=YOUR_KEY'
);

const fallback = new ethers.JsonRpcProvider(
  'https://your-fallback-rpc-url'
);

// Tries primary first, falls back automatically
const provider = new ethers.FallbackProvider([
  { provider: primary, priority: 1, weight: 2 },
  { provider: fallback, priority: 2, weight: 1 },
]);

Connecting with WebSocket

Use WebSocketProvider when you need real-time subscriptions: new block notifications, contract event streams, pending transaction monitoring.

const wsProvider = new ethers.WebSocketProvider(
  'wss://eu.endpoints.matrixed.link/ws/ethereum?auth=YOUR_KEY'
);

// Listen for new blocks
wsProvider.on('block', (blockNumber) => {
  console.log('New block:', blockNumber);
});

// Listen for contract events
const contract = new ethers.Contract(CONTRACT_ADDRESS, ABI, wsProvider);

contract.on('Transfer', (from, to, value, event) => {
  console.log(`Transfer: ${from} → ${to}: ${ethers.formatEther(value)} ETH`);
});

For a complete WebSocket guide with reconnection handling, see: How to Listen for Ethereum Events with WebSocket


Error Handling

Always wrap RPC calls in try/catch:

async function safeGetBalance(address) {
  try {
    const balance = await provider.getBalance(address);
    return ethers.formatEther(balance);
  } catch (error) {
    if (error.code === 'NETWORK_ERROR') {
      console.error('RPC connection failed');
    } else if (error.code === 'CALL_EXCEPTION') {
      console.error('Contract call reverted');
    } else {
      console.error('Unexpected error:', error.message);
    }
    return null;
  }
}

Transaction Simulation Before Sending

Use eth_call to simulate state-changing transactions before broadcasting. Catches reverts before you spend gas:

try {
  // Simulate the transaction first
  await tokenContract.transfer.staticCall('0xRecipient', amount);

  // Simulation passed — safe to send
  const tx = await tokenContract.transfer('0xRecipient', amount);
  await tx.wait();
} catch (error) {
  // Simulation failed — transaction would revert
  console.error('Transaction would fail:', error.reason);
}

Common Patterns Quick Reference

// Check if address is a contract
const code = await provider.getCode(address);
const isContract = code !== '0x';

// Get transaction details
const tx = await provider.getTransaction(txHash);

// Get transaction receipt
const receipt = await provider.getTransactionReceipt(txHash);
const success = receipt?.status === 1;

// Wait for a transaction with timeout
const receipt = await provider.waitForTransaction(txHash, 1, 30000);
// confirmations = 1, timeout = 30 seconds

// Encode function data manually
const iface = new ethers.Interface(ABI);
const data = iface.encodeFunctionData('transfer', [recipient, amount]);

// Decode event log
const iface = new ethers.Interface(ABI);
const decoded = iface.parseLog({ topics: log.topics, data: log.data });

v5 to v6 Migration Notes

If you are upgrading from ethers.js v5:

v5v6
ethers.providers.JsonRpcProviderethers.JsonRpcProvider
ethers.providers.WebSocketProviderethers.WebSocketProvider
ethers.providers.Web3Providerethers.BrowserProvider
ethers.utils.formatEtherethers.formatEther
ethers.utils.parseEtherethers.parseEther
BigNumberNative BigInt
provider.getGasPrice()provider.getFeeData()

Start Building

BoltRPC provides HTTP and WebSocket endpoints for 20+ blockchain networks. Replace any endpoint URL in this guide with your BoltRPC URL and it works immediately across Ethereum, Arbitrum, Base, Polygon, Optimism, Avalanche, and more.

Get your free 2-week trial: trial.boltrpc.io

See all supported networks: boltrpc.io/networks


FAQ

What is the difference between a Provider and a Signer in ethers.js?

A Provider is a read-only connection to the blockchain. It can query state, get balances, read contracts, and fetch block data. A Signer has a private key and can sign transactions. To send transactions or write to contracts, you connect a Signer to a Provider.

Should I use ethers.js v5 or v6?

Use v6. It is the current major version with active maintenance. v5 still works but will receive fewer updates. The main differences are import paths (no more ethers.providers.*, ethers.utils.*) and native BigInt replacing BigNumber.

Can I use ethers.js for non-Ethereum EVM chains?

Yes. ethers.js works with any EVM-compatible chain. Just change the RPC endpoint URL. Arbitrum, Base, Polygon, Optimism, Avalanche, BNB Chain all work identically.

What is the difference between JsonRpcProvider and WebSocketProvider?

JsonRpcProvider uses HTTP: request/response, no persistent connection. Best for standard queries and transaction submission. WebSocketProvider uses WebSocket: persistent connection that supports real-time subscriptions (new blocks, contract events). Use HTTP for everything except subscriptions.

How do I handle private keys securely?

Never hardcode private keys. Load them from environment variables (process.env.PRIVATE_KEY), a secrets manager (AWS Secrets Manager, HashiCorp Vault), or a hardware wallet integration. For frontend applications, always use browser wallet providers (MetaMask) rather than handling private keys directly.

Frequently asked questions

Ready to build with high-performance RPC?

Start your free trial today. No credit card required. Access 20+ networks instantly.

Disclaimer: The content in this article is for informational purposes only and does not constitute financial, legal, or technical advice. Code examples and configurations are provided as-is. Always verify information with official documentation and test thoroughly in your own environment before deploying to production.

Continue reading