🎯 Ejemplos recomendados
Balanced sample collections from various categories for you to explore
Biblioteca Ethers.js Ethereum
Ejemplos de Ethers.js incluyendo proveedores, billeteras, contratos, transacciones, eventos, firmas, utilidades y patrones de interacción blockchain
💻 Ethers.js Básicos - Guía de Inicio javascript
🟢 simple
⭐⭐
Fundamentos completos de Ethers.js incluyendo proveedores, billeteras, interacción de contratos y operaciones básicas de blockchain
⏱️ 25 min
🏷️ ethers, ethereum, web3, basics
Prerequisites:
Node.js, Basic understanding of Ethereum, API keys for providers
// Ethers.js Basics - Complete Getting Started Guide
// Install: npm install ethers
const { ethers } = require("ethers");
// ===== Provider Examples =====
console.log("=== PROVIDER EXAMPLES ===");
// 1. Provider from JSON-RPC URL
const provider = new ethers.providers.JsonRpcProvider("https://eth-mainnet.alchemyapi.io/v2/YOUR-API-KEY");
console.log("✅ JSON-RPC Provider created");
// 2. Provider from environment (MetaMask, etc.)
if (typeof window !== 'undefined' && window.ethereum) {
const metamaskProvider = new ethers.providers.Web3Provider(window.ethereum);
console.log("✅ MetaMask Provider created");
}
// 3. Provider from WebSocket (for real-time updates)
const wsProvider = new ethers.providers.WebSocketProvider("wss://eth-mainnet.alchemyapi.io/v2/YOUR-API-KEY");
console.log("✅ WebSocket Provider created");
// 4. Fallback provider (multiple providers for reliability)
const fallbackProvider = new ethers.providers.FallbackProvider([
ethers.providers.InfuraProvider.getWebSocketProvider("mainnet"),
ethers.providers.AlchemyProvider.getWebSocketProvider("mainnet", "YOUR-API-KEY"),
]);
console.log("✅ Fallback Provider created");
// ===== Wallet Examples =====
console.log("\n=== WALLET EXAMPLES ===");
// 1. Create random wallet
const wallet = ethers.Wallet.createRandom();
console.log("🔑 Random Wallet:");
console.log(" Address:", wallet.address);
console.log(" Private Key:", wallet.privateKey);
console.log(" Mnemonic:", wallet.mnemonic.phrase);
// 2. Wallet from private key
const privateKey = "0x0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef";
const walletFromKey = new ethers.Wallet(privateKey);
console.log("\n🔐 Wallet from Private Key:");
console.log(" Address:", walletFromKey.address);
// 3. Wallet from mnemonic
const mnemonic = "abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about";
const walletFromMnemonic = ethers.Wallet.fromMnemonic(mnemonic);
console.log("\n📝 Wallet from Mnemonic:");
console.log(" Address:", walletFromMnemonic.address);
// 4. Connect wallet to provider
const connectedWallet = wallet.connect(provider);
console.log("\n🔗 Connected Wallet:");
console.log(" Address:", connectedWallet.address);
console.log(" Connected to:", connectedWallet.provider.connection.url);
// ===== Basic Blockchain Operations =====
console.log("\n=== BLOCKCHAIN OPERATIONS ===");
async function blockchainBasics() {
try {
// Get network information
const network = await provider.getNetwork();
console.log("🌐 Network Info:");
console.log(" Name:", network.name);
console.log(" Chain ID:", network.chainId);
// Get latest block
const block = await provider.getBlock("latest");
console.log("\n📦 Latest Block:");
console.log(" Number:", block.number);
console.log(" Hash:", block.hash);
console.log(" Timestamp:", new Date(block.timestamp * 1000).toLocaleString());
console.log(" Gas Limit:", block.gasLimit.toString());
console.log(" Transactions:", block.transactions.length);
// Get account balance
const address = "0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045"; // Vitalik's address
const balance = await provider.getBalance(address);
console.log("\n💰 Balance Info:");
console.log(" Address:", address);
console.log(" Balance:", ethers.utils.formatEther(balance), "ETH");
// Get transaction count
const transactionCount = await provider.getTransactionCount(address);
console.log(" Transaction Count:", transactionCount);
// Get gas price
const gasPrice = await provider.getGasPrice();
console.log("\n⛽ Gas Info:");
console.log(" Gas Price:", ethers.utils.formatUnits(gasPrice, "gwei"), "gwei");
return { network, block, balance, gasPrice };
} catch (error) {
console.error("❌ Error in blockchain operations:", error.message);
return null;
}
}
// ===== Transaction Examples =====
console.log("\n=== TRANSACTION EXAMPLES =====");
async function transactionExamples() {
try {
// Send ETH transaction
const sendWallet = new ethers.Wallet(privateKey, provider);
const recipient = "0x70997970C51812dc3A010C7d01b50e0d17dc79C8";
const amount = ethers.utils.parseEther("0.01"); // 0.01 ETH
console.log("💸 Sending ETH...");
console.log(" From:", sendWallet.address);
console.log(" To:", recipient);
console.log(" Amount:", ethers.utils.formatEther(amount), "ETH");
// Create transaction
const tx = {
to: recipient,
value: amount,
gasLimit: 21000, // Standard ETH transfer
};
// Estimate gas
const gasEstimate = await sendWallet.estimateGas(tx);
console.log(" Estimated Gas:", gasEstimate.toString());
// Get current gas price
const gasPrice = await sendWallet.getGasPrice();
const txWithGas = { ...tx, gasPrice };
// Calculate total cost
const gasCost = gasEstimate.mul(gasPrice);
const totalCost = amount.add(gasCost);
console.log(" Gas Cost:", ethers.utils.formatEther(gasCost), "ETH");
console.log(" Total Cost:", ethers.utils.formatEther(totalCost), "ETH");
// Check balance
const walletBalance = await sendWallet.getBalance();
if (walletBalance.lt(totalCost)) {
console.log("❌ Insufficient balance");
return;
}
// Send transaction (commented out to avoid actual spend)
console.log(" Ready to send (transaction commented out for safety)");
/*
const txResponse = await sendWallet.sendTransaction(txWithGas);
console.log(" Transaction Hash:", txResponse.hash);
// Wait for confirmation
const receipt = await txResponse.wait();
console.log(" Confirmed in block:", receipt.blockNumber);
console.log(" Gas Used:", receipt.gasUsed.toString());
*/
return { tx, gasEstimate, gasPrice };
} catch (error) {
console.error("❌ Error in transaction:", error.message);
return null;
}
}
// ===== Smart Contract Interaction =====
console.log("\n=== CONTRACT INTERACTION EXAMPLES ===");
// ERC20 ABI (minimal example)
const erc20ABI = [
"function name() view returns (string)",
"function symbol() view returns (string)",
"function decimals() view returns (uint8)",
"function totalSupply() view returns (uint256)",
"function balanceOf(address) view returns (uint256)",
"function transfer(address, uint256) returns (bool)",
"event Transfer(address indexed from, address indexed to, uint256 value)",
];
// WETH contract address on mainnet
const wethAddress = "0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2";
async function contractInteraction() {
try {
// Create contract instance
const wethContract = new ethers.Contract(wethAddress, erc20ABI, provider);
console.log("📄 Contract Instance:");
console.log(" Address:", wethContract.address);
console.log(" Interface:", wethContract.interface.format());
// Read contract data (view functions)
const name = await wethContract.name();
const symbol = await wethContract.symbol();
const decimals = await wethContract.decimals();
const totalSupply = await wethContract.totalSupply();
console.log("\n📊 Contract Data:");
console.log(" Name:", name);
console.log(" Symbol:", symbol);
console.log(" Decimals:", decimals);
console.log(" Total Supply:", ethers.utils.formatUnits(totalSupply, decimals));
// Get balance of specific address
const address = "0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045";
const balance = await wethContract.balanceOf(address);
console.log("\n💼 Balance Info:");
console.log(" Address:", address);
console.log(" Balance:", ethers.utils.formatUnits(balance, decimals), symbol);
// Connect wallet for write operations
const walletWithContract = wethContract.connect(connectedWallet);
console.log("\n✍️ Contract ready for write operations with wallet:", connectedWallet.address);
// Listen to events (requires WebSocket provider or polling)
console.log("\n📡 Setting up event listeners...");
wethContract.on("Transfer", (from, to, value, event) => {
console.log("🎯 Transfer Event:");
console.log(" From:", from);
console.log(" To:", to);
console.log(" Value:", ethers.utils.formatUnits(value, decimals), symbol);
console.log(" Block:", event.blockNumber);
});
// Example of calling a function (would require ETH for WETH)
console.log("\n💱 WETH Deposit Example (commented out):");
console.log(" To deposit ETH for WETH: walletWithContract.deposit({ value: ethers.utils.parseEther('1') })");
return { name, symbol, decimals, totalSupply, balance };
} catch (error) {
console.error("❌ Error in contract interaction:", error.message);
return null;
}
}
// ===== Event Handling =====
console.log("\n=== EVENT HANDLING EXAMPLES =====");
async function eventHandling() {
try {
// Create contract with WebSocket provider for real-time events
const wsProvider = new ethers.providers.WebSocketProvider("wss://eth-mainnet.alchemyapi.io/v2/YOUR-API-KEY");
const contract = new ethers.Contract(wethAddress, erc20ABI, wsProvider);
console.log("🎧 Event Listeners Setup");
// Listen to all Transfer events
contract.on("Transfer", (from, to, value, event) => {
if (ethers.BigNumber.from(value).gt(ethers.utils.parseUnits("100", 18))) {
console.log("🚨 Large Transfer Detected!");
console.log(" From:", from);
console.log(" To:", to);
console.log(" Value:", ethers.utils.formatUnits(value, 18), "WETH");
console.log(" Transaction:", event.transactionHash);
}
});
// Filter events by specific address
const vitalikAddress = "0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045";
const filter = contract.filters.Transfer(null, vitalikAddress);
contract.on(filter, (from, to, value) => {
console.log("💰 Transfer to Vitalik:");
console.log(" From:", from);
console.log(" Value:", ethers.utils.formatUnits(value, 18), "WETH");
});
// Query historical events
const currentBlock = await wsProvider.getBlockNumber();
const fromBlock = currentBlock - 100; // Last 100 blocks
console.log("\n📜 Querying historical events...");
const historicalEvents = await contract.queryFilter(
"Transfer",
fromBlock,
currentBlock
);
console.log(` Found ${historicalEvents.length} transfer events in last 100 blocks`);
// Process recent events
historicalEvents.slice(-5).forEach(event => {
const { from, to, value } = event.args;
console.log(` Recent Transfer: ${ethers.utils.formatUnits(value, 18)} WETH from ${from} to ${to}`);
});
return { historicalEvents };
} catch (error) {
console.error("❌ Error in event handling:", error.message);
return null;
}
}
// ===== Utility Functions =====
console.log("\n=== UTILITY FUNCTIONS ===");
function utilityExamples() {
console.log("🔧 Ethers Utilities:");
// Address operations
const address = "0x70997970C51812dc3A010C7d01b50e0d17dc79C8";
console.log("\n📍 Address Operations:");
console.log(" Address Checksum:", ethers.utils.getAddress(address));
console.log(" Is Address:", ethers.utils.isAddress(address));
console.log(" Compute Address (from private key):", ethers.utils.computeAddress(privateKey));
// Hashing
const message = "Hello, Ethereum!";
console.log("\n🔐 Hashing:");
console.log(" Keccak256:", ethers.utils.keccak256(ethers.utils.toUtf8Bytes(message)));
console.log(" Solidity Keccak256:", ethers.utils.solidityKeccak256(["string"], [message]));
// Encoding/Decoding
console.log("\n📝 Encoding/Decoding:");
console.log(" UTF8 to Hex:", ethers.utils.hexlify(ethers.utils.toUtf8Bytes(message)));
console.log(" Hex to UTF8:", ethers.utils.toUtf8String("0x48656c6c6f2c20457468657265756d21"));
// Unit conversions
console.log("\n🔄 Unit Conversions:");
const weiAmount = ethers.utils.parseEther("1.5");
console.log(" 1.5 ETH to Wei:", weiAmount.toString());
console.log(" Wei back to ETH:", ethers.utils.formatEther(weiAmount));
const gweiAmount = ethers.utils.parseUnits("20", "gwei");
console.log(" 20 Gwei to Wei:", gweiAmount.toString());
console.log(" Wei back to Gwei:", ethers.utils.formatUnits(gweiAmount, "gwei"));
// Big number operations
console.log("\n🔢 Big Number Operations:");
const big1 = ethers.BigNumber.from("1000000000000000000");
const big2 = ethers.BigNumber.from("2000000000000000000");
console.log(" Big Number 1:", big1.toString());
console.log(" Big Number 2:", big2.toString());
console.log(" Sum:", big1.add(big2).toString());
console.log(" Product:", big1.mul(3).toString());
console.log(" Division:", big2.div(big1).toString());
// ABI operations
const iface = new ethers.utils.Interface(erc20ABI);
console.log("\n📜 ABI Operations:");
console.log(" Functions:", iface.functions);
console.log(" Events:", iface.events);
console.log(" Encode function call:", iface.encodeFunctionData("balanceOf", [address]));
return {
address,
hashedMessage: ethers.utils.keccak256(ethers.utils.toUtf8Bytes(message)),
weiAmount,
bigNumber: big1,
};
}
// ===== Error Handling =====
console.log("\n=== ERROR HANDLING EXAMPLES =====");
async function errorHandling() {
try {
// Example of catching different types of errors
const badAddress = "0xinvalid";
try {
ethers.utils.getAddress(badAddress);
} catch (error) {
console.log("❌ Invalid Address Error:", error.message);
}
// Network errors
const badProvider = new ethers.providers.JsonRpcProvider("http://invalid-url");
try {
await badProvider.getNetwork();
} catch (error) {
console.log("❌ Network Error:", error.message);
}
// Insufficient funds simulation
const poorWallet = new ethers.Wallet(privateKey, provider);
const balance = await poorWallet.getBalance();
try {
const largeAmount = balance.add(ethers.utils.parseEther("1"));
await poorWallet.estimateGas({
to: "0x70997970C51812dc3A010C7d01b50e0d17dc79C8",
value: largeAmount,
});
} catch (error) {
console.log("❌ Insufficient Funds Error:", error.message);
}
return { balance };
} catch (error) {
console.error("❌ Unexpected error:", error.message);
return null;
}
}
// ===== Main Execution =====
async function main() {
console.log("🚀 ETHERS.JS COMPLETE GUIDE");
console.log("=" .repeat(50));
try {
// Execute all examples
await blockchainBasics();
console.log("\n" + "-".repeat(50));
await transactionExamples();
console.log("\n" + "-".repeat(50));
await contractInteraction();
console.log("\n" + "-".repeat(50));
await eventHandling();
console.log("\n" + "-".repeat(50));
utilityExamples();
console.log("\n" + "-".repeat(50));
await errorHandling();
console.log("\n✅ ALL EXAMPLES COMPLETED SUCCESSFULLY!");
console.log("\n📚 Next Steps:");
console.log("1. Replace 'YOUR-API-KEY' with actual API keys");
console.log("2. Uncomment transaction examples to test with real ETH");
console.log("3. Explore more complex contract interactions");
console.log("4. Learn about ENS, gas estimation, and transaction signing");
console.log("5. Build a complete dApp with frontend integration");
} catch (error) {
console.error("\n❌ FATAL ERROR:", error.message);
}
}
// Only run main if this file is executed directly
if (require.main === module) {
main().catch(console.error);
}
// Export functions for individual testing
module.exports = {
blockchainBasics,
transactionExamples,
contractInteraction,
eventHandling,
utilityExamples,
errorHandling,
};
💻 Operaciones Avanzadas de Contratos Ethers.js javascript
🟡 intermediate
⭐⭐⭐⭐
Patrones avanzados de interacción de contratos incluyendo multisig, operaciones por lotes, optimización de gas y manejo complejo de ABI
⏱️ 35 min
🏷️ ethers, advanced, contracts, optimization
Prerequisites:
Ethers.js basics, Understanding of Ethereum transactions, Solidity knowledge
// Ethers.js Advanced Contract Operations
// Complex patterns for professional dApp development
const { ethers } = require("ethers");
// ===== Multisig Operations =====
console.log("=== MULTISIG OPERATIONS ===");
// Gnosis Safe ABI (simplified)
const multisigABI = [
"function getOwners() view returns (address[])",
"function getThreshold() view returns (uint256)",
"function nonce() view returns (uint256)",
"function execTransaction(address to, uint256 value, bytes calldata data, uint8 operation, uint256 safeTxGas, uint256 baseGas, uint256 gasPrice, address gasToken, address refundReceiver, bytes calldata signatures) returns (bool success)",
"function approveHash(bytes32 hashToApprove) external",
"function confirmTransaction(uint256 transactionId) external",
"event ExecutionSuccess(bytes32 indexed txHash, uint256 payment)",
"event ExecutionFailure(bytes32 indexed txHash, uint256 payment)",
];
class MultisigManager {
constructor(contractAddress, provider, signers = []) {
this.contract = new ethers.Contract(contractAddress, multisigABI, provider);
this.signers = signers;
this.provider = provider;
}
async getMultisigInfo() {
const [owners, threshold, nonce] = await Promise.all([
this.contract.getOwners(),
this.contract.getThreshold(),
this.contract.nonce(),
]);
return {
owners,
threshold: threshold.toNumber(),
nonce: nonce.toNumber(),
contractAddress: this.contract.address,
};
}
async buildTransaction(to, value = 0, data = "0x", operation = 0) {
const info = await this.getMultisigInfo();
const safeTx = {
to,
value,
data,
operation,
safeTxGas: 0, // Let contract estimate
baseGas: 0,
gasPrice: 0,
gasToken: ethers.constants.AddressZero,
refundReceiver: ethers.constants.AddressZero,
nonce: info.nonce,
};
// Calculate transaction hash
const txHash = await this.calculateTransactionHash(safeTx);
return {
...safeTx,
hash: txHash,
};
}
async calculateTransactionHash(tx) {
const domain = {
verifyingContract: this.contract.address,
};
const types = {
SafeTx: [
{ name: "to", type: "address" },
{ name: "value", type: "uint256" },
{ name: "data", type: "bytes" },
{ name: "operation", type: "uint8" },
{ name: "safeTxGas", type: "uint256" },
{ name: "baseGas", type: "uint256" },
{ name: "gasPrice", type: "uint256" },
{ name: "gasToken", type: "address" },
{ name: "refundReceiver", type: "address" },
{ name: "nonce", type: "uint256" },
],
};
return ethers.utils._TypedDataEncoder.hash(domain, types, tx);
}
async signTransaction(tx, signerIndex = 0) {
if (!this.signers[signerIndex]) {
throw new Error(`Signer at index ${signerIndex} not found`);
}
const signature = await this.signers[signerIndex]._signTypedData(
{ verifyingContract: this.contract.address },
{
SafeTx: [
{ name: "to", type: "address" },
{ name: "value", type: "uint256" },
{ name: "data", type: "bytes" },
{ name: "operation", type: "uint8" },
{ name: "safeTxGas", type: "uint256" },
{ name: "baseGas", type: "uint256" },
{ name: "gasPrice", type: "uint256" },
{ name: "gasToken", type: "address" },
{ name: "refundReceiver", type: "address" },
{ name: "nonce", type: "uint256" },
],
},
tx
);
return {
signer: this.signers[signerIndex].address,
signature,
data: ethers.utils.joinSignature(signature),
};
}
async executeTransaction(tx, signatures = []) {
if (signatures.length === 0) {
throw new Error("At least one signature required");
}
// Combine signatures
const combinedSignatures = signatures.map(s => s.data).join("");
const contractWithSigner = this.contract.connect(this.signers[0]);
return await contractWithSigner.execTransaction(
tx.to,
tx.value,
tx.data,
tx.operation,
tx.safeTxGas,
tx.baseGas,
tx.gasPrice,
tx.gasToken,
tx.refundReceiver,
combinedSignatures
);
}
}
// ===== Batch Operations =====
console.log("\n=== BATCH OPERATIONS ===");
class BatchOperation {
constructor(provider) {
this.provider = provider;
this.transactions = [];
}
addTransfer(to, amount) {
this.transactions.push({
type: 'transfer',
to,
amount: ethers.utils.parseEther(amount.toString()),
});
return this;
}
addContractInteraction(contractAddress, abi, functionName, args = [], value = 0) {
const iface = new ethers.utils.Interface(abi);
const data = iface.encodeFunctionData(functionName, args);
this.transactions.push({
type: 'contract',
to: contractAddress,
data,
value: ethers.utils.parseEther(value.toString()),
});
return this;
}
async estimateGas(wallet) {
const estimates = [];
for (const tx of this.transactions) {
try {
if (tx.type === 'transfer') {
const gasLimit = await wallet.estimateGas({
to: tx.to,
value: tx.amount,
});
estimates.push({ ...tx, gasLimit });
} else {
const gasLimit = await wallet.estimateGas({
to: tx.to,
data: tx.data,
value: tx.value,
});
estimates.push({ ...tx, gasLimit });
}
} catch (error) {
console.error(`Gas estimation failed for transaction: ${error.message}`);
estimates.push({ ...tx, gasLimit: ethers.BigNumber.from(210000) });
}
}
return estimates;
}
async execute(wallet, options = {}) {
const gasPrice = options.gasPrice || await wallet.getGasPrice();
const estimates = await this.estimateGas(wallet);
const results = [];
for (const tx of estimates) {
try {
const txParams = {
to: tx.to,
value: tx.value,
gasLimit: tx.gasLimit,
gasPrice,
};
if (tx.data) {
txParams.data = tx.data;
}
const txResponse = await wallet.sendTransaction(txParams);
const receipt = await txResponse.wait();
results.push({
success: true,
hash: txResponse.hash,
receipt,
gasUsed: receipt.gasUsed,
});
} catch (error) {
results.push({
success: false,
error: error.message,
transaction: tx,
});
}
}
return results;
}
// Multicall alternative (using a multicall contract)
async executeMulticall(wallet, multicallAddress) {
const multicallABI = [
"function aggregate((address target, bytes callData)[] calls) returns (uint256 blockNumber, bytes[] returnData)",
];
const multicall = new ethers.Contract(multicallAddress, multicallABI, wallet);
const calls = [];
for (const tx of this.transactions) {
calls.push({
target: tx.to,
callData: tx.data || "0x",
});
}
try {
const result = await multicall.aggregate(calls);
return result;
} catch (error) {
console.error(`Multicall failed: ${error.message}`);
return null;
}
}
}
// ===== Gas Optimization =====
console.log("\n=== GAS OPTIMIZATION ===");
class GasOptimizer {
constructor(provider) {
this.provider = provider;
this.cache = new Map();
}
async getOptimizedGasPrice(wallet, options = {}) {
const {
speed = 'standard', // 'safe', 'standard', 'fast'
maxGasPrice = ethers.utils.parseUnits('100', 'gwei'),
multiplier = 1.1,
} = options;
let gasPrice;
if (this.provider.getFeeData) {
// EIP-1559 (Ethereum London fork and later)
const feeData = await this.provider.getFeeData();
if (feeData.maxFeePerGas && feeData.maxPriorityFeePerGas) {
const multipliers = {
safe: 0.9,
standard: 1.0,
fast: 1.2,
};
const m = multipliers[speed] || 1.0;
gasPrice = {
maxFeePerGas: feeData.maxFeePerGas.mul(multiplier).mul(m),
maxPriorityFeePerGas: feeData.maxPriorityFeePerGas.mul(multiplier).mul(m),
};
} else {
gasPrice = feeData.gasPrice || await wallet.getGasPrice();
}
} else {
gasPrice = await wallet.getGasPrice();
}
// Apply multiplier
if (typeof gasPrice === 'object') {
gasPrice.maxFeePerGas = ethers.BigNumber.from(gasPrice.maxFeePerGas).mul(multiplier).div(100);
gasPrice.maxPriorityFeePerGas = ethers.BigNumber.from(gasPrice.maxPriorityFeePerGas).mul(multiplier).div(100);
} else {
gasPrice = ethers.BigNumber.from(gasPrice).mul(multiplier).div(100);
}
// Cap maximum gas price
if (typeof gasPrice === 'object') {
if (gasPrice.maxFeePerGas.gt(maxGasPrice)) {
gasPrice.maxFeePerGas = maxGasPrice;
}
} else {
if (gasPrice.gt(maxGasPrice)) {
gasPrice = maxGasPrice;
}
}
return gasPrice;
}
async estimateAndOptimize(transaction, wallet, options = {}) {
const {
gasBuffer = 1.2, // 20% buffer
maxGasLimit = 8000000,
} = options;
try {
// Initial gas estimation
let gasEstimate = await wallet.estimateGas(transaction);
// Apply buffer
gasEstimate = gasEstimate.mul(Math.floor(gasBuffer * 100)).div(100);
// Cap maximum
if (gasEstimate.gt(maxGasLimit)) {
gasEstimate = ethers.BigNumber.from(maxGasLimit);
}
// Check if we can optimize by adjusting data
if (transaction.data) {
const optimizedData = this.optimizeCalldata(transaction.data);
if (optimizedData !== transaction.data) {
const optimizedTx = { ...transaction, data: optimizedData };
const optimizedGas = await wallet.estimateGas(optimizedTx);
if (optimizedGas.lt(gasEstimate)) {
return {
...transaction,
data: optimizedData,
gasLimit: optimizedGas,
originalGasLimit: gasEstimate,
savings: gasEstimate.sub(optimizedGas),
};
}
}
}
return {
...transaction,
gasLimit: gasEstimate,
};
} catch (error) {
console.error(`Gas estimation failed: ${error.message}`);
return {
...transaction,
gasLimit: ethers.BigNumber.from(500000), // Fallback
};
}
}
optimizeCalldata(calldata) {
// Remove leading zeros from calldata
if (calldata.startsWith('0x')) {
const hex = calldata.slice(2);
// Remove unnecessary padding zeros
const optimized = hex.replace(/0+$/, '');
return '0x' + optimized;
}
return calldata;
}
async simulateTransaction(transaction, blockNumber = 'latest') {
try {
return await this.provider.call(transaction, blockNumber);
} catch (error) {
return { error: error.message };
}
}
}
// ===== Advanced ABI Handling =====
console.log("\n=== ADVANCED ABI HANDLING =====");
class ABIManager {
constructor() {
this.interfaces = new Map();
this.caches = new Map();
}
loadInterface(name, abi) {
const iface = new ethers.utils.Interface(abi);
this.interfaces.set(name, iface);
return iface;
}
getInterface(name) {
return this.interfaces.get(name);
}
decodeLog(log, abi) {
const iface = typeof abi === 'string' ? this.getInterface(abi) : new ethers.utils.Interface(abi);
try {
return iface.parseLog(log);
} catch (error) {
console.warn(`Failed to decode log: ${error.message}`);
return null;
}
}
decodeTransactionResult(transaction, abi) {
const iface = typeof abi === 'string' ? this.getInterface(abi) : new ethers.utils.Interface(abi);
try {
return iface.decodeFunctionResult(transaction.data, transaction.value);
} catch (error) {
console.warn(`Failed to decode result: ${error.message}`);
return null;
}
}
encodeFunctionCall(contractName, functionName, args, abi) {
const iface = typeof abi === 'string' ? this.getInterface(abi) : new ethers.utils.Interface(abi);
try {
return iface.encodeFunctionData(functionName, args);
} catch (error) {
console.error(`Failed to encode function call: ${error.message}`);
return null;
}
}
// Dynamic ABI loading
async loadABIFromAddress(address, provider) {
const cacheKey = `abi_${address}`;
if (this.caches.has(cacheKey)) {
return this.caches.get(cacheKey);
}
try {
// This would typically use an API like Etherscan
// For example purposes, we'll return a mock ABI
const mockABI = [
"function name() view returns (string)",
"function symbol() view returns (string)",
];
const iface = new ethers.utils.Interface(mockABI);
this.caches.set(cacheKey, iface);
return iface;
} catch (error) {
console.error(`Failed to load ABI for ${address}: ${error.message}`);
return null;
}
}
}
// ===== Event Processing =====
console.log("\n=== EVENT PROCESSING ===");
class EventProcessor {
constructor(provider) {
this.provider = provider;
this.filters = new Map();
this.handlers = new Map();
}
addEventListener(contractAddress, eventName, handler, abi) {
const key = `${contractAddress}_${eventName}`;
if (!this.filters.has(key)) {
const contract = new ethers.Contract(contractAddress, abi, this.provider);
const filter = contract.filters[eventName]();
this.filters.set(key, { contract, filter });
}
if (!this.handlers.has(key)) {
this.handlers.set(key, []);
}
this.handlers.get(key).push(handler);
}
async startListening() {
for (const [key, { contract, filter }] of this.filters) {
contract.on(filter, (...args) => {
const handlers = this.handlers.get(key) || [];
handlers.forEach(handler => {
try {
handler(...args);
} catch (error) {
console.error(`Event handler error: ${error.message}`);
}
});
});
}
}
async getHistoricalEvents(contractAddress, eventName, fromBlock, toBlock, abi) {
const contract = new ethers.Contract(contractAddress, abi, this.provider);
const filter = contract.filters[eventName]();
try {
return await contract.queryFilter(filter, fromBlock, toBlock);
} catch (error) {
console.error(`Failed to get historical events: ${error.message}`);
return [];
}
}
// Event aggregation and analytics
async aggregateEvents(contractAddress, eventName, fromBlock, toBlock, abi, aggregationFn) {
const events = await this.getHistoricalEvents(contractAddress, eventName, fromBlock, toBlock, abi);
return aggregationFn(events);
}
}
// ===== Main Examples =====
async function advancedExamples() {
console.log("🚀 ADVANCED ETHERS.JS EXAMPLES");
try {
// Initialize provider (use your preferred provider)
const provider = new ethers.providers.JsonRpcProvider("https://eth-mainnet.alchemyapi.io/v2/YOUR-API-KEY");
// Example 1: Multisig Operations
console.log("\n1. Multisig Operations:");
const multisig = new MultisigManager(
"0x...YOUR_MULTISIG_ADDRESS...",
provider
);
// Example transaction to multisig
const tx = await multisig.buildTransaction(
"0x...RECIPIENT...",
ethers.utils.parseEther("0.1"),
"0x" // Optional calldata
);
console.log(" Multisig transaction built:", tx.hash);
// Example 2: Batch Operations
console.log("\n2. Batch Operations:");
const wallet = new ethers.Wallet("YOUR_PRIVATE_KEY", provider);
const batch = new BatchOperation(provider);
batch.addTransfer("0x...RECIPIENT1...", 0.1);
batch.addTransfer("0x...RECIPIENT2...", 0.05);
console.log(" Batch created with", batch.transactions.length, "transactions");
// Example 3: Gas Optimization
console.log("\n3. Gas Optimization:");
const gasOptimizer = new GasOptimizer(provider);
const optimizedTx = await gasOptimizer.estimateAndOptimize(
{
to: "0x...RECIPIENT...",
value: ethers.utils.parseEther("0.1"),
},
wallet,
{ speed: 'standard' }
);
console.log(" Optimized gas limit:", optimizedTx.gasLimit.toString());
// Example 4: Advanced ABI Handling
console.log("\n4. Advanced ABI Handling:");
const abiManager = new ABIManager();
const erc20ABI = [
"function name() view returns (string)",
"function symbol() view returns (string)",
"function balanceOf(address) view returns (uint256)",
];
const iface = abiManager.loadInterface("ERC20", erc20ABI);
console.log(" Loaded ERC20 interface with", Object.keys(iface.functions).length, "functions");
// Example 5: Event Processing
console.log("\n5. Event Processing:");
const eventProcessor = new EventProcessor(provider);
// Setup event listener (example)
eventProcessor.addEventListener(
"0x...CONTRACT_ADDRESS...",
"Transfer",
(from, to, value, event) => {
console.log(` Transfer: ${ethers.utils.formatEther(value)} from ${from} to ${to}`);
},
erc20ABI
);
console.log(" Event processor configured");
console.log("\n✅ Advanced examples completed successfully!");
} catch (error) {
console.error("\n❌ Error in advanced examples:", error.message);
}
}
// Utility function for testing
function testAdvancedFeatures() {
console.log("\n=== TESTING ADVANCED FEATURES ===");
// Test batch operations
const batch = new BatchOperation(null);
batch.addTransfer("0x1234...", 1.5);
batch.addTransfer("0x5678...", 2.3);
console.log("Batch operations test:", batch.transactions.length, "transactions");
// Test gas optimizer
const optimizer = new GasOptimizer(null);
const optimizedData = optimizer.optimizeCalldata("0x0000000000000000000000000000000000000000000000000000000000000001");
console.log("Calldata optimization:", optimizedData);
// Test ABI manager
const abiManager = new ABIManager();
const simpleABI = ["function test() view returns (string)"];
abiManager.loadInterface("Test", simpleABI);
console.log("ABI manager test:", abiManager.interfaces.size, "interfaces loaded");
}
// Export for use in other modules
module.exports = {
MultisigManager,
BatchOperation,
GasOptimizer,
ABIManager,
EventProcessor,
advancedExamples,
testAdvancedFeatures,
};
// Run examples if executed directly
if (require.main === module) {
advancedExamples();
testAdvancedFeatures();
}
console.log("\n📚 Advanced Ethers.js patterns implemented:");
console.log("✅ Multisig transaction management");
console.log("✅ Batch operations and multicall");
console.log("✅ Gas optimization strategies");
console.log("✅ Advanced ABI handling");
console.log("✅ Event processing and analytics");
console.log("✅ Error handling and recovery");
💻 Integración de dApp Ethers.js javascript
🟡 intermediate
⭐⭐⭐
Patrones completos de integración de dApp incluyendo conexión MetaMask, despliegue de contratos, integración UI y gestión de estado
⏱️ 30 min
🏷️ ethers, dapp, frontend, wallet
Prerequisites:
Ethers.js basics, Understanding of web apps, MetaMask or similar wallet
// Ethers.js dApp Integration
// Complete frontend integration with wallet providers, contracts, and UI
// ===== Wallet Connection Manager =====
class WalletConnectionManager {
constructor() {
this.provider = null;
this.signer = null;
this.account = null;
this.chainId = null;
this.listeners = new Map();
}
async connectWallet(walletType = 'metamask') {
try {
if (walletType === 'metamask') {
if (!window.ethereum) {
throw new Error('MetaMask is not installed');
}
// Request account access
await window.ethereum.request({ method: 'eth_requestAccounts' });
// Create provider
this.provider = new ethers.providers.Web3Provider(window.ethereum);
// Get signer
this.signer = this.provider.getSigner();
this.account = await this.signer.getAddress();
// Get network
const network = await this.provider.getNetwork();
this.chainId = network.chainId;
// Setup event listeners
this.setupEventListeners();
console.log(`✅ Connected to ${walletType}`);
console.log(` Account: ${this.account}`);
console.log(` Chain ID: ${this.chainId}`);
this.emit('connected', {
account: this.account,
chainId: this.chainId,
walletType,
});
return { account: this.account, chainId: this.chainId };
}
} catch (error) {
console.error(`❌ Failed to connect to ${walletType}:`, error.message);
this.emit('error', error);
throw error;
}
}
async disconnectWallet() {
try {
this.provider = null;
this.signer = null;
this.account = null;
this.chainId = null;
// Clear event listeners
this.clearEventListeners();
this.emit('disconnected');
console.log('✅ Wallet disconnected');
} catch (error) {
console.error('❌ Error disconnecting wallet:', error.message);
this.emit('error', error);
}
}
setupEventListeners() {
if (!window.ethereum) return;
// Account change
window.ethereum.on('accountsChanged', (accounts) => {
if (accounts.length === 0) {
this.disconnectWallet();
} else {
this.account = accounts[0];
this.emit('accountChanged', this.account);
}
});
// Chain change
window.ethereum.on('chainChanged', (chainId) => {
this.chainId = parseInt(chainId, 16);
this.emit('chainChanged', this.chainId);
});
// Connect
window.ethereum.on('connect', (connectInfo) => {
this.chainId = parseInt(connectInfo.chainId, 16);
this.emit('connected', { chainId: this.chainId });
});
// Disconnect
window.ethereum.on('disconnect', (error) => {
this.emit('disconnected', error);
});
}
clearEventListeners() {
if (!window.ethereum) return;
// Remove all listeners (MetaMask doesn't expose individual removal)
window.ethereum.removeAllListeners();
}
// Event emitter methods
on(event, callback) {
if (!this.listeners.has(event)) {
this.listeners.set(event, []);
}
this.listeners.get(event).push(callback);
}
off(event, callback) {
if (this.listeners.has(event)) {
const callbacks = this.listeners.get(event);
const index = callbacks.indexOf(callback);
if (index > -1) {
callbacks.splice(index, 1);
}
}
}
emit(event, data) {
if (this.listeners.has(event)) {
this.listeners.get(event).forEach(callback => {
try {
callback(data);
} catch (error) {
console.error(`Event listener error for ${event}:`, error);
}
});
}
}
async switchNetwork(chainId) {
try {
await window.ethereum.request({
method: 'wallet_switchEthereumChain',
params: [{ chainId: `0x${chainId.toString(16)}` }],
});
} catch (error) {
// This error code indicates that the chain has not been added to MetaMask
if (error.code === 4902) {
await this.addNetwork(chainId);
} else {
throw error;
}
}
}
async addNetwork(chainId) {
const networks = {
1: {
chainId: '0x1',
chainName: 'Ethereum Mainnet',
rpcUrls: ['https://mainnet.infura.io/v3/YOUR-PROJECT-ID'],
nativeCurrency: { name: 'ETH', symbol: 'ETH', decimals: 18 },
},
5: {
chainId: '0x5',
chainName: 'Goerli Testnet',
rpcUrls: ['https://goerli.infura.io/v3/YOUR-PROJECT-ID'],
nativeCurrency: { name: 'ETH', symbol: 'ETH', decimals: 18 },
},
// Add more networks as needed
};
const networkConfig = networks[chainId];
if (!networkConfig) {
throw new Error(`Network configuration not found for chainId ${chainId}`);
}
await window.ethereum.request({
method: 'wallet_addEthereumChain',
params: [networkConfig],
});
}
async signMessage(message) {
if (!this.signer) {
throw new Error('Wallet not connected');
}
try {
const signature = await this.signer.signMessage(message);
return signature;
} catch (error) {
console.error('❌ Failed to sign message:', error.message);
throw error;
}
}
}
// ===== Contract Manager =====
class ContractManager {
constructor(walletManager) {
this.walletManager = walletManager;
this.contracts = new Map();
this.deployedContracts = new Map();
}
loadContract(name, address, abi) {
if (!this.walletManager.signer) {
throw new Error('Wallet not connected');
}
const contract = new ethers.Contract(address, abi, this.walletManager.signer);
this.contracts.set(name, contract);
console.log(`✅ Contract ${name} loaded at ${address}`);
return contract;
}
getContract(name) {
const contract = this.contracts.get(name);
if (!contract) {
throw new Error(`Contract ${name} not found`);
}
return contract;
}
async deployContract(name, factoryClass, constructorArgs = []) {
if (!this.walletManager.signer) {
throw new Error('Wallet not connected');
}
try {
console.log(`🚀 Deploying ${name}...`);
const factory = new factoryClass();
const contract = await factory.deploy(...constructorArgs);
console.log(`⏳ Waiting for deployment...`);
await contract.deployed();
console.log(`✅ ${name} deployed at: ${contract.address}`);
this.contracts.set(name, contract);
this.deployedContracts.set(name, {
address: contract.address,
transactionHash: contract.deployTransaction.hash,
gasUsed: contract.deployTransaction.gasLimit,
});
this.walletManager.emit('contractDeployed', {
name,
address: contract.address,
transactionHash: contract.deployTransaction.hash,
});
return contract;
} catch (error) {
console.error(`❌ Failed to deploy ${name}:`, error.message);
this.walletManager.emit('error', error);
throw error;
}
}
async callContractMethod(contractName, methodName, args = [], options = {}) {
try {
const contract = this.getContract(contractName);
console.log(`📞 Calling ${contractName}.${methodName} with args:`, args);
const gasEstimate = await contract.estimateGas[methodName](...args);
const gasPrice = await this.walletManager.provider.getGasPrice();
const txOptions = {
gasLimit: gasEstimate.mul(120).div(100), // 20% buffer
gasPrice,
...options,
};
const tx = await contract[methodName](...args, txOptions);
console.log(`⏳ Transaction sent: ${tx.hash}`);
const receipt = await tx.wait();
console.log(`✅ Transaction confirmed in block: ${receipt.blockNumber}`);
this.walletManager.emit('transactionConfirmed', {
contractName,
methodName,
transactionHash: tx.hash,
blockNumber: receipt.blockNumber,
gasUsed: receipt.gasUsed,
});
return { tx, receipt };
} catch (error) {
console.error(`❌ Failed to call ${contractName}.${methodName}:`, error.message);
this.walletManager.emit('error', error);
throw error;
}
}
async readContractMethod(contractName, methodName, args = []) {
try {
const contract = this.getContract(contractName);
const result = await contract[methodName](...args);
console.log(`📖 Read ${contractName}.${methodName}:`, result);
return result;
} catch (error) {
console.error(`❌ Failed to read ${contractName}.${methodName}:`, error.message);
throw error;
}
}
setupContractEventListener(contractName, eventName, callback) {
const contract = this.getContract(contractName);
contract.on(eventName, (...args) => {
console.log(`🎯 ${contractName} event: ${eventName}`, args);
callback(...args);
});
console.log(`👂 Listening for ${eventName} events on ${contractName}`);
}
}
// ===== UI State Manager =====
class UIStateManager {
constructor() {
this.state = {
walletConnected: false,
account: null,
chainId: null,
balance: '0',
contracts: {},
loading: false,
error: null,
};
this.subscribers = [];
}
setState(updates) {
this.state = { ...this.state, ...updates };
this.notifySubscribers();
}
getState() {
return this.state;
}
subscribe(callback) {
this.subscribers.push(callback);
callback(this.state); // Send current state immediately
// Return unsubscribe function
return () => {
const index = this.subscribers.indexOf(callback);
if (index > -1) {
this.subscribers.splice(index, 1);
}
};
}
notifySubscribers() {
this.subscribers.forEach(callback => {
try {
callback(this.state);
} catch (error) {
console.error('State subscriber error:', error);
}
});
}
async updateBalance(provider, account) {
try {
const balance = await provider.getBalance(account);
this.setState({ balance: ethers.utils.formatEther(balance) });
} catch (error) {
console.error('Failed to update balance:', error.message);
}
}
}
// ===== dApp Component Examples =====
// React-like component structure
class WalletConnectComponent {
constructor(walletManager, stateManager) {
this.walletManager = walletManager;
this.stateManager = stateManager;
this.element = null;
this.unsubscribe = null;
}
mount(elementId) {
this.element = document.getElementById(elementId);
if (!this.element) {
throw new Error(`Element ${elementId} not found`);
}
this.unsubscribe = this.stateManager.subscribe((state) => {
this.render(state);
});
this.render(this.stateManager.getState());
// Setup event listeners
this.walletManager.on('connected', (data) => {
this.stateManager.setState({
walletConnected: true,
account: data.account,
chainId: data.chainId,
});
});
this.walletManager.on('disconnected', () => {
this.stateManager.setState({
walletConnected: false,
account: null,
chainId: null,
balance: '0',
});
});
}
render(state) {
if (!this.element) return;
if (state.walletConnected) {
this.element.innerHTML = `
<div class="wallet-connected">
<div class="account-info">
<span class="account">${this.formatAddress(state.account)}</span>
<span class="balance">${parseFloat(state.balance).toFixed(4)} ETH</span>
<span class="chain">Chain: ${state.chainId}</span>
</div>
<button onclick="window.dApp.disconnectWallet()">Disconnect</button>
</div>
`;
} else {
this.element.innerHTML = `
<div class="wallet-disconnected">
<button onclick="window.dApp.connectWallet()">Connect Wallet</button>
</div>
`;
}
}
formatAddress(address) {
if (!address) return '';
return `${address.slice(0, 6)}...${address.slice(-4)}`;
}
unmount() {
if (this.unsubscribe) {
this.unsubscribe();
}
}
}
// Contract Interaction Component
class ContractInteractionComponent {
constructor(contractManager, stateManager) {
this.contractManager = contractManager;
this.stateManager = stateManager;
this.element = null;
this.unsubscribe = null;
}
mount(elementId) {
this.element = document.getElementById(elementId);
if (!this.element) {
throw new Error(`Element ${elementId} not found`);
}
this.unsubscribe = this.stateManager.subscribe((state) => {
this.render(state);
});
this.render(this.stateManager.getState());
}
render(state) {
if (!this.element) return;
if (!state.walletConnected) {
this.element.innerHTML = '<p>Please connect your wallet to interact with contracts</p>';
return;
}
this.element.innerHTML = `
<div class="contract-interaction">
<h3>Contract Interaction</h3>
<div class="form-group">
<label>Value to Store:</label>
<input type="number" id="valueInput" placeholder="Enter value" />
</div>
<div class="form-group">
<label>Message:</label>
<input type="text" id="messageInput" placeholder="Enter message" />
</div>
<button onclick="window.dApp.storeValue()">Store Value</button>
<button onclick="window.dApp.retrieveValue()">Retrieve Value</button>
<div id="contractResult"></div>
</div>
`;
}
async storeValue() {
const valueInput = document.getElementById('valueInput');
const messageInput = document.getElementById('messageInput');
const resultDiv = document.getElementById('contractResult');
try {
const value = parseInt(valueInput.value);
const message = messageInput.value;
this.stateManager.setState({ loading: true });
const { tx, receipt } = await this.contractManager.callContractMethod(
'SimpleStorage',
'store',
[value, message]
);
resultDiv.innerHTML = `
<div class="success">
<h4>✅ Transaction Confirmed</h4>
<p>Hash: ${tx.hash}</p>
<p>Block: ${receipt.blockNumber}</p>
<p>Gas Used: ${receipt.gasUsed.toString()}</p>
</div>
`;
} catch (error) {
resultDiv.innerHTML = `
<div class="error">
<h4>❌ Error</h4>
<p>${error.message}</p>
</div>
`;
} finally {
this.stateManager.setState({ loading: false });
}
}
async retrieveValue() {
const resultDiv = document.getElementById('contractResult');
try {
this.stateManager.setState({ loading: true });
const result = await this.contractManager.readContractMethod(
'SimpleStorage',
'retrieve'
);
resultDiv.innerHTML = `
<div class="result">
<h4>📖 Retrieved Value</h4>
<p>Value: ${result[0].toString()}</p>
<p>Message: ${result[1]}</p>
</div>
`;
} catch (error) {
resultDiv.innerHTML = `
<div class="error">
<h4>❌ Error</h4>
<p>${error.message}</p>
</div>
`;
} finally {
this.stateManager.setState({ loading: false });
}
}
}
// ===== Main dApp Class =====
class DApp {
constructor() {
this.walletManager = new WalletConnectionManager();
this.stateManager = new UIStateManager();
this.contractManager = new ContractManager(this.walletManager);
this.components = {};
// Make dApp globally available for button clicks
window.dApp = this;
}
async initialize() {
console.log('🚀 Initializing dApp...');
try {
// Initialize components
this.components.walletConnect = new WalletConnectComponent(
this.walletManager,
this.stateManager
);
this.components.contractInteraction = new ContractInteractionComponent(
this.contractManager,
this.stateManager
);
// Mount components
this.components.walletConnect.mount('wallet-connect');
this.components.contractInteraction.mount('contract-interaction');
// Setup error handling
this.walletManager.on('error', (error) => {
this.stateManager.setState({ error: error.message });
console.error('dApp error:', error);
});
// Try to auto-connect if previously connected
if (window.ethereum && window.ethereum.selectedAddress) {
await this.connectWallet();
}
console.log('✅ dApp initialized successfully');
} catch (error) {
console.error('❌ Failed to initialize dApp:', error.message);
}
}
async connectWallet() {
try {
await this.walletManager.connectWallet('metamask');
// Update balance
await this.stateManager.updateBalance(
this.walletManager.provider,
this.walletManager.account
);
// Load example contract
const simpleStorageABI = [
"function store(uint256 value, string memory message)",
"function retrieve() view returns (uint256, string memory)",
];
// Example contract address (replace with your deployed contract)
const contractAddress = "0x...YOUR_CONTRACT_ADDRESS...";
this.contractManager.loadContract('SimpleStorage', contractAddress, simpleStorageABI);
} catch (error) {
console.error('Failed to connect wallet:', error);
}
}
async disconnectWallet() {
try {
await this.walletManager.disconnectWallet();
} catch (error) {
console.error('Failed to disconnect wallet:', error);
}
}
// Delegate methods for component interaction
async storeValue() {
return this.components.contractInteraction.storeValue();
}
async retrieveValue() {
return this.components.contractInteraction.retrieveValue();
}
}
// ===== HTML Template =====
const htmlTemplate = `
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Ethers.js dApp</title>
<style>
body {
font-family: Arial, sans-serif;
max-width: 800px;
margin: 0 auto;
padding: 20px;
}
.wallet-connected {
display: flex;
justify-content: space-between;
align-items: center;
background: #f0f0f0;
padding: 10px;
border-radius: 5px;
}
.account-info {
display: flex;
gap: 20px;
}
button {
background: #007bff;
color: white;
border: none;
padding: 10px 20px;
border-radius: 5px;
cursor: pointer;
}
button:hover {
background: #0056b3;
}
.contract-interaction {
margin-top: 20px;
padding: 20px;
border: 1px solid #ddd;
border-radius: 5px;
}
.form-group {
margin-bottom: 15px;
}
.form-group label {
display: block;
margin-bottom: 5px;
}
.form-group input {
width: 100%;
padding: 8px;
border: 1px solid #ddd;
border-radius: 3px;
}
.success {
background: #d4edda;
color: #155724;
padding: 10px;
border-radius: 3px;
margin-top: 10px;
}
.error {
background: #f8d7da;
color: #721c24;
padding: 10px;
border-radius: 3px;
margin-top: 10px;
}
.result {
background: #e2e3e5;
padding: 10px;
border-radius: 3px;
margin-top: 10px;
}
</style>
</head>
<body>
<h1>Ethers.js dApp Example</h1>
<div id="wallet-connect"></div>
<div id="contract-interaction"></div>
<script src="dapp.js"></script>
<script>
// Initialize dApp when page loads
document.addEventListener('DOMContentLoaded', () => {
const dapp = new DApp();
dapp.initialize();
});
</script>
</body>
</html>
`;
console.log("🌐 Ethers.js dApp integration ready!");
console.log("\n📋 Features implemented:");
console.log("✅ Wallet connection management");
console.log("✅ Contract interaction");
console.log("✅ State management");
console.log("✅ UI components");
console.log("✅ Event handling");
console.log("✅ Error handling");
console.log("✅ Transaction management");
console.log("\n📁 To use this dApp:");
console.log("1. Save the HTML template as index.html");
console.log("2. Save this JavaScript as dapp.js");
console.log("3. Open index.html in your browser");
console.log("4. Install MetaMask and connect");
console.log("5. Deploy your own contracts and update addresses");
// Export for use in other modules
module.exports = {
WalletConnectionManager,
ContractManager,
UIStateManager,
WalletConnectComponent,
ContractInteractionComponent,
DApp,
htmlTemplate,
};
// Auto-initialize if loaded in browser
if (typeof window !== 'undefined') {
window.DApp = DApp;
window.WalletConnectionManager = WalletConnectionManager;
window.ContractManager = ContractManager;
}