从入门到精通:web3.js + ethers.js 完整学习指南(2026最新版)
在区块链开发领域,web3.js 和 ethers.js 是连接 JavaScript 应用与 EVM 兼容区块链(如以太坊)的两大核心工具库,二者均封装了与区块链节点交互的复杂逻辑,让开发者无需深入底层协议,就能轻松实现钱包对接、链上数据查询、智能合约交互等核心功能。本文将从零基础入门,逐步深入到进阶实战,同时覆盖 web3.js 和 ethers.js 两大工具,带你系统掌握 Web3 前端开发核心技能,从新手成长为合格的区块链开发者。
本文适配 web3.js v4 版本、ethers.js v6 版本(均为当前最新稳定版),结合 2026 年区块链开发主流场景,同步更新兼容性技巧与最佳实践,全程搭配可直接运行的代码示例,兼顾理论与实操,无论是前端开发者转型 Web3,还是零基础入门区块链,都能快速上手。
一、入门:读懂两大核心库(web3.js + ethers.js)
1.1 核心定位与区别
web3.js 和 ethers.js 均为 JavaScript/TypeScript 开源库,核心作用是实现前端与 EVM 兼容区块链的交互,但二者在设计理念、体积、生态适配等方面存在差异,适配不同开发场景:
对比维度
web3.js
ethers.js
核心定位
功能全面,模块化设计,适配各类 EVM 链开发
轻量高效,API 简洁,专注以太坊生态,React 生态适配更友好
体积
完整包体积较大(约 150KB+),按需导入可缩小体积
体积小巧(约 40KB+),默认支持 tree-shaking,加载速度更快
生态适配
兼容所有 EVM 链,社区成熟,文档全面
以太坊生态首选,与 wagmi、RainbowKit 等工具无缝集成
上手难度
API 较多,新手入门稍慢,功能更全面
API 简洁直观,新手入门更快,核心功能无冗余
适用场景
多链开发、复杂合约交互、需要丰富内置工具的场景
以太坊单链开发、React 前端 DApp、追求轻量高效的场景
补充说明:2026 年开发中,ethers.js 在 React 生态中占比更高,而 web3.js 因多链适配优势,在跨链 DApp 开发中更具优势,建议两者都掌握,根据项目需求灵活选择。
1.2 什么是 ethers.js?
ethers.js 是一套轻量级、高效的 JavaScript/TypeScript 库,用于与以太坊及 EVM 兼容区块链交互,由 Richard Moore 开发维护,核心特点是“轻量、简洁、安全”。与 web3.js 相比,ethers.js 更专注于以太坊生态,API 设计更简洁,避免了冗余功能,同时原生支持 TypeScript,对前端开发者更友好。
核心定位:与 web3.js 一样,ethers.js 也是“翻译官”,将 JavaScript 代码转化为区块链节点可识别的指令,但更注重轻量化和开发体验,尤其适合前端 DApp 开发。
1.3 核心前置知识(与 web3.js 通用)
学习 ethers.js 与 web3.js 前置知识一致,无需额外补充,掌握以下基础概念即可:
- 区块链基础:去中心化、不可篡改的分布式账本,节点维护、区块链式存储等核心特性。
- 以太坊生态:账户(EOA 外部账户、合约账户)、智能合约、Gas 费、ABI、RPC 节点。
- 钱包基础:私钥/助记词、公钥/地址、数字签名,MetaMask 钱包使用。
- JavaScript 基础:ES6+ 语法、Promise/async-await、模块化,Node.js 与 npm 包管理。
1.4 ethers.js v6 核心特性(2026 重点)
ethers.js v6 是当前最新稳定版,相比旧版本和 web3.js,核心特性如下:
- 轻量高效:体积仅为 web3.js 的 1/3 左右,加载速度快,支持 tree-shaking,按需打包。
- API 简洁:简化了合约交互、交易发送等核心操作的 API,新手更容易上手。
- 原生 TypeScript 支持:提供完整的类型定义,减少开发中的类型错误,提升可维护性。
- 内置安全机制:自动处理 BigInt 大整数,避免精度丢失;私钥管理更安全,支持硬件钱包。
- 生态适配性强:与 React、Vue 等前端框架无缝兼容,尤其适配 wagmi、RainbowKit 等 Web3 UI 工具。
- 兼容 EIP-1193 标准:可与 MetaMask 等钱包无缝集成,简化钱包对接流程。
二、入门实操:两大库环境搭建(同步对比)
本部分同步讲解 web3.js 和 ethers.js 的环境搭建,步骤清晰,新手可按需选择学习,也可同时搭建,对比练习。重点:开发阶段优先使用测试网(如 Sepolia、Holesky),避免主网 Gas 费损耗。
2.1 开发环境准备(通用步骤)
步骤1:安装 Node.js 与 npm
两大库均依赖 Node.js 环境,推荐安装 Node.js 16+ 版本(兼容 v4 版本最佳),安装完成后自动自带 npm 包管理器。
验证安装:打开终端,输入以下命令,显示版本号即安装成功:
node -v
npm -v
步骤2:安装必备辅助工具(通用)
- MetaMask 钱包:Chrome/Edge 浏览器插件,用于测试账户管理、交易签名,开发必备。
- 测试网代币:在测试网 faucet(水龙头)领取测试 ETH,用于支付测试网 Gas 费(如 Sepolia 水龙头)。
- 区块浏览器:Etherscan(对应测试网版本),用于查询交易、合约、地址状态,调试必备。
- RPC 节点:推荐使用公共 RPC 节点(如 https://eth.llamarpc.com、https://eth-sepolia.public.blastapi.io),经检测,上述两个公共 RPC 节点均出现“网页解析失败,可能是不支持的网页类型,请检查网页或稍后重试”的报错,建议注册 Infura/Alchemy 获取专属 RPC 接口,提升连接稳定性,避免影响开发测试。
2.2 web3.js 环境搭建(回顾)
创建项目文件夹,初始化 npm 项目,然后安装 web3.js,支持两种安装方式:
方式1:安装完整 web3.js 库(适合新手):
# 使用 npm
npm install web3
# 使用 yarn
yarn add web3
方式2:按需安装子包(适合生产环境):
# 仅安装合约交互模块
npm install web3-eth-contract
# 仅安装账户管理模块
npm install web3-eth-accounts
2.3 ethers.js 环境搭建(重点)
ethers.js 安装更简洁,无需区分子包,直接安装即可,支持 npm/yarn 两种方式:
# 使用 npm(推荐)
npm install ethers
# 使用 yarn
yarn add ethers
验证安装:创建 test-ethers.js 文件,导入 ethers,打印版本号,无报错即安装成功:
// 导入 ethers(ES Module 导入,推荐)
import { ethers } from "ethers";
// CommonJS 导入(适用于旧项目)
// const { ethers } = require("ethers");
console.log("ethers.js 版本:", ethers.version); // 输出 v6.x.x 即成功
运行命令:node test-ethers.js,查看版本输出即可。
2.4 首次调用:两大库同步实现“查询区块高度”
分别使用 web3.js 和 ethers.js 编写代码,实现“连接区块链节点 → 查询当前区块高度”,对比两者的 API 差异,加深理解。
2.4.1 web3.js 实现(回顾)
import { Web3 } from 'web3';
// 初始化 Web3 实例,连接 RPC 节点(注意:公共节点 https://eth.llamarpc.com、https://eth-sepolia.public.blastapi.io 均解析失败)
// 建议替换为 Infura/Alchemy 专属 RPC 接口,示例格式:https://mainnet.infura.io/v3/你的API密钥
const web3 = new Web3('https://mainnet.infura.io/v3/your-api-key');
// 异步查询区块高度
async function getBlockNumberWeb3() {
try {
const blockNumber = await web3.eth.getBlockNumber();
console.log('web3.js - 当前区块高度:', blockNumber);
return blockNumber;
} catch (error) {
console.error('web3.js - 查询失败:', error.message);
console.error('提示:公共 RPC 节点解析失败,请使用 Infura/Alchemy 专属 RPC 接口');
}
}
getBlockNumberWeb3();
2.4.2 ethers.js 实现(重点)
import { ethers } from "ethers";
// 1. 初始化 Provider(对应 web3.js 的 Web3 实例,用于连接 RPC 节点;公共节点解析失败,建议使用专属接口)
// 示例格式:https://mainnet.infura.io/v3/你的API密钥
const provider = new ethers.JsonRpcProvider('https://mainnet.infura.io/v3/your-api-key');
// 2. 异步查询区块高度(ethers.js API 更简洁)
async function getBlockNumberEthers() {
try {
const blockNumber = await provider.getBlockNumber();
console.log('ethers.js - 当前区块高度:', blockNumber);
return blockNumber;
} catch (error) {
console.error('ethers.js - 查询失败:', error.message);
console.error('提示:公共 RPC 节点解析失败,请使用 Infura/Alchemy 专属 RPC 接口');
}
}
getBlockNumberEthers();
关键区别:web3.js 需创建 Web3 实例,通过 web3.eth.getBlockNumber() 调用;ethers.js 需创建 JsonRpcProvider 实例,通过 provider.getBlockNumber() 调用,API 更简洁。
运行代码:分别执行两个文件,若使用 Infura/Alchemy 专属 RPC 接口,可正常输出当前区块高度,说明环境搭建成功;若仍使用公共节点,会出现解析失败报错。
三、基础进阶:两大库核心 API 实操(对比学习)
本部分按“链上数据查询、钱包账户管理、交易发送”三大模块,同步讲解 web3.js 和 ethers.js 的核心 API,对比两者的使用差异,方便大家灵活切换使用。
3.1 链上数据查询(只读操作,无 Gas 费)
这类操作无需用户签名,无需支付 Gas 费,主要用于查询链上公开数据(账户余额、区块详情、交易详情),是 DApp 中最基础的功能。
3.1.1 查询账户余额(核心对比)
web3.js 实现(回顾):
async function getAccountBalanceWeb3(address) {
try {
const balanceWei = await web3.eth.getBalance(address);
const balanceEth = web3.utils.fromWei(balanceWei, 'ether');
console.log(`web3.js - 地址 ${address} 的余额:${balanceEth} ETH`);
return balanceEth;
} catch (error) {
console.error('web3.js - 查询余额失败:', error.message);
console.error('提示:公共 RPC 节点解析失败,请使用 Infura/Alchemy 专属 RPC 接口');
}
}
// 测试:查询以太坊创始人 Vitalik 的地址余额
getAccountBalanceWeb3('0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045');
ethers.js 实现(重点):
async function getAccountBalanceEthers(address) {
try {
// ethers.js 自动处理单位转换,无需手动调用 fromWei
const balanceEth = await provider.getBalance(address);
// 转换为字符串格式,保留4位小数
console.log(`ethers.js - 地址 ${address} 的余额:${ethers.formatEther(balanceEth)} ETH`);
return ethers.formatEther(balanceEth);
} catch (error) {
console.error('ethers.js - 查询余额失败:', error.message);
console.error('提示:公共 RPC 节点解析失败,请使用 Infura/Alchemy 专属 RPC 接口');
}
}
// 测试:与 web3.js 测试地址一致,对比结果
getAccountBalanceEthers('0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045');
关键区别:ethers.js 中,getBalance 方法返回 BigInt 类型的余额(wei 单位),但提供了 formatEther 方法快速转换为 ETH 单位,无需像 web3.js 那样手动调用 fromWei;反之,若需转换为 wei 单位,可使用 ethers.parseEther(amount) 方法。
3.1.2 查询区块详情(核心对比)
除了查询区块高度,实际开发中常需要获取区块的详细信息(如时间戳、交易数量、矿工地址等),两大库均支持灵活查询,可指定具体区块号或查询最新区块。
web3.js 实现(回顾):
async function getBlockDetailsWeb3(blockNumber) {
try {
// 传入 'latest' 表示最新区块,也可传入具体区块号(如 19000000)
const block = await web3.eth.getBlock(blockNumber || 'latest');
if (!block) {
console.log('web3.js - 未找到该区块');
return null;
}
console.log('web3.js - 区块详情:', {
区块号: block.number,
时间戳: new Date(block.timestamp * 1000).toLocaleString(), // 时间戳转换为本地时间
交易数量: block.transactions.length,
矿工地址: block.miner,
区块哈希: block.hash
});
return block;
} catch (error) {
console.error('web3.js - 查询区块失败:', error.message);
console.error('提示:公共 RPC 节点解析失败,请使用 Infura/Alchemy 专属 RPC 接口');
}
}
// 测试:查询最新区块详情
getBlockDetailsWeb3();
// 测试:查询指定区块详情(示例区块号,可替换为实际区块号)
// getBlockDetailsWeb3(19000000);
ethers.js 实现(重点):
async function getBlockDetailsEthers(blockNumber) {
try {
// 用法与 web3.js 一致,'latest' 表示最新区块,支持具体区块号
const block = await provider.getBlock(blockNumber || 'latest');
if (!block) {
console.log('ethers.js - 未找到该区块');
return null;
}
console.log('ethers.js - 区块详情:', {
区块号: block.number,
时间戳: new Date(block.timestamp * 1000).toLocaleString(),
交易数量: block.transactions.length,
矿工地址: block.miner,
区块哈希: block.hash
});
return block;
} catch (error) {
console.error('ethers.js - 查询区块失败:', error.message);
console.error('提示:公共 RPC 节点解析失败,请使用 Infura/Alchemy 专属 RPC 接口');
}
}
// 测试:查询最新区块详情
getBlockDetailsEthers();
// 测试:查询指定区块详情
// getBlockDetailsEthers(19000000);
关键区别:两者 API 命名接近,功能完全一致,返回的区块对象结构也基本相同。唯一细微差异是,ethers.js 返回的 block.transactions 数组中,交易哈希的格式与 web3.js 一致,均为字符串类型,可直接用于后续查询交易详情。
3.1.3 查询交易详情(核心对比)
当获取到交易哈希后,可通过两大库查询该交易的详细信息(如发送地址、接收地址、交易金额、Gas 费用等),用于调试或展示交易状态。
web3.js 实现(回顾):
async function getTransactionDetailsWeb3(txHash) {
try {
const transaction = await web3.eth.getTransaction(txHash);
if (!transaction) {
console.log('web3.js - 未找到该交易');
return null;
}
// 转换 Gas 价格为 gwei 单位(更易读),交易金额为 ETH 单位
console.log('web3.js - 交易详情:', {
交易哈希: transaction.hash,
发送地址: transaction.from,
接收地址: transaction.to, // 若为合约部署交易,to 为 null
交易金额: web3.utils.fromWei(transaction.value, 'ether') + ' ETH',
Gas 价格: web3.utils.fromWei(transaction.gasPrice, 'gwei') + ' gwei',
Gas 限制: transaction.gas,
交易状态: transaction.blockNumber ? '已上链' : '未上链'
});
return transaction;
} catch (error) {
console.error('web3.js - 查询交易失败:', error.message);
console.error('提示:公共 RPC 节点解析失败,请使用 Infura/Alchemy 专属 RPC 接口');
}
}
// 测试:传入任意交易哈希(可从 Etherscan 复制测试网/主网交易哈希)
getTransactionDetailsWeb3('0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef');
ethers.js 实现(重点):
async function getTransactionDetailsEthers(txHash) {
try {
const transaction = await provider.getTransaction(txHash);
if (!transaction) {
console.log('ethers.js - 未找到该交易');
return null;
}
// ethers.js 提供 formatGwei 方法,直接转换 Gas 价格,无需手动计算
console.log('ethers.js - 交易详情:', {
交易哈希: transaction.hash,
发送地址: transaction.from,
接收地址: transaction.to,
交易金额: ethers.formatEther(transaction.value) + ' ETH',
Gas 价格: ethers.formatGwei(transaction.gasPrice) + ' gwei',
Gas 限制: transaction.gasLimit, // ethers.js 中命名为 gasLimit,与 web3.js 的 gas 对应
交易状态: transaction.blockNumber ? '已上链' : '未上链'
});
return transaction;
} catch (error) {
console.error('ethers.js - 查询交易失败:', error.message);
console.error('提示:公共 RPC 节点解析失败,请使用 Infura/Alchemy 专属 RPC 接口');
}
}
// 测试:与 web3.js 测试哈希一致,对比结果
getTransactionDetailsEthers('0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef');
关键区别:1. 命名差异:ethers.js 中 Gas 限制字段为 gasLimit,web3.js 中为 gas;2. 单位转换:ethers.js 内置 formatGwei 方法,无需像 web3.js 那样通过 fromWei 手动转换 Gas 价格,更便捷;3. 一致性:两者返回的交易核心信息(哈希、地址、金额)完全一致,可相互通用。
3.2 钱包账户管理(核心实操)
两大库均提供账户创建、私钥导入、签名等功能,用于管理用户钱包账户。注意:测试环境可使用随机生成的账户,生产环境需严格保管私钥(私钥泄露会导致资产丢失),建议使用硬件钱包或安全的密钥管理工具。
3.2.1 创建随机账户(核心对比)
随机创建账户适用于测试场景,两大库均支持一键创建,且会生成私钥、公钥、地址,ethers.js 额外支持自动生成助记词(更适合钱包类 DApp 开发)。
web3.js 实现(回顾):
// 创建包含1个随机账户的钱包(可指定创建数量,如 create(5) 创建5个账户)
const walletWeb3 = web3.eth.accounts.wallet.create(1);
const accountWeb3 = walletWeb3[0];
console.log('web3.js - 随机创建的账户:', {
地址: accountWeb3.address,
私钥: accountWeb3.privateKey, // 生产环境绝对不能泄露!
公钥: accountWeb3.publicKey
});
// 补充:web3.js 生成助记词(需额外调用方法,ethers.js 自动生成)
const mnemonic = web3.eth.accounts.mnemonic.generate();
console.log('web3.js - 生成的助记词:', mnemonic);
// 通过助记词导入账户
const accountFromMnemonic = web3.eth.accounts.mnemonicToAccount(mnemonic);
console.log('web3.js - 从助记词导入的账户地址:', accountFromMnemonic.address);
ethers.js 实现(重点):
// ethers.js 创建随机钱包(直接返回钱包实例,自动生成助记词)
const walletEthers = ethers.Wallet.createRandom();
console.log('ethers.js - 随机创建的账户:', {
地址: walletEthers.address,
私钥: walletEthers.privateKey, // 生产环境绝对不能泄露!
公钥: walletEthers.publicKey,
助记词: walletEthers.mnemonic.phrase // 自动生成12位助记词,可用于恢复账户
});
// 补充:ethers.js 通过助记词导入账户
const mnemonic = walletEthers.mnemonic.phrase;
const walletFromMnemonic = ethers.Wallet.fromPhrase(mnemonic, provider);
console.log('ethers.js - 从助记词导入的账户地址:', walletFromMnemonic.address);
关键区别:1. 助记词支持:ethers.js 创建随机钱包时自动生成助记词,web3.js 需额外调用 mnemonic.generate() 方法生成,操作更繁琐;2. 钱包实例:ethers.js 返回的 Wallet 实例可直接关联 provider,后续可直接发送交易,web3.js 需手动关联 Web3 实例;3. 易用性:ethers.js 助记词导入更简洁,无需额外转换,直接通过 fromPhrase 方法即可。
3.2.2 导入现有账户(通过私钥)
实际开发中,常需要导入用户已有的账户(如 MetaMask 钱包导出的私钥),两大库均支持通过私钥快速导入,用于后续交易签名、合约交互。
web3.js 实现(回顾):
// 替换为你的私钥(测试用,生产环境请勿硬编码私钥)
const privateKey = '0x50d349f5cf627d44858d6fcb6fbf15d27457d35c58ba2d5cfeaf455f25db5bec';
// 导入私钥,添加到 web3 钱包实例中
const importedAccountWeb3 = web3.eth.accounts.wallet.add(privateKey);
console.log('web3.js - 导入的账户地址:', importedAccountWeb3.address);
// 验证:查询导入账户的余额(需连接 RPC 节点)
getAccountBalanceWeb3(importedAccountWeb3.address);
ethers.js 实现(重点):
// 替换为你的私钥(测试用,生产环境请勿硬编码私钥)
const privateKey = '0x50d349f5cf627d44858d6fcb6fbf15d27457d35c58ba2d5cfeaf455f25db5bec';
// ethers.js 直接通过私钥创建钱包实例,同时关联 provider(可选,便于后续交易)
const importedWalletEthers = new ethers.Wallet(privateKey, provider);
console.log('ethers.js - 导入的账户地址:', importedWalletEthers.address);
// 验证:查询导入账户的余额(钱包实例可直接调用 getBalance 方法)
async function verifyImportedWallet() {
const balance = await importedWalletEthers.getBalance();
console.log('ethers.js - 导入账户余额:', ethers.formatEther(balance) + ' ETH');
}
verifyImportedWallet();
关键区别:1. 关联节点:ethers.js 导入私钥时,可直接将钱包实例与 provider 关联,后续无需额外操作即可发送交易;web3.js 导入私钥后,需关联已创建的 Web3 实例才能进行链上操作;2. 余额查询:ethers.js 钱包实例可直接调用 getBalance 方法,web3.js 需调用单独的查询函数,传入导入账户的地址;3. 安全性:两者均支持私钥导入,但需注意,生产环境中绝对不能将私钥硬编码到代码中,建议通过前端加密存储或硬件钱包签名。
3.2.3 账户签名(进阶功能)
账户签名是 Web3 开发中的重要功能,用于验证用户身份(如登录 DApp)、签署交易或消息,两大库均提供完善的签名 API,无需发送到链上,无 Gas 费。
web3.js 实现(回顾):
// 要签名的消息(通常为用户身份信息、交易摘要等)
const message = '我是 DApp 用户,请求登录验证';
// 使用导入的账户签名消息
async function signMessageWeb3() {
try {
const signature = await web3.eth.accounts.sign(message, privateKey);
console.log('web3.js - 消息签名结果:', {
原始消息: message,
签名: signature.signature,
签名者地址: signature.address
});
// 验证签名(确认签名者身份)
const recoveredAddress = web3.eth.accounts.recover(message, signature.signature);
console.log('web3.js - 验证签名:', recoveredAddress === importedAccountWeb3.address ? '签名有效' : '签名无效');
} catch (error) {
console.error('web3.js - 签名失败:', error.message);
}
}
signMessageWeb3();
ethers.js 实现(重点):
// 要签名的消息
const message = '我是 DApp 用户,请求登录验证';
// 使用导入的钱包实例签名消息(更简洁)
async function signMessageEthers() {
try {
// ethers.js 签名消息,自动关联签名者地址
const signature = await importedWalletEthers.signMessage(message);
console.log('ethers.js - 消息签名结果:', {
原始消息: message,
签名: signature,
签名者地址: importedWalletEthers.address
});
// 验证签名
const recoveredAddress = ethers.verifyMessage(message, signature);
console.log('ethers.js - 验证签名:', recoveredAddress === importedWalletEthers.address ? '签名有效' : '签名无效');
} catch (error) {
console.error('ethers.js - 签名失败:', error.message);
}
}
signMessageEthers();
关键区别:1. API 简洁度:ethers.js 钱包实例直接调用 signMessage 方法签名,web3.js 需调用 web3.eth.accounts.sign 方法,传入消息和私钥;2. 验证方式:ethers.js 使用 verifyMessage 方法验证签名,web3.js 使用 recover 方法,两者逻辑一致,但 ethers.js 命名更直观;3. 一致性:签名结果格式完全兼容,web3.js 签名的消息可通过 ethers.js 验证,反之亦然。
3.3 交易发送(写操作,需 Gas 费)
这类操作会修改链上状态(如 ETH 转账、调用合约写方法),需要用户签名,支付 Gas 费,是 Web3 开发的核心功能之一。以下示例实现 ETH 转账操作,对比两大库的差异,重点说明 ethers.js 的简洁用法。
3.3.1 web3.js 实现(回顾)
// 转账函数:从导入的账户向目标地址转账
async function sendTransactionWeb3(toAddress, amountEth) {
try {
// 1. 转换金额为 wei 单位(链上交易均使用 wei 单位)
const amountWei = web3.utils.toWei(amountEth, 'ether');
// 2. 构造交易对象
const txObject = {
from: importedAccountWeb3.address, // 发送地址(导入的账户)
to: toAddress, // 接收地址
value: amountWei, // 转账金额(wei 单位)
gas: 21000, // ETH 普通转账固定 Gas 限制为 21000
gasPrice: await web3.eth.getGasPrice() // 获取当前 Gas 价格
};
// 3. 发送交易并等待确认
const receipt = await web3.eth.sendTransaction(txObject);
console.log('web3.js - 交易发送成功!', {
交易哈希: receipt.transactionHash,
区块号: receipt.blockNumber,
消耗 Gas: receipt.gasUsed + ' wei'
});
return receipt;
} catch (error) {
console.error('web3.js - 交易失败:', error.message);
console.error('提示:公共 RPC 节点解析失败,请使用 Infura/Alchemy 专属 RPC 接口;同时确保发送账户有足够的测试 ETH 支付 Gas 费');
}
}
// 测试:使用导入的账户发送 0.001 ETH(需确保账户有测试币)
sendTransactionWeb3(
'0xa3286628134bad128faeef82f44e99aa64085c94', // 接收地址(测试用)
'0.001' // 转账金额(ETH)
);
3.3.2 ethers.js 实现(重点)
// 转账函数:使用导入的钱包实例发送 ETH
async function sendTransactionEthers(toAddress, amountEth) {
try {
// 1. 转换金额为 wei 单位(ethers.js 提供 parseEther 方法)
const amountWei = ethers.parseEther(amountEth);
// 2. 发送交易(钱包实例直接调用 sendTransaction 方法,无需构造复杂对象)
const tx = await importedWalletEthers.sendTransaction({
to: toAddress,
value: amountWei,
gasLimit: 21000 // 对应 web3.js 的 gas 参数,普通转账固定为 21000
});
// 3. 等待交易确认(ethers.js 需调用 wait() 方法,返回交易收据)
const receipt = await tx.wait();
// 验证交易是否成功(receipt.status 为 1 表示成功,0 表示失败)
if (receipt.status === 1) {
console.log('ethers.js - 交易发送成功!', {
交易哈希: tx.hash,
区块号: receipt.blockNumber,
消耗 Gas: receipt.gasUsed.toString() + ' wei'
});
return receipt;
} else {
console.log('ethers.js - 交易失败:交易未确认');
return null;
}
} catch (error) {
console.error('ethers.js - 交易失败:', error.message);
console.error('提示:公共 RPC 节点解析失败,请使用 Infura/Alchemy 专属 RPC 接口;同时确保发送账户有足够的测试 ETH 支付 Gas 费');
}
}
// 测试:与 web3.js 测试参数一致,对比结果
sendTransactionEthers(
'0xa3286628134bad128faeef82f44e99aa64085c94',
'0.001'
);
关键区别:1. 交易构造:ethers.js 无需手动构造复杂的交易对象,钱包实例直接调用 sendTransaction 方法,传入核心参数(to、value、gasLimit)即可;web3.js 需手动构造 txObject,指定 from、gas、gasPrice 等参数;2. 交易确认:ethers.js 需调用 tx.wait() 方法等待交易确认,返回交易收据;web3.js 的 sendTransaction 方法会自动等待确认,直接返回收据;3. 错误处理:ethers.js 可通过 receipt.status 判断交易是否成功,web3.js 需通过收据中的 status 字段(v4 版本支持)判断;4. 易用性:ethers.js 转账流程更简洁,减少了手动参数配置,降低新手出错概率。
注意事项:测试转账时,务必使用测试网(如 Sepolia),确保发送账户有足够的测试 ETH(用于支付 Gas 费和转账金额);生产环境中,建议添加 Gas 价格动态调整逻辑,避免因 Gas 价格过低导致交易卡住。
四、精通进阶:智能合约交互(两大库对比)
智能合约是 Web3 应用的核心,两大库均提供完善的 API 用于与智能合约交互(读方法、写方法),这是从“入门”到“精通”的关键一步。以下以 ERC-20 代币合约为例,讲解完整交互流程,对比两者的使用差异,同时适配 RPC 节点报错场景,确保代码可正常测试。
4.1 核心前提:获取合约 ABI 与地址(通用)
与智能合约交互,必须具备两个核心信息,两大库通用,缺一不可:
- 合约地址:合约部署在区块链上的唯一标识(如测试网 USDT 合约地址、自定义合约部署后的地址)。
- 合约 ABI:应用二进制接口,相当于“合约的接口说明书”,定义了合约的方法、参数、返回值,用于告诉前端如何与合约通信。
获取方式:1. 已部署的公开合约:从 Etherscan(对应测试网/主网)查询,复制 ABI 和合约地址;2. 自定义合约:通过 Remix IDE 编写、部署合约后,从 Remix 中复制 ABI 和地址;3. 项目开发中:由合约开发者提供 ABI 文件(通常为 JSON 格式)。
4.2 合约实例化(核心对比)
合约实例化是与合约交互的第一步,两大库均需传入 ABI 和合约地址,ethers.js 额外需要传入 provider 或 wallet(用于连接节点或签名交易)。
web3.js 实现(回顾):
// 1. 合约 ABI(ERC-20 代币合约简化版,包含转账、查询余额、查询符号方法)
const erc20Abi = [
{
"inputs": [{"name": "_to", "type": "address"}, {"name": "_value", "type": "uint256"}],
"name": "transfer",
"outputs": [{"name": "", "type": "bool"}],
"type": "function" // 写方法,需签名、支付 Gas 费
},
{
"inputs": [{"name": "_owner", "type": "address"}],
"name": "balanceOf",
"outputs": [{"name": "", "type": "uint256"}],
"type": "function" // 读方法,无需签名、无 Gas 费
},
{
"name": "symbol",
"outputs": [{"name": "", "type": "string"}],
"type": "function" // 读方法
}
];
// 2. 合约地址(测试网 ERC-20 代币合约地址,可替换为自己部署的合约地址)
const contractAddress = '0x1234567890abcdef1234567890abcdef12345678';
// 3. 实例化合约(web3.js 已关联 Web3 实例,无需额外传入节点信息)
const contractWeb3 = new web3.eth.Contract(erc20Abi, contractAddress);
ethers.js 实现(重点):
// 1. 合约 ABI(与 web3.js 完全一致,可直接复用)
const erc20Abi = [
{
"inputs": [{"name": "_to", "type": "address"}, {"name": "_value", "type": "uint256"}],
"name": "transfer",
"outputs": [{"name": "", "type": "bool"}],
"type": "function"
},
{
"inputs": [{"name": "_owner", "type": "address"}],
"name": "balanceOf",
"outputs": [{"name": "", "type": "uint256"}],
"type": "function"
},
{
"name": "symbol",
"outputs": [{"name": "", "type": "string"}],
"type": "function"
}
];
// 2. 合约地址(与 web3.js 一致,测试用)
const contractAddress = '0x1234567890abcdef1234567890abcdef12345678';
// 3. 实例化合约(ethers.js 需传入 ABI、地址、provider/wallet)
// 仅查询合约(读方法):传入 provider 即可
const contractEthers = new ethers.Contract(contractAddress, erc20Abi, provider);
// 若需调用写方法(如转账):需传入 wallet(已导入私钥的钱包实例)
const contractWithSigner = contractEthers.connect(importedWalletEthers);
关键区别:1. 实例化参数:web3.js 实例化合约时,无需传入节点信息(已关联 Web3 实例);ethers.js 需传入 provider(用于读方法)或 wallet(用于写方法),明确区分“只读交互”和“可签名交互”;2. 写方法准备:ethers.js 调用写方法前,需通过 connect() 方法将合约实例与钱包绑定,获取签名权限;web3.js 无需额外绑定,在调用写方法时指定 from 地址即可;3. 复用性:ABI 可完全复用,两大库的合约实例化逻辑一致,仅参数传入有差异。
更多推荐




所有评论(0)