Binance Smart Chain Ethers.js Integration Guide
Ethers.js is a modern, lightweight, and TypeScript-first library for interacting with the Binance Smart Chain blockchain. This guide covers Ethers.js v6+ patterns and best practices using BlockEden.xyz's Binance Smart Chain infrastructure.
Why Ethers.js?
Advantages over Web3.js
- TypeScript First: Built with TypeScript for better developer experience
- Modular Design: Import only what you need
- Modern Promise-based API: Clean async/await patterns
- Better Error Handling: More informative error messages
- Tree Shakable: Smaller bundle sizes
- ENS Support: Built-in Binance Smart Chain Name Service support
- Provider Abstraction: Clean separation between providers and signers
Installation & Setup
Installation
# Using npm
npm install ethers
# Using yarn
yarn add ethers
# Using pnpm
pnpm add ethers
Basic Setup
import { ethers } from 'ethers';
// Create provider with BlockEden.xyz endpoint
const provider = new ethers.JsonRpcProvider(
'https://api.blockeden.xyz/bsc/${accessKey}'
);
// Verify connection
async function testConnection() {
try {
const network = await provider.getNetwork();
const blockNumber = await provider.getBlockNumber();
console.log('Connected to:', network.name);
console.log('Chain ID:', network.chainId);
console.log('Current block:', blockNumber);
} catch (error) {
console.error('Connection failed:', error);
}
}
testConnection();
Multi-Network Configuration
// config/providers.ts
import { ethers } from 'ethers';
interface NetworkConfig {
name: string;
chainId: number;
rpcUrl: string;
}
const networks: Record<string, NetworkConfig> = {
mainnet: {
name: 'mainnet',
chainId: 1,
rpcUrl: 'https://ethereum-mainnet.blockeden.xyz'
},
sepolia: {
name: 'sepolia',
chainId: 11155111,
rpcUrl: 'https://ethereum-sepolia.blockeden.xyz'
},
polygon: {
name: 'polygon',
chainId: 137,
rpcUrl: 'https://polygon-mainnet.blockeden.xyz'
},
arbitrum: {
name: 'arbitrum',
chainId: 42161,
rpcUrl: 'https://arbitrum-mainnet.blockeden.xyz'
}
};
export class ProviderManager {
private providers: Map<string, ethers.JsonRpcProvider> = new Map();
constructor(private apiKey: string) {}
getProvider(network: string): ethers.JsonRpcProvider {
if (!this.providers.has(network)) {
const config = networks[network];
if (!config) {
throw new Error(`Unsupported network: ${network}`);
}
const provider = new ethers.JsonRpcProvider(`${config.rpcUrl}/${this.apiKey}`);
this.providers.set(network, provider);
}
return this.providers.get(network)!;
}
getAllProviders(): Record<string, ethers.JsonRpcProvider> {
const result: Record<string, ethers.JsonRpcProvider> = {};
Object.keys(networks).forEach(network => {
result[network] = this.getProvider(network);
});
return result;
}
}
// Usage
const providerManager = new ProviderManager(process.env.BLOCKEDEN_API_KEY!);
const mainnetProvider = providerManager.getProvider('mainnet');
const sepoliaProvider = providerManager.getProvider('sepolia');
Wallets and Signers
Creating Wallets
// Create random wallet
const randomWallet = ethers.Wallet.createRandom();
console.log('Address:', randomWallet.address);
console.log('Private Key:', randomWallet.privateKey);
console.log('Mnemonic:', randomWallet.mnemonic?.phrase);
// Create wallet from private key
const privateKey = '0x1234567890abcdef...';
const wallet = new ethers.Wallet(privateKey);
// Create wallet from mnemonic
const mnemonic = 'abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about';
const walletFromMnemonic = ethers.Wallet.fromPhrase(mnemonic);
// Connect wallet to provider
const connectedWallet = wallet.connect(provider);
HD Wallet Management
// HD Wallet utilities
class HDWallet {
private hdNode: ethers.HDNodeWallet;
constructor(mnemonic?: string) {
if (mnemonic) {
this.hdNode = ethers.Wallet.fromPhrase(mnemonic);
} else {
this.hdNode = ethers.Wallet.createRandom();
}
}
// Get wallet at specific derivation path
getWallet(index: number, account: number = 0): ethers.HDNodeWallet {
const path = `m/44'/60'/${account}'/0/${index}`;
return this.hdNode.derivePath(path);
}
// Get multiple wallets
getWallets(count: number, account: number = 0): ethers.HDNodeWallet[] {
const wallets: ethers.HDNodeWallet[] = [];
for (let i = 0; i < count; i++) {
wallets.push(this.getWallet(i, account));
}
return wallets;
}
// Get master wallet
getMasterWallet(): ethers.HDNodeWallet {
return this.hdNode;
}
// Get mnemonic
getMnemonic(): string | null {
return this.hdNode.mnemonic?.phrase || null;
}
// Connect wallets to provider
connectWallets(wallets: ethers.HDNodeWallet[], provider: ethers.JsonRpcProvider): ethers.Wallet[] {
return wallets.map(wallet => wallet.connect(provider));
}
}
// Usage
const hdWallet = new HDWallet();
const wallets = hdWallet.getWallets(5); // Get first 5 wallets
const connectedWallets = hdWallet.connectWallets(wallets, provider);
console.log('Generated addresses:');
connectedWallets.forEach((wallet, index) => {
console.log(`Wallet ${index}:`, wallet.address);
});
Encrypted Wallet Storage
// Encrypt and store wallet
async function encryptWallet(wallet: ethers.Wallet, password: string): Promise<string> {
try {
const encryptedJson = await wallet.encrypt(password);
return encryptedJson;
} catch (error) {
console.error('Error encrypting wallet:', error);
throw error;
}
}
// Decrypt wallet
async function decryptWallet(encryptedJson: string, password: string): Promise<ethers.Wallet> {
try {
const wallet = await ethers.Wallet.fromEncryptedJson(encryptedJson, password);
return wallet;
} catch (error) {
console.error('Error decrypting wallet:', error);
throw error;
}
}
// Wallet manager with encryption
class SecureWalletManager {
private encryptedWallets: Map<string, string> = new Map();
async addWallet(alias: string, privateKey: string, password: string): Promise<string> {
const wallet = new ethers.Wallet(privateKey);
const encrypted = await encryptWallet(wallet, password);
this.encryptedWallets.set(alias, encrypted);
return wallet.address;
}
async getWallet(alias: string, password: string): Promise<ethers.Wallet> {
const encrypted = this.encryptedWallets.get(alias);
if (!encrypted) {
throw new Error(`Wallet not found: ${alias}`);
}
return await decryptWallet(encrypted, password);
}
getAliases(): string[] {
return Array.from(this.encryptedWallets.keys());
}
removeWallet(alias: string): boolean {
return this.encryptedWallets.delete(alias);
}
// Export for storage
export(): Record<string, string> {
return Object.fromEntries(this.encryptedWallets);
}
// Import from storage
import(data: Record<string, string>): void {
this.encryptedWallets = new Map(Object.entries(data));
}
}
Reading Blockchain Data
Account Information
// Get comprehensive account information
async function getAccountInfo(address: string, provider: ethers.JsonRpcProvider) {
try {
const [balance, nonce, code] = await Promise.all([
provider.getBalance(address),
provider.getTransactionCount(address),
provider.getCode(address)
]);
return {
address,
balance: {
wei: balance.toString(),
eth: ethers.formatEther(balance)
},
nonce,
isContract: code !== '0x',
code: code !== '0x' ? code : null
};
} catch (error) {
console.error('Error getting account info:', error);
throw error;
}
}
// Get balance in different denominations
async function getBalanceFormatted(address: string, provider: ethers.JsonRpcProvider) {
const balance = await provider.getBalance(address);
return {
wei: balance.toString(),
gwei: ethers.formatUnits(balance, 'gwei'),
eth: ethers.formatEther(balance),
usd: null // You would need to fetch price data
};
}