第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 = 历史事件。下一节收敛到常用工具函数。