メインコンテンツまでスキップ

Ethereum Web3.js Integration Guide

Web3.js is the original and most widely-used JavaScript library for interacting with the Ethereum blockchain. This guide covers everything from basic setup to advanced patterns using BlockEden.xyz's Ethereum infrastructure.

Overview

Web3.js provides a complete JavaScript API for Ethereum, including:

  • Account Management: Create, import, and manage Ethereum accounts
  • Smart Contract Interaction: Deploy and call smart contracts
  • Transaction Handling: Send, sign, and monitor transactions
  • Event Monitoring: Subscribe to blockchain events
  • Utility Functions: Format conversions, hashing, and validation

Installation & Setup

Installation

# Using npm
npm install web3

# Using yarn
yarn add web3

# Using pnpm
pnpm add web3

Basic Setup

import { Web3 } from 'web3';

// Initialize with BlockEden.xyz endpoint
const web3 = new Web3('https://ethereum-mainnet.blockeden.xyz/<your-api-key>');

// Verify connection
async function testConnection() {
try {
const blockNumber = await web3.eth.getBlockNumber();
console.log('Current block number:', blockNumber);
console.log('Web3.js version:', web3.version);
} catch (error) {
console.error('Connection failed:', error);
}
}

testConnection();

Environment Configuration

// config/web3.js
import { Web3 } from 'web3';

const networks = {
mainnet: 'https://ethereum-mainnet.blockeden.xyz',
sepolia: 'https://ethereum-sepolia.blockeden.xyz',
polygon: 'https://polygon-mainnet.blockeden.xyz',
arbitrum: 'https://arbitrum-mainnet.blockeden.xyz'
};

export function createWeb3Instance(network = 'mainnet', apiKey) {
const rpcUrl = `${networks[network]}/${apiKey}`;
return new Web3(rpcUrl);
}

// Usage
const web3 = createWeb3Instance('mainnet', process.env.BLOCKEDEN_API_KEY);

Account Management

Creating Accounts

// Create a new account
const account = web3.eth.accounts.create();
console.log('Address:', account.address);
console.log('Private Key:', account.privateKey);

// Create multiple accounts
const accounts = web3.eth.accounts.create(5); // Creates 5 accounts
console.log('Generated accounts:', accounts);

// Create from entropy
const entropy = web3.utils.randomHex(32);
const accountFromEntropy = web3.eth.accounts.create(entropy);

Account Recovery

// Recover account from private key
const privateKey = '0x1234567890abcdef...';
const account = web3.eth.accounts.privateKeyToAccount(privateKey);

// Recover from mnemonic (requires additional library)
import { generateMnemonic, mnemonicToSeedSync } from 'bip39';
import { hdkey } from 'ethereumjs-wallet';

function recoverFromMnemonic(mnemonic, index = 0) {
const seed = mnemonicToSeedSync(mnemonic);
const hdwallet = hdkey.fromMasterSeed(seed);
const path = `m/44'/60'/0'/0/${index}`;
const wallet = hdwallet.derivePath(path).getWallet();

return web3.eth.accounts.privateKeyToAccount('0x' + wallet.getPrivateKey().toString('hex'));
}

// Usage
const mnemonic = 'your twelve word mnemonic phrase here...';
const account = recoverFromMnemonic(mnemonic, 0);

Wallet Management

class EthereumWallet {
constructor(web3Instance) {
this.web3 = web3Instance;
this.accounts = new Map();
}

// Add account to wallet
addAccount(privateKey, password) {
const account = this.web3.eth.accounts.privateKeyToAccount(privateKey);
const encrypted = this.web3.eth.accounts.encrypt(privateKey, password);

this.accounts.set(account.address.toLowerCase(), {
account,
encrypted
});

return account.address;
}

// Get account by address
getAccount(address) {
return this.accounts.get(address.toLowerCase())?.account;
}

// Sign transaction
async signTransaction(txObject, fromAddress, password) {
const accountData = this.accounts.get(fromAddress.toLowerCase());
if (!accountData) {
throw new Error('Account not found in wallet');
}

const decrypted = this.web3.eth.accounts.decrypt(accountData.encrypted, password);
return await this.web3.eth.accounts.signTransaction(txObject, decrypted.privateKey);
}

// Get all addresses
getAddresses() {
return Array.from(this.accounts.keys());
}

// Remove account
removeAccount(address) {
return this.accounts.delete(address.toLowerCase());
}
}

// Usage
const wallet = new EthereumWallet(web3);
const address = wallet.addAccount('0x1234...', 'password123');
console.log('Added account:', address);

Reading Blockchain Data

Account Information

// Get account balance
async function getAccountInfo(address) {
try {
// Balance in wei
const balanceWei = await web3.eth.getBalance(address);
// Convert to ether
const balanceEth = web3.utils.fromWei(balanceWei, 'ether');

// Transaction count (nonce)
const nonce = await web3.eth.getTransactionCount(address);

// Check if it's a contract
const code = await web3.eth.getCode(address);
const isContract = code !== '0x';

return {
address,
balance: {
wei: balanceWei,
eth: balanceEth
},
nonce,
isContract
};
} catch (error) {
console.error('Error getting account info:', error);
throw error;
}
}

// Usage
const info = await getAccountInfo('0x742D5Cc6bF2442E8C7c74c7b4Be6AB9d6f10f5B4');
console.log('Account info:', info);

Block Information

// Get latest block
async function getLatestBlock() {
const block = await web3.eth.getBlock('latest', true); // true = include transactions

return {
number: block.number,
hash: block.hash,
timestamp: new Date(Number(block.timestamp) * 1000),
gasUsed: block.gasUsed,
gasLimit: block.gasLimit,
transactionCount: block.transactions.length,
miner: block.miner
};
}

// Get block by number
async function getBlockByNumber(blockNumber) {
const block = await web3.eth.getBlock(blockNumber, false); // false = only tx hashes
return block;
}

// Get block range
async function getBlockRange(startBlock, endBlock) {
const blocks = [];

for (let i = startBlock; i <= endBlock; i++) {
const block = await web3.eth.getBlock(i, false);
blocks.push(block);
}

return blocks;
}

Transaction Information

// Get transaction details
async function getTransactionInfo(txHash) {
try {
// Get transaction
const tx = await web3.eth.getTransaction(txHash);
if (!tx) {
throw new Error('Transaction not found');
}

// Get receipt (only available for mined transactions)
const receipt = await web3.eth.getTransactionReceipt(txHash);

return {
hash: tx.hash,
status: receipt ? (receipt.status ? 'success' : 'failed') : 'pending',
blockNumber: tx.blockNumber,
from: tx.from,
to: tx.to,
value: web3.utils.fromWei(tx.value, 'ether'),
gasUsed: receipt ? receipt.gasUsed : null,
gasPrice: web3.utils.fromWei(tx.gasPrice, 'gwei'),
nonce: tx.nonce,
data: tx.input,
logs: receipt ? receipt.logs : null
};
} catch (error) {
console.error('Error getting transaction info:', error);
throw error;
}
}

// Monitor transaction status
async function waitForTransaction(txHash, timeout = 300000) { // 5 minutes default
const startTime = Date.now();

while (Date.now() - startTime < timeout) {
try {
const receipt = await web3.eth.getTransactionReceipt(txHash);
if (receipt) {
return {
success: receipt.status,
receipt
};
}

// Wait 2 seconds before checking again
await new Promise(resolve => setTimeout(resolve, 2000));
} catch (error) {
console.error('Error checking transaction:', error);
await new Promise(resolve => setTimeout(resolve, 5000));
}
}

throw new Error('Transaction timeout');
}

Sending Transactions

Basic Ether Transfer

// Send Ether using private key
async function sendEther(fromPrivateKey, toAddress, amountEth) {
try {
const account = web3.eth.accounts.privateKeyToAccount(fromPrivateKey);

// Get current gas price
const gasPrice = await web3.eth.getGasPrice();

// Get nonce
const nonce = await web3.eth.getTransactionCount(account.address, 'pending');

// Build transaction
const tx = {
from: account.address,
to: toAddress,
value: web3.utils.toWei(amountEth, 'ether'),
gas: 21000, // Standard gas limit for ETH transfer
gasPrice: gasPrice,
nonce: nonce
};

// Sign transaction
const signedTx = await web3.eth.accounts.signTransaction(tx, fromPrivateKey);

// Send transaction
const receipt = await web3.eth.sendSignedTransaction(signedTx.rawTransaction);

return {
txHash: receipt.transactionHash,
blockNumber: receipt.blockNumber,
gasUsed: receipt.gasUsed,
status: receipt.status
};

} catch (error) {
console.error('Error sending ether:', error);
throw error;
}
}

// Usage
const result = await sendEther(
'0x1234567890abcdef...', // private key
'0x742D5Cc6bF2442E8C7c74c7b4Be6AB9d6f10f5B4', // to address
'0.1' // amount in ETH
);
console.log('Transaction sent:', result);

EIP-1559 Transactions

// Send EIP-1559 transaction with dynamic fees
async function sendEIP1559Transaction(fromPrivateKey, toAddress, amountEth) {
try {
const account = web3.eth.accounts.privateKeyToAccount(fromPrivateKey);

// Get fee data
const feeHistory = await web3.eth.getFeeHistory(4, 'latest', [25, 50, 75]);
const baseFee = BigInt(feeHistory.baseFeePerGas[feeHistory.baseFeePerGas.length - 1]);

// Calculate priority fee (tip)
const maxPriorityFeePerGas = web3.utils.toWei('2', 'gwei'); // 2 gwei tip

// Max fee = base fee * 2 + priority fee (with buffer)
const maxFeePerGas = (baseFee * BigInt(2)) + BigInt(maxPriorityFeePerGas);

const nonce = await web3.eth.getTransactionCount(account.address, 'pending');

const tx = {
from: account.address,
to: toAddress,
value: web3.utils.toWei(amountEth, 'ether'),
gas: 21000,
maxFeePerGas: maxFeePerGas.toString(),
maxPriorityFeePerGas: maxPriorityFeePerGas,
nonce: nonce,
type: 2 // EIP-1559 transaction type
};

const signedTx = await web3.eth.accounts.signTransaction(tx, fromPrivateKey);
const receipt = await web3.eth.sendSignedTransaction(signedTx.rawTransaction);

return receipt;

} catch (error) {
console.error('Error sending EIP-1559 transaction:', error);
throw error;
}
}

Gas Estimation

// Estimate gas for transaction
async function estimateTransactionGas(txObject) {
try {
const gasEstimate = await web3.eth.estimateGas(txObject);
const gasPrice = await web3.eth.getGasPrice();

// Add 20% buffer to gas estimate
const gasLimit = Math.ceil(gasEstimate * 1.2);

const cost = {
gasEstimate,
gasLimit,
gasPrice,
totalCostWei: BigInt(gasLimit) * BigInt(gasPrice),
totalCostEth: web3.utils.fromWei((BigInt(gasLimit) * BigInt(gasPrice)).toString(), 'ether')
};

return cost;
} catch (error) {
console.error('Error estimating gas:', error);
throw error;
}
}

// Usage
const txObject = {
from: '0x742D5Cc6bF2442E8C7c74c7b4Be6AB9d6f10f5B4',
to: '0xA0b86a33E6c0e4A2a2a5FB1C6A6D6a30BF8b6B3a',
value: web3.utils.toWei('1', 'ether')
};

const gasCost = await estimateTransactionGas(txObject);
console.log('Gas estimation:', gasCost);

Smart Contract Interaction

Contract Instance Creation

// ERC-20 Token ABI (simplified)
const erc20ABI = [
{
"constant": true,
"inputs": [],
"name": "name",
"outputs": [{"name": "", "type": "string"}],
"type": "function"
},
{
"constant": true,
"inputs": [],
"name": "symbol",
"outputs": [{"name": "", "type": "string"}],
"type": "function"
},
{
"constant": true,
"inputs": [],
"name": "decimals",
"outputs": [{"name": "", "type": "uint8"}],
"type": "function"
},
{
"constant": true,
"inputs": [{"name": "_owner", "type": "address"}],
"name": "balanceOf",
"outputs": [{"name": "balance", "type": "uint256"}],
"type": "function"
},
{
"constant": false,
"inputs": [
{"name": "_to", "type": "address"},
{"name": "_value", "type": "uint256"}
],
"name": "transfer",
"outputs": [{"name": "", "type": "bool"}],
"type": "function"
},
{
"anonymous": false,
"inputs": [
{"indexed": true, "name": "from", "type": "address"},
{"indexed": true, "name": "to", "type": "address"},
{"indexed": false, "name": "value", "type": "uint256"}
],
"name": "Transfer",
"type": "event"
}
];

// Create contract instance
const tokenAddress = '0xA0b86a33E6c0e4A2a2a5FB1C6A6D6a30BF8b6B3a'; // USDC example
const tokenContract = new web3.eth.Contract(erc20ABI, tokenAddress);

Reading from Contracts

// Read token information
async function getTokenInfo(tokenContract) {
try {
const [name, symbol, decimals] = await Promise.all([
tokenContract.methods.name().call(),
tokenContract.methods.symbol().call(),
tokenContract.methods.decimals().call()
]);

return {
name,
symbol,
decimals: parseInt(decimals)
};
} catch (error) {
console.error('Error reading token info:', error);
throw error;
}
}

// Get token balance
async function getTokenBalance(tokenContract, address) {
try {
const balance = await tokenContract.methods.balanceOf(address).call();
const decimals = await tokenContract.methods.decimals().call();

// Convert from wei to token units
const formattedBalance = balance / Math.pow(10, decimals);

return {
raw: balance,
formatted: formattedBalance.toString()
};
} catch (error) {
console.error('Error getting token balance:', error);
throw error;
}
}

// Usage
const tokenInfo = await getTokenInfo(tokenContract);
console.log('Token info:', tokenInfo);

const balance = await getTokenBalance(tokenContract, '0x742D5Cc6bF2442E8C7c74c7b4Be6AB9d6f10f5B4');
console.log('Token balance:', balance);

Writing to Contracts

// Transfer tokens
async function transferTokens(tokenContract, fromPrivateKey, toAddress, amount) {
try {
const account = web3.eth.accounts.privateKeyToAccount(fromPrivateKey);

// Get token decimals
const decimals = await tokenContract.methods.decimals().call();

// Convert amount to wei equivalent
const amountWei = (amount * Math.pow(10, decimals)).toString();

// Encode the function call
const data = tokenContract.methods.transfer(toAddress, amountWei).encodeABI();

// Estimate gas
const gasEstimate = await tokenContract.methods.transfer(toAddress, amountWei).estimateGas({
from: account.address
});

// Get gas price
const gasPrice = await web3.eth.getGasPrice();

// Get nonce
const nonce = await web3.eth.getTransactionCount(account.address, 'pending');

// Build transaction
const tx = {
from: account.address,
to: tokenContract.options.address,
data: data,
gas: Math.ceil(gasEstimate * 1.2), // Add 20% buffer
gasPrice: gasPrice,
nonce: nonce
};

// Sign and send
const signedTx = await web3.eth.accounts.signTransaction(tx, fromPrivateKey);
const receipt = await web3.eth.sendSignedTransaction(signedTx.rawTransaction);

return receipt;

} catch (error) {
console.error('Error transferring tokens:', error);
throw error;
}
}

// Usage
const transferReceipt = await transferTokens(
tokenContract,
'0x1234567890abcdef...', // private key
'0x742D5Cc6bF2442E8C7c74c7b4Be6AB9d6f10f5B4', // to address
100 // amount of tokens
);
console.log('Transfer completed:', transferReceipt.transactionHash);

Contract Deployment

// Deploy a simple contract
async function deployContract(abi, bytecode, constructorArgs, fromPrivateKey) {
try {
const account = web3.eth.accounts.privateKeyToAccount(fromPrivateKey);

// Create contract instance
const contract = new web3.eth.Contract(abi);

// Get deployment data
const deployData = contract.deploy({
data: bytecode,
arguments: constructorArgs
}).encodeABI();

// Estimate gas for deployment
const gasEstimate = await web3.eth.estimateGas({
from: account.address,
data: deployData
});

const gasPrice = await web3.eth.getGasPrice();
const nonce = await web3.eth.getTransactionCount(account.address, 'pending');

// Build deployment transaction
const tx = {
from: account.address,
data: deployData,
gas: Math.ceil(gasEstimate * 1.2),
gasPrice: gasPrice,
nonce: nonce
};

// Sign and send
const signedTx = await web3.eth.accounts.signTransaction(tx, fromPrivateKey);
const receipt = await web3.eth.sendSignedTransaction(signedTx.rawTransaction);

// Return deployed contract address
return {
contractAddress: receipt.contractAddress,
transactionHash: receipt.transactionHash,
gasUsed: receipt.gasUsed
};

} catch (error) {
console.error('Error deploying contract:', error);
throw error;
}
}

Event Monitoring

Listening to Contract Events

// Listen to Transfer events
function listenToTransferEvents(tokenContract, callback) {
const transferEvent = tokenContract.events.Transfer({
filter: {}, // Can filter by indexed parameters
fromBlock: 'latest'
});

transferEvent.on('data', (event) => {
const { from, to, value } = event.returnValues;

callback({
from,
to,
value,
blockNumber: event.blockNumber,
transactionHash: event.transactionHash,
logIndex: event.logIndex
});
});

transferEvent.on('error', (error) => {
console.error('Event error:', error);
});

return transferEvent; // Return for cleanup
}

// Usage
const eventSubscription = listenToTransferEvents(tokenContract, (transferData) => {
console.log('Token transfer detected:', transferData);
});

// Stop listening
// eventSubscription.unsubscribe();

Historical Event Queries

// Get historical events
async function getHistoricalTransfers(tokenContract, fromBlock, toBlock, addresses = []) {
try {
const events = await tokenContract.getPastEvents('Transfer', {
filter: addresses.length > 0 ? { from: addresses } : {},
fromBlock: fromBlock,
toBlock: toBlock
});

return events.map(event => ({
from: event.returnValues.from,
to: event.returnValues.to,
value: event.returnValues.value,
blockNumber: event.blockNumber,
transactionHash: event.transactionHash,
timestamp: null // You'd need to fetch block to get timestamp
}));
} catch (error) {
console.error('Error getting historical events:', error);
throw error;
}
}

// Get events with block timestamps
async function getTransfersWithTimestamps(tokenContract, fromBlock, toBlock) {
const events = await getHistoricalTransfers(tokenContract, fromBlock, toBlock);

// Get unique block numbers
const blockNumbers = [...new Set(events.map(e => e.blockNumber))];

// Fetch block information
const blocks = await Promise.all(
blockNumbers.map(blockNum => web3.eth.getBlock(blockNum))
);

// Create block timestamp lookup
const blockTimestamps = Object.fromEntries(
blocks.map(block => [block.number, Number(block.timestamp)])
);

// Add timestamps to events
return events.map(event => ({
...event,
timestamp: blockTimestamps[event.blockNumber]
}));
}

Utility Functions & Helpers

Format Conversions

// Common format conversions
const utils = {
// Wei conversions
toWei: (amount, unit = 'ether') => web3.utils.toWei(amount.toString(), unit),
fromWei: (amount, unit = 'ether') => web3.utils.fromWei(amount.toString(), unit),

// Unit conversions
toGwei: (wei) => web3.utils.fromWei(wei.toString(), 'gwei'),
fromGwei: (gwei) => web3.utils.toWei(gwei.toString(), 'gwei'),

// Number formatting
toBN: (value) => web3.utils.toBN(value),
toHex: (value) => web3.utils.toHex(value),
fromHex: (hex) => web3.utils.hexToNumber(hex),

// Address utilities
isAddress: (address) => web3.utils.isAddress(address),
toChecksumAddress: (address) => web3.utils.toChecksumAddress(address),

// Hash utilities
keccak256: (value) => web3.utils.keccak256(value),
soliditySha3: (...values) => web3.utils.soliditySha3(...values),

// Random utilities
randomHex: (size) => web3.utils.randomHex(size),

// Encoding utilities
encodeParameter: (type, value) => web3.eth.abi.encodeParameter(type, value),
decodeParameter: (type, hex) => web3.eth.abi.decodeParameter(type, hex),
encodeParameters: (types, values) => web3.eth.abi.encodeParameters(types, values),
decodeParameters: (types, hex) => web3.eth.abi.decodeParameters(types, hex)
};

// Usage examples
console.log('1 ETH in wei:', utils.toWei('1'));
console.log('Gas price in gwei:', utils.toGwei('20000000000'));
console.log('Is valid address:', utils.isAddress('0x742D5Cc6bF2442E8C7c74c7b4Be6AB9d6f10f5B4'));

Error Handling

// Comprehensive error handler
class Web3ErrorHandler {
static handle(error) {
if (error.code) {
switch (error.code) {
case 4001:
return 'User rejected the request';
case 4100:
return 'The requested account and/or method has not been authorized';
case 4200:
return 'The requested method is not supported';
case 4900:
return 'The provider is disconnected';
case 4901:
return 'The provider is disconnected from all chains';
case -32700:
return 'Parse error - Invalid JSON';
case -32600:
return 'Invalid Request';
case -32601:
return 'Method not found';
case -32602:
return 'Invalid params';
case -32603:
return 'Internal error';
default:
return `Unknown error code: ${error.code}`;
}
}

if (error.message) {
if (error.message.includes('insufficient funds')) {
return 'Insufficient funds for transaction';
}
if (error.message.includes('gas too low')) {
return 'Gas limit too low';
}
if (error.message.includes('nonce too low')) {
return 'Nonce too low - transaction may have been sent already';
}
if (error.message.includes('replacement transaction underpriced')) {
return 'Replacement transaction underpriced';
}
}

return error.message || 'Unknown error occurred';
}

static async handleWithRetry(fn, maxRetries = 3, delay = 1000) {
for (let i = 0; i < maxRetries; i++) {
try {
return await fn();
} catch (error) {
if (i === maxRetries - 1) {
throw new Error(this.handle(error));
}

console.warn(`Attempt ${i + 1} failed, retrying in ${delay}ms:`, this.handle(error));
await new Promise(resolve => setTimeout(resolve, delay));
delay *= 2; // Exponential backoff
}
}
}
}

// Usage
try {
const result = await Web3ErrorHandler.handleWithRetry(async () => {
return await web3.eth.getBalance('0x742D5Cc6bF2442E8C7c74c7b4Be6AB9d6f10f5B4');
});
console.log('Balance:', result);
} catch (error) {
console.error('Final error:', error.message);
}

Performance Optimization

Batch Requests

// Batch multiple requests
async function batchRequests(requests) {
const batch = new web3.BatchRequest();
const promises = [];

requests.forEach(({ method, params }) => {
promises.push(new Promise((resolve, reject) => {
const request = web3.eth[method].request(...params, (error, result) => {
if (error) reject(error);
else resolve(result);
});
batch.add(request);
}));
});

batch.execute();
return Promise.all(promises);
}

// Usage
const results = await batchRequests([
{ method: 'getBalance', params: ['0x742D5Cc6bF2442E8C7c74c7b4Be6AB9d6f10f5B4'] },
{ method: 'getTransactionCount', params: ['0x742D5Cc6bF2442E8C7c74c7b4Be6AB9d6f10f5B4'] },
{ method: 'getBlockNumber', params: [] }
]);

console.log('Batch results:', results);

Connection Pooling

// Simple connection pool
class Web3Pool {
constructor(endpoints, maxConnections = 5) {
this.endpoints = endpoints;
this.maxConnections = maxConnections;
this.connections = [];
this.currentIndex = 0;

this.initializePool();
}

initializePool() {
for (let i = 0; i < this.maxConnections; i++) {
const endpoint = this.endpoints[i % this.endpoints.length];
this.connections.push(new Web3(endpoint));
}
}

getConnection() {
const connection = this.connections[this.currentIndex];
this.currentIndex = (this.currentIndex + 1) % this.connections.length;
return connection;
}

async executeWithLoadBalancing(method, ...params) {
const connection = this.getConnection();
return await connection.eth[method](...params);
}
}

// Usage
const pool = new Web3Pool([
'https://ethereum-mainnet.blockeden.xyz/api-key-1',
'https://ethereum-mainnet.blockeden.xyz/api-key-2',
'https://ethereum-mainnet.blockeden.xyz/api-key-3'
]);

const balance = await pool.executeWithLoadBalancing('getBalance', '0x742D5Cc6bF2442E8C7c74c7b4Be6AB9d6f10f5B4');

Next Steps

Resources