Web3.js/Ethers.js使用
Web3.js和Ethers.js是开发DApp时与区块链交互的两个核心JavaScript库。本章将详细介绍这两个库的安装、基本使用方法、钱包连接和账户管理功能。
Web3.js基础
概述
Web3.js是以太坊官方的JavaScript库,用于与以太坊区块链进行交互。它允许开发者发送交易、读取合约数据、监听事件等。
安装
# 使用npm安装
npm install web3
# 或使用yarn安装
yarn add web3
初始化
要使用Web3.js,首先需要创建一个Web3实例并连接到以太坊网络:
import Web3 from 'web3';
// 检查是否有已注入的web3实例(如MetaMask)
if (window.ethereum) {
// 使用现代dapp浏览器
const web3 = new Web3(window.ethereum);
try {
// 请求用户授权连接
await window.ethereum.enable();
console.log('MetaMask已连接');
} catch (error) {
console.error('用户拒绝了连接请求');
}
} else if (window.web3) {
// 使用旧版dapp浏览器
const web3 = new Web3(window.web3.currentProvider);
console.log('已连接到旧版钱包');
} else {
// 如果没有钱包,连接到公共节点
const provider = new Web3.providers.HttpProvider('https://mainnet.infura.io/v3/YOUR_INFURA_PROJECT_ID');
const web3 = new Web3(provider);
console.log('已连接到公共节点');
}
获取基本信息
使用Web3.js获取区块链和账户的基本信息:
// 获取当前网络ID
const networkId = await web3.eth.net.getId();
console.log('当前网络ID:', networkId);
// 获取最新区块号
const blockNumber = await web3.eth.getBlockNumber();
console.log('最新区块号:', blockNumber);
// 获取账户列表
const accounts = await web3.eth.getAccounts();
console.log('账户列表:', accounts);
// 获取账户余额
const balance = await web3.eth.getBalance(accounts[0]);
// 将wei转换为ether
const balanceInEther = web3.utils.fromWei(balance, 'ether');
console.log('账户余额:', balanceInEther, 'ETH');
Ethers.js基础
概述
Ethers.js是一个轻量级、模块化的JavaScript库,提供了与以太坊区块链交互的全面功能。它的API设计更加现代化,性能也优于Web3.js。
安装
# 使用npm安装
npm install ethers
# 或使用yarn安装
yarn add ethers
初始化
创建Ethers.js实例并连接到以太坊网络:
import { ethers } from 'ethers';
// 检查是否有已注入的提供者(如MetaMask)
if (window.ethereum) {
// 创建Web3Provider实例
const provider = new ethers.providers.Web3Provider(window.ethereum);
try {
// 请求用户授权连接
await window.ethereum.request({ method: 'eth_requestAccounts' });
console.log('MetaMask已连接');
} catch (error) {
console.error('用户拒绝了连接请求');
}
} else {
// 连接到公共节点
const provider = new ethers.providers.JsonRpcProvider('https://mainnet.infura.io/v3/YOUR_INFURA_PROJECT_ID');
console.log('已连接到公共节点');
}
获取基本信息
使用Ethers.js获取区块链和账户的基本信息:
// 获取当前网络信息
const network = await provider.getNetwork();
console.log('当前网络名称:', network.name);
console.log('当前网络ID:', network.chainId);
// 获取最新区块号
const blockNumber = await provider.getBlockNumber();
console.log('最新区块号:', blockNumber);
// 获取签名者(连接的账户)
const signer = provider.getSigner();
// 获取账户地址
const address = await signer.getAddress();
console.log('账户地址:', address);
// 获取账户余额
const balance = await provider.getBalance(address);
// 将wei转换为ether
const balanceInEther = ethers.utils.formatEther(balance);
console.log('账户余额:', balanceInEther, 'ETH');
钱包连接
钱包类型
在Web3开发中,常见的钱包类型包括:
- 浏览器扩展钱包:如MetaMask、Coinbase Wallet
- 移动钱包:如Trust Wallet、Rainbow
- 硬件钱包:如Ledger、Trezor
- 桌面钱包:如Exodus、Electrum
检测钱包
在连接钱包之前,首先需要检测用户是否安装了支持Web3的钱包:
// 检测是否安装了MetaMask
function detectMetaMask() {
if (typeof window.ethereum !== 'undefined') {
console.log('MetaMask已安装');
return true;
} else {
console.log('未检测到MetaMask');
return false;
}
}
// 检测多种钱包
function detectWallet() {
if (typeof window.ethereum !== 'undefined') {
return 'MetaMask或其他现代钱包';
} else if (typeof window.web3 !== 'undefined') {
return '旧版Web3钱包';
} else {
return '未检测到钱包';
}
}
连接MetaMask钱包
使用Web3.js连接MetaMask钱包:
// Web3.js连接MetaMask
async function connectWalletWithWeb3() {
try {
if (typeof window.ethereum !== 'undefined') {
// 请求用户授权
await window.ethereum.request({ method: 'eth_requestAccounts' });
// 创建Web3实例
const web3 = new Web3(window.ethereum);
// 获取账户
const accounts = await web3.eth.getAccounts();
console.log('已连接账户:', accounts[0]);
return {
web3,
account: accounts[0]
};
} else {
throw new Error('请安装MetaMask钱包');
}
} catch (error) {
console.error('连接钱包失败:', error);
throw error;
}
}
使用Ethers.js连接MetaMask钱包:
// Ethers.js连接MetaMask
async function connectWalletWithEthers() {
try {
if (typeof window.ethereum !== 'undefined') {
// 请求用户授权
await window.ethereum.request({ method: 'eth_requestAccounts' });
// 创建提供者和签名者
const provider = new ethers.providers.Web3Provider(window.ethereum);
const signer = provider.getSigner();
// 获取账户地址
const address = await signer.getAddress();
console.log('已连接账户:', address);
return {
provider,
signer,
address
};
} else {
throw new Error('请安装MetaMask钱包');
}
} catch (error) {
console.error('连接钱包失败:', error);
throw error;
}
}
监听账户和网络变化
当用户切换账户或网络时,应用应该能够响应这些变化:
// 监听账户变化
window.ethereum.on('accountsChanged', (accounts) => {
console.log('账户已切换:', accounts[0] || '未连接');
// 在这里更新应用状态
if (accounts.length > 0) {
// 用户已连接新账户
updateUIWithNewAccount(accounts[0]);
} else {
// 用户已断开连接
handleDisconnect();
}
});
// 监听网络变化
window.ethereum.on('chainChanged', (chainId) => {
console.log('网络已切换:', chainId);
// 通常需要刷新页面以使用新网络
window.location.reload();
});
// 监听连接状态
window.ethereum.on('connect', (connectInfo) => {
console.log('已连接到网络:', connectInfo.chainId);
});
window.ethereum.on('disconnect', (error) => {
console.log('已断开连接:', error);
handleDisconnect();
});
账户管理
获取账户信息
获取当前连接账户的详细信息:
// Web3.js获取账户信息
async function getAccountInfoWithWeb3(web3) {
const accounts = await web3.eth.getAccounts();
if (accounts.length === 0) {
throw new Error('未连接账户');
}
const address = accounts[0];
const balance = await web3.eth.getBalance(address);
const balanceInEther = web3.utils.fromWei(balance, 'ether');
return {
address,
balance: balanceInEther
};
}
// Ethers.js获取账户信息
async function getAccountInfoWithEthers(provider) {
const signer = provider.getSigner();
const address = await signer.getAddress();
const balance = await provider.getBalance(address);
const balanceInEther = ethers.utils.formatEther(balance);
return {
address,
balance: balanceInEther
};
}
发送交易
使用Web3.js发送交易:
// Web3.js发送ETH交易
async function sendTransactionWithWeb3(web3, fromAddress, toAddress, amountInEther) {
try {
// 估算Gas价格
const gasPrice = await web3.eth.getGasPrice();
// 估算Gas限制
const gasLimit = await web3.eth.estimateGas({
from: fromAddress,
to: toAddress,
value: web3.utils.toWei(amountInEther, 'ether')
});
// 发送交易
const tx = await web3.eth.sendTransaction({
from: fromAddress,
to: toAddress,
value: web3.utils.toWei(amountInEther, 'ether'),
gas: gasLimit,
gasPrice: gasPrice
});
console.log('交易成功:', tx.transactionHash);
return tx.transactionHash;
} catch (error) {
console.error('发送交易失败:', error);
throw error;
}
}
使用Ethers.js发送交易:
// Ethers.js发送ETH交易
async function sendTransactionWithEthers(signer, toAddress, amountInEther) {
try {
// 发送交易
const tx = await signer.sendTransaction({
to: toAddress,
value: ethers.utils.parseEther(amountInEther)
});
console.log('交易已发送,等待确认:', tx.hash);
// 等待交易确认(可选择等待更多确认)
const receipt = await tx.wait(1);
console.log('交易已确认:', receipt.transactionHash);
return receipt.transactionHash;
} catch (error) {
console.error('发送交易失败:', error);
throw error;
}
}
处理交易历史
获取账户的交易历史:
// 使用Web3.js和第三方服务获取交易历史
async function getTransactionHistory(address, apiKey) {
try {
// 使用Etherscan API获取交易历史
const response = await fetch(`https://api.etherscan.io/api?module=account&action=txlist&address=${address}&startblock=0&endblock=99999999&sort=asc&apikey=${apiKey}`);
const data = await response.json();
if (data.status === '1') {
return data.result;
} else {
throw new Error('获取交易历史失败:', data.message);
}
} catch (error) {
console.error('获取交易历史出错:', error);
throw error;
}
}
// 处理并显示交易历史
function processTransactionHistory(transactions) {
return transactions.map(tx => ({
hash: tx.hash,
from: tx.from,
to: tx.to,
value: web3.utils.fromWei(tx.value, 'ether'),
gas: tx.gas,
gasPrice: web3.utils.fromWei(tx.gasPrice, 'gwei'),
timeStamp: new Date(tx.timeStamp * 1000).toLocaleString(),
status: tx.isError === '0' ? '成功' : '失败'
}));
}
Web3.js vs Ethers.js比较
| 特性 | Web3.js | Ethers.js |
|---|---|---|
| 包大小 | 较大 | 较小 |
| API设计 | 传统回调风格 | 现代Promise风格 |
| 性能 | 较低 | 较高 |
| 文档质量 | 一般 | 优秀 |
| 维护活跃度 | 较低 | 较高 |
| 社区支持 | 较大 | 快速增长 |
| 功能完整性 | 完整 | 完整 |
最佳实践
错误处理
在与区块链交互时,应该实现全面的错误处理:
// 通用错误处理函数
function handleBlockchainError(error) {
console.error('区块链交互错误:', error);
if (error.code === 4001) {
// 用户拒绝了操作
return '您已拒绝了该操作';
} else if (error.code === 'INSUFFICIENT_FUNDS') {
// 余额不足
return '账户余额不足,请确保您有足够的资金支付交易费用';
} else if (error.message && error.message.includes('gas')) {
// Gas相关错误
return 'Gas设置不足或Gas价格过低,请尝试增加Gas限制或价格';
} else if (error.message && error.message.includes('Network error')) {
// 网络错误
return '网络连接错误,请检查您的网络连接';
} else {
// 其他错误
return '操作失败,请稍后再试';
}
}
性能优化
优化Web3应用的性能:
- 缓存频繁调用的数据
- 批量处理交易
- 使用事件监听而非轮询
- 合理设置Gas价格和限制
- 利用链下计算减轻链上负担
安全性考虑
在使用Web3.js和Ethers.js时,应注意以下安全事项:
- 永远不要在前端暴露私钥
- 验证所有用户输入
- 使用官方或可信的RPC节点
- 实现超时和重试机制
- 定期更新库版本以修复已知漏洞
总结
Web3.js和Ethers.js是DApp开发中与区块链交互的两个核心库。Web3.js作为以太坊官方库,功能全面但体积较大;Ethers.js则以其轻量级、现代化的API和高性能受到越来越多开发者的青睐。
在实际开发中,选择哪个库取决于项目需求、团队熟悉度和性能考量。无论选择哪个库,理解区块链交互的基本原理、掌握钱包连接和账户管理的方法,以及遵循最佳实践,都是构建高质量DApp的关键。