前言
在以太坊生态中,
ethers.js是开发者与区块链交互的核心工具包。本文系统梳理了其六大核心模块(Provider、Contract、Wallet、Utils、部署工具及高级功能),通过代码示例 + 关键差异对比 + 安全实践,帮助开发者快速掌握从环境搭建到链上交互的全流程。无论是浏览器端轻量集成,还是 Node.js 服务端深度开发,本文均提供可直接落地的解决方案安装
链接方式
# 在页面上使用链接使用 <script type="module"> import { ethers } from "https://cdnjs.cloudflare.com/ajax/libs/ethers/6.7.0/ethers.min.js"; </script>安装包方式
# 项目中引入包 npm install ethers import {ethers} from "ethers"; import { BrowserProvider, parseUnits } from "ethers"; import { HDNodeWallet } from "ethers/wallet";Provider提供者类
通过Provider类,读取链上的信息;
- ethers.BrowserProvider
#和钱包插件交 链接钱包插件读取钱包账号的信息 import { ethers } from "https://cdnjs.cloudflare.com/ajax/libs/ethers/6.7.0/ethers.min.js"; const provider = new ethers.BrowserProvider(window.ethereum);- ethers.JsonRpcProvider
import { ethers } from "https://cdnjs.cloudflare.com/ajax/libs/ethers/6.7.0/ethers.min.js"; # 注册infura 创意一个项目获取your-infura-project-id ,读取sepolia链上的信息 const ALCHEMY_MAINNET_URL="https://sepolia.infura.io/v3/{your-infura-project-id}" const provider=new ethers.JsonRpcProvider(ALCHEMY_MAINNET_URL); or # 启动ganach # 读取本地ganach const provider = new ethers.JsonRpcProvider("http://127.0.0.1:7545");区别
特性 ethers.BrowserProviderethers.JsonRpcProvider用途 连接到用户的钱包(如 MetaMask) 连接到远程以太坊节点(如 Infura、Alchemy) 依赖 依赖 window.ethereum依赖远程节点的 JSON-RPC 接口 用户友好 高(用户直接使用钱包) 低(需要配置远程节点) 安全性 高(私钥存储在本地钱包) 低(需要信任远程节点) 功能 有限(依赖钱包功能) 强大(访问节点全部功能) 适用场景 浏览器环境,用户交互 服务器端或无钱包环境 Provider常见属性和方法
属性
blockNumber:返回Provider已经知晓的最新区块号(块高),如果没有同步到区块,则为null。polling:可变属性,表示Provider是否正在轮询。轮询可以设置为临时启用/禁用或永久禁用以允许节点进程退出。pollingInterval:可变属性,表示Provider的轮询频率(以毫秒为单位)。默认时间间隔为 4 秒方法
getNetwork():获取当前连接的网络信息const ALCHEMY_MAINNET_URL="https://sepolia.infura.io/v3/{your-infura-project-id}" const provider=new ethers.JsonRpcProvider(ALCHEMY_MAINNET_URL); const network = await provider.getNetwork(); # 网络名 console.log("Network name:", network.name);//sepolia # 网络id console.log("Chain ID:", network.chainId);//11155111
getBalance(address) :获取指定地址的余额
# 节点公钥 例如:0xEb9e88cE633B2a8F400xxxxxxxxx const balance = await provider.getBalance("0xEb9e88cE633B2a8F400xxxxxxxxx"); console.log("Balance:", ethers.formatEther(balance)); // 把wei格式化为 ETHgetTransaction(transactionHash) :获取指定交易的详细信息。
const ALCHEMY_MAINNET_URL="https://sepolia.infura.io/v3/{your-infura-project-id}" const provider=new ethers.JsonRpcProvider(ALCHEMY_MAINNET_URL); //通过交易hash:在sepolia.etherscan浏览器器上找一个交易hash:例如:0xb1fd8ca5493460bb7d9d1ae7d0a96fcd7a77c3eea53954f9ab6ae6225019972b验证 const transaction = await provider.getTransaction("0xYourTransactionHashHere"); console.log("Transaction from:", transaction.from); console.log("Transaction to:", transaction.to); console.log("Transaction value:", ethers.formatEther(transaction.value));getTransactionReceipt(transactionHash) :获取指定交易的收据信息。
const ALCHEMY_MAINNET_URL="https://sepolia.infura.io/v3/{your-infura-project-id}" const provider=new ethers.JsonRpcProvider(ALCHEMY_MAINNET_URL); //通过交易hash:在sepolia.etherscan浏览器器上找一个交易hash:例如:0xb1fd8ca5493460bb7d9d1ae7d0a96fcd7a77c3eea53954f9ab6ae6225019972b验证 const receipt = await provider.getTransactionReceipt("0xYourTransactionHashHere"); console.log("Transaction status:", receipt.status); console.log("Transaction logs:", receipt.logs);sendTransaction(signedTransaction) :发送一个已签名的交易。
const txHash = await provider.sendTransaction(signedTransaction); console.log("Transaction hash:", txHash); getSigner(address) :获取与指定地址关联的 Signer 对象。
const signer = provider.getSigner("0xYourAddressHere"); console.log("Signer address:", await signer.getAddress());estimateGas(transaction) :估算执行指定交易所需的 gas。
const gasEstimate = await provider.estimateGas({ to: "0xRecipientAddressHere", from: "0xSenderAddressHere", value: ethers.parseEther("1.0"), }); console.log("Estimated gas:", gasEstimate.toString());call(transaction, blockTag) :调用一个智能合约的函数,不发送交易。
const result = await provider.call({ to: "0xContractAddressHere", data: "0xYourEncodedFunctionCallHere", }); console.log("Call result:", result);getBlock(blockTag) :获取指定区块的信息
const block = await provider.getBlock("latest"); console.log("Block number:", block.number); console.log("Block timestamp:", block.timestamp);getBlockTransactionCount(blockTag) :获取指定区块中的交易数量。
const transactionCount = await provider.getBlockTransactionCount("latest"); console.log("Transaction count in block:", transactionCount);resolveName(ensName) :获取 ensName 对应地址的 Promise,如果没有则为 null。
provider.resolveName("registrar.firefly.eth").then(function(address) { console.log("Address: " + address); // "0x6fC21092DA55B392b045eD78F4732bff3C580e2c" });lookupAddress(address) :获取 address 对应的 ENS 名称的 Promise,如果没有则为 null。
let address = "0x6fC21092DA55B392b045eD78F4732bff3C580e2c"; provider.lookupAddress(address).then(function(name) { console.log("Name: " + name); // "registrar.firefly.eth" });Contract合约类
说明:通过一个ERC20的代币合约,实现对合约的读写操作
合约类型
- 可读:最后参数:Provider
- 可读、写:最后参数 Singer
- 区别:创建合约的最后一个参数:是Provider合约只读,Singer可以读、写
只读
# 说明: 使用hardhat 部署成功后会在控制台返回合约地址 abi 在artifacts/contracts/中的json中取出abi数组 # 引入包 import { ethers } from 'https://cdnjs.cloudflare.com/ajax/libs/ethers/6.7.0/ethers.min.js'; //const provider = new ethers.BrowserProvider(window.ethereum); const provider = new ethers.JsonRpcProvider("http://127.0.0.1:8545"); # 创建合约 const TokenAddress="0xxxxxxxxxx"; const const TokenABI =[{ "inputs": [], "name": "name", "outputs": [{"type": "string"}], "stateMutability": "view", "type": "function" }, { "inputs": [], "name": "symbol", "outputs": [{"type": "string"}], "stateMutability": "view", "type": "function" }, { "inputs": [], "name": "totalSupply", "outputs": [{"type": "uint256"}], "stateMutability": "view", "type": "function" } //其他更多 ] const TokenContract = new ethers.Contract(TokenAddress, TokenABI, provider); # 代币名 const name = await TokenContract.name(); console.log("Contract Name:", name); # 代币符合 const symbol = await TokenContract.symbol(); console.log("Contract Symbol:", symbol); # 代币总额 const totalSupply = await TokenContract.totalSupply();读、写
说明:最主要的区别是创建合约的提供者不同(singer)
import { ethers } from 'https://cdnjs.cloudflare.com/ajax/libs/ethers/6.7.0/ethers.min.js'; //const provider = new ethers.BrowserProvider(window.ethereum); const provider = new ethers.JsonRpcProvider("http://127.0.0.1:8545"); # 创建合约 const TokenAddress="0xxxxxxxxxx"; const const TokenABI =[]; const signer = await provider.getSigner(); const TokenContract = new ethers.Contract(TokenAddress, TokenABI, singer); # 交互操作 转账 const addr1="0xxxxxxxxxx"; # 给addr1转1LYTH const tx= await TokenContract.transfer(addr1,1); # 等待交易成功 await tx.wait() console.log("查看账号addr1的余额",await TokenContract.balanceOf(addr1)) console.log("查看账号signer的余额",await TokenContract.balanceOf(signer))部署
说明通过合约工厂部署合约
const { ethers } = require("hardhat"); async function deployContract() { try { const [signer] = await ethers.getSigners();//签名者 //通过 npx hardhat compile编译合约,在artifacts/contracts/xxx.sol/xxx.json中复制abi数组和bytecode码 const abi=[]// const bytecode="";// const deployFactory = new ethers.ContractFactory(abi,bytecode,signer)//工厂合约 const TokenContract = await deployFactory.deploy();//部署 console.log("部署合约交易详情",TokenContract.deploymentTransaction()) await TokenContract.waitForDeployment();//等待部署成功 console.log("Token deployed at:", TokenContract.target);//合约地址 # 调用合约的方法和交互 console.log(`合约代号: ${await TokenContract.name()`) console.log(`合约代号: ${await TokenContract.symbol()}`) console.log(`合约代号: ${await TokenContract.totalSupply()}`) const addr1="0xxxxxxx" const tx= await TokenContract.transfer(addr1,1);//向addr1转1 console.log("Transfer Transaction:", tx); const balance = await TokenContract.balanceOf(addr1);//查看addr1余额 console.log("Balance:", balance.toString()); console.log(await TokenContract.balanceOf(signer))//查看signer余额 }catch (error) { console.log(error) } }Wallet钱包类
创建钱包
说明:Wallet是Singer的子类,Singer的抽象类不能直接实例化
随机钱包
const Wallet=ethers.Wallet.createRandom(); # 公钥 console.log("Address:", Wallet.address); # 私钥 console.log("Private Key:", Wallet.privateKey); # 助记词 console.log("Mnemonic:", Wallet.mnemonic.phrase);用私钥创建wallet对象
const private=new ethers.JsonRpcProvider("http://127.0.1:8545"); const privateKey = "acxxxxxxxxx";//私钥 const wallet = new ethers.Wallet(privateKey,private); console.log("Wallet address:", wallet);//钱包公钥从助记词创建wallet对象
# 助记词 const Mnemonic="quote shy web universe book wheat turtle cabin degree permit beach capable";//12个不同的单词() const wallet3 = ethers.Wallet.fromPhrase(Mnemonic); console.log("Wallet3:", wallet3); console.log("Address:", wallet3.address); console.log("Private Key:", wallet3.privateKey); console.log("Mnemonic:", wallet3.mnemonic.phrase);通过JSON文件创建wallet对象
如何生成Keystore V3 文件格式(以太坊钱包加密 JSON 文件)
async function creactKeystoreV3() { const wallet = ethers.Wallet.createRandom(); // 2. 加密为 Keystore V3 JSON const password="xxxxxx";//密码 const json = await wallet.encrypt(password); // 3. 保存到文件 require("fs").writeFileSync("keystore.json", json); console.log("JSON:", json); } creactKeystoreV3();//会在当前页面生成一个keystore.json文件Keystore V3创建wallet
async function creatWalletJson() { console.log("开始读取json文件"); const json=require("fs").readFileSync("keystore.json", "utf8"); const password="xxxxxx" const walletJson =await ethers.Wallet.fromEncryptedJson(json, password);//json文件和密码 console.log("Wallet from JSON:",walletJson); console.log("Address:", walletJson.address);//钱包地址 console.log("Private Key:", walletJson.privateKey);//私钥 console.log("Mnemonic:", walletJson.mnemonic.phrase);//助记词 } creatWalletJson();转账
async function sendTransactionFn() { const private=new ethers.JsonRpcProvider("http://127.0.1:8545"); const privateKey = "xxx";//私钥 const wallet = new ethers.Wallet(privateKey,private); const address1="0xxxxx";//账号1公钥 const tx={ to:address1, value:ethers.parseEther("100"),//转移100eth }// //执行交易 const txRes = await wallet2.sendTransaction(tx); const receipt = await txRes.wait() // 等待链上确认交易 console.log(receipt) // 打印交易的收据 } sendTransactionFn(); // 把公钥导入MetaMask插件中导入在账号可以查看账号余额的变化
# Utils工具类 | 工具方法 | 典型用途 | 示例代码 | | :------------------------- | :-------------------- | :----------------------------- | | `parseUnits`/`formatUnits` | 处理代币单位转换(如 ETH ↔ Wei) | `ethers.parseUnits("1.5", 18)` | | `keccak256` | 生成哈希(如事件签名、地址校验) | `ethers.keccak256("0x1234")` | | `toChecksumAddress` | 校验并标准化地址格式 | `ethers.getAddress("0x...")` | | `hexlify`/`hexStripZeros` | 处理十六进制字符串 | `ethers.hexlify([1, 2, 3])` | | `randomBytes` | 生成安全随机数 | `ethers.randomBytes(32)` # 总结 通过上述实践,我们系统梳理了 ethers.js 核心模块,在开发中的典型应用场景与最佳实践,为高效构建区块链交互提供了可复用的技术方案。