如何通过TypeScript订阅Solana链上Anchor合约事件与日志
结论
Solana 原生支持合约事件的历史查询与实时订阅能力,Anchor 框架的 TypeScript SDK 已经对事件序列化/反序列化、日志拉取、Websocket 订阅做了完整封装,不需要手动处理底层日志解析逻辑,直接按以下步骤实现即可。
前置准备
首先安装前端依赖,注意使用当前官方维护的新版 Anchor TS 包(旧版@project-serum/anchor已停止更新):
npm install @coral-xyz/anchor @solana/web3.js
在代码中初始化连接、程序实例与事件解析器,编译 Anchor 合约时生成的 IDL 文件已经自动包含了你定义的MyEvent结构信息,不需要额外编写序列化规则:
import { Program, AnchorProvider, EventParser, setProvider, PublicKey } from "@coral-xyz/anchor"; import { Connection, clusterApiUrl } from "@solana/web3.js"; // 引入本地保存的合约IDL,路径对应Anchor编译后target/idl目录下的文件 import idl from "./idl/your_contract.json"; // 初始化RPC连接,生产环境建议替换为付费RPC节点地址 const connection = new Connection(clusterApiUrl("devnet"), "confirmed"); // 替换为你实际接入的钱包实例,示例为Phantom等钱包注入的window.solana对象 const wallet = window.solana; const provider = new AnchorProvider(connection, wallet, { commitment: "confirmed" }); setProvider(provider); // 替换为你自己的链上程序公钥 const PROGRAM_ID = new PublicKey("你的程序公钥地址"); const program = new Program(idl as any, PROGRAM_ID); // 初始化事件解析器,可直接将链上日志解析为TS对象 const eventParser = new EventParser(PROGRAM_ID, program.coder);
历史事件查询实现
通过 RPC 拉取指定范围内目标程序产生的日志,再用事件解析器批量解析即可得到历史事件。注意免费公开 RPC 对日志查询的区块范围限制较严,大跨度查询建议分批拉取,或使用支持历史日志索引的付费 RPC 服务:
/** * 拉取指定槽位范围内的MyEvent历史事件 * @param startSlot 起始区块槽位 * @param endSlot 结束区块槽位,不传默认查询到最新已确认区块 */ async function getHistoryMyEvents(startSlot: number, endSlot?: number) { const logResp = await connection.getLogs({ mentions: [PROGRAM_ID.toBase58()], // 仅过滤目标程序产生的日志 commitment: "confirmed", minContextSlot: startSlot, maxContextSlot: endSlot }); const eventList: Array<{ slot: number; txSignature: string; data: bigint; label: Uint8Array; }> = []; for (const logItem of logResp.value) { // 跳过执行失败的交易日志 if (logItem.err) continue; // 解析当前交易中包含的所有Anchor事件 const parsedEvents = eventParser.parseLogs(logItem.logs); for (const event of parsedEvents) { // 筛选出你定义的MyEvent类型 if (event.name === "MyEvent") { eventList.push({ slot: logItem.slot, txSignature: logItem.signature, data: event.data.data as bigint, label: event.data.label as Uint8Array }); } } } return eventList; }
实时事件订阅实现
Anchor Program 实例封装了基于 Websocket 的日志订阅能力,只需要传入事件名和回调函数即可,不需要手动维护连接、解析日志:
// 注册事件监听器,第三个参数为承诺级别:processed延迟最低,finalized确定性最高 const listenerId = program.addEventListener( "MyEvent", (event, slot, txSignature) => { // 新事件上链后会自动触发该回调,event为已解析好的事件对象 console.log("收到新的MyEvent事件:", { slot, txSignature, data: event.data as bigint, label: event.label as Uint8Array }); }, "confirmed" ); // 页面卸载或不需要订阅时,记得移除监听器避免内存泄漏 function unsubscribeEvent() { program.removeEventListener(listenerId); }
注意事项
- Anchor 中
emit!宏发出的事件本质是写入交易日志的固定格式数据,解析逻辑完全由 IDL 驱动,只要 IDL 和链上程序版本一致就不会出现解析错误。 - 合约中定义的
u64类型在 TS 侧会被解析为bigint类型,如果需要转为数字,注意判断数值不要超过Number.MAX_SAFE_INTEGER避免精度丢失。 - 免费公开 RPC 普遍存在 Websocket 连接不稳定、历史日志查询范围小、请求限流的问题,生产环境请使用付费 RPC 服务。
- 如果需要按事件字段做过滤(比如只订阅
data大于特定值的事件),普通 RPC 不支持该能力,需要使用带索引功能的增强 RPC 或自建链上索引服务。
内容的提问来源于stack exchange,提问作者Senuda Jayalath
相关产品推荐
相关产品推荐

