Keyboard shortcuts

Press ← or → to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

ethers.js v6:从 RPC 读取和发送交易

ethers.js 把节点连接分成两个概念:Provider 只负责读取链上数据和发送已签名交易;Signer 代表能签名的账户。Contract 连接 Provider 时只能调用 view/pure 方法,连接 Signer 后才能发送状态交易。

读取合约状态

import { Contract, JsonRpcProvider, Wallet, formatUnits } from "ethers";

const provider = new JsonRpcProvider(process.env.RPC_URL);
const token = new Contract(
  process.env.TOKEN_ADDRESS,
  ["function balanceOf(address) view returns (uint256)",
   "function decimals() view returns (uint8)",
   "function transfer(address,uint256) returns (bool)"],
  provider,
);

const raw = await token.balanceOf(process.env.USER_ADDRESS);
console.log(formatUnits(raw, await token.decimals()));

RPC URL、私钥和合约地址应通过环境变量注入,不要提交到仓库。代币金额始终使用 bigint,不要用 JavaScript number。

发送交易并等待确认

const signer = new Wallet(process.env.PRIVATE_KEY, provider);
const writable = token.connect(signer);
const tx = await writable.transfer(process.env.RECIPIENT_ADDRESS, 1_000_000n);
const receipt = await tx.wait();
if (!receipt || receipt.status !== 1) throw new Error("交易未成功确认");
console.log(receipt.hash, receipt.status);

以上片段使用 ethers v6,需要设置 RPC_URL、TOKEN_ADDRESS、USER_ADDRESS、RECIPIENT_ADDRESS 和 PRIVATE_KEY。只有节点管理并解锁了账户时,JsonRpcProvider.getSigner() 才适用;公共 RPC 场景通常需要本地 Wallet,浏览器钱包则使用 BrowserProvider。

发送前应区分三种状态:交易尚未进入区块、交易已确认但执行失败、交易成功并产生事件。wait() 返回的 receipt 需要检查 status,不能只根据 RPC 请求是否成功判断业务成功。

部署和交互时的检查

  • 检查 provider.getNetwork() 返回的 chain ID,避免把主网交易发到错误网络。
  • 对用户输入使用 parseUnits,根据代币实际 decimals 转换。
  • 估算 Gas 只能作为参考,合约状态可能在发送前变化。
  • 监听事件时保存区块号和交易哈希,并处理链重组带来的重复或撤销。
  • 签名消息时明确域、用途和有效期,避免把交易签名和普通消息混用。

更完整的代币示例见 ERC-20。

参考:ethers v6 Wallet、Contract。