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。