第4节:写合约与事件

小白入门:https://github.com/dukedaily/solidity-expert ,欢迎star转发,文末加V入群。

职场进阶: https://dukeweb3.com

Signer 传进 Contract,就能发交易、监听事件、查询历史日志。

发送交易

import { ethers } from "ethers";

const provider = new ethers.JsonRpcProvider(process.env.RPC);
const wallet = new ethers.Wallet(process.env.PRIVATE_KEY, provider);

const USDC = "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48";
const abi = [
  "function transfer(address to, uint256 amount) returns (bool)",
  "event Transfer(address indexed from, address indexed to, uint256 value)",
];
const usdc = new ethers.Contract(USDC, abi, wallet);

// 发交易(返回 TransactionResponse)
const tx = await usdc.transfer("0xRecipient", ethers.parseUnits("1.0", 6));

console.log("tx hash:", tx.hash);

// 等上链
const receipt = await tx.wait();
console.log("gasUsed:", receipt.gasUsed);

覆盖 gas / value / nonce

最后一个参数可以是 overrides 对象:

await usdc.transfer(to, amount, {
  gasLimit: 60_000n,
  maxFeePerGas: ethers.parseUnits("30", "gwei"),
  maxPriorityFeePerGas: ethers.parseUnits("1.5", "gwei"),
  nonce: 42,
});

订阅事件(WebSocket)

const ws = new ethers.WebSocketProvider(process.env.WS);
const usdc = new ethers.Contract(USDC, abi, ws);

usdc.on("Transfer", (from, to, amount, event) => {
  console.log(`${from} -> ${to}: ${ethers.formatUnits(amount, 6)}`);
  console.log("tx:", event.log.transactionHash);
});

v6 变化:事件回调的最后一个参数是 event(v5 是 log),原始 log 在 event.log

查询历史事件

const filter = usdc.filters.Transfer(
  null, // from: 任意
  "0xRecipientAddress", // to: 固定
);

const current = await ws.getBlockNumber();
const logs = await usdc.queryFilter(filter, current - 10000, current); // 过去 10000 块

for (const log of logs) {
  const { from, to, value } = log.args;
  console.log(from, "->", to, ":", ethers.formatUnits(value, 6));
}

log.args 在 v6 中是 Result 类型:既可按索引访问(log.args[0])也可按名字访问(log.args.from)。

小结

Contract + Signer = 写合约;WebSocketProvider + on = 实时事件;queryFilter = 历史事件。下一节收敛到常用工具函数。