Derick
1402 words
7 minutes
Solana 链上事件与交易监听

一、为什么监听事件?#

在真实业务中,我们经常需要「实时响应链上状态变化」:

  • 钱包余额更新(收款到账)
  • 代币转账或 NFT 交易
  • 合约(Program)执行事件
  • 大额交易监控、套利机会识别
  • 扫链与索引器系统(Indexer)

以太坊使用 eth_subscribe 来监听事件,

Solana 则提供了两种主流方式:

方式协议说明
WebSocket 监听WSS最常用,直接从 RPC 节点订阅实时消息
gRPC 监听gRPC Stream更高性能、结构化更强,适合自建节点或生产系统

这两种方式本质相同 —— 都是实时订阅链上数据变化。

不同的是:gRPC 具备更好的类型安全与流控制能力


二、准备环境#

我们继续使用 solana-go 库。

依赖安装如下:

go get github.com/gagliardetto/[email protected]
go get github.com/gagliardetto/[email protected]

RPC 地址建议选择 Devnet 或自建节点

  • RPC(HTTP/WSS)端点

    https://api.devnet.solana.com
    wss://api.devnet.solana.com
    
  • gRPC 端点(部分节点提供)

    grpc://api.devnet.solana.com:10000
    

三、监听类型概览#

Solana 的事件订阅支持多种类型。

常见监听类型如下表所示:

监听类型说明常用场景
Account Subscribe监听指定账户数据变化监听钱包余额变动、NFT 元数据更新
Logs Subscribe监听指定 Program 的日志输出解析交易事件(如 Token Program 的 Transfer)
Program Subscribe监听某个 Program 下所有账户变化建立索引器、同步 DApp 状态
Signature Subscribe监听交易签名状态变化等待交易确认或失败
Slot Subscribe监听新区块(Slot)实时获取最新区块高度
Root Subscribe监听已确认区块根追踪 Finalized 状态

四、使用 WebSocket 实现监听#

我们先来看 最简单、最常见的方式

通过 WSS 订阅日志流。

package main

import (
	"context"
	"fmt"
	"log"

	"github.com/gagliardetto/solana-go"
	"github.com/gagliardetto/solana-go/rpc"
)

func main() {
	ctx := context.Background()

	//  连接 WSS
	client, err := rpc.ConnectWS(ctx, "wss://api.devnet.solana.com")
	if err != nil {
		log.Fatalf("无法连接 WebSocket: %v", err)
	}

	//  订阅 Token Program 日志
	stream, err := client.LogsSubscribe(ctx, rpc.LogsSubscribeFilterMentions{
		Mentions: []solana.PublicKey{
			solana.MustPublicKeyFromBase58("TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"),
		},
	}, rpc.CommitmentFinalized)
	if err != nil {
		log.Fatalf("订阅失败: %v", err)
	}

	fmt.Println(" 正在监听 Token Program 的日志...")

	//  实时接收
	for {
		msg, err := stream.Recv()
		if err != nil {
			log.Fatalf("接收失败: %v", err)
		}
		fmt.Printf(" 区块 %d 日志:\n", msg.Context.Slot)
		for _, l := range msg.Value.Logs {
			fmt.Println("🪶", l)
		}
	}
}

运行后你会看到来自 Token Program 的实时日志输出:

正在监听 Token Program 的日志...
 区块 31212400 日志:
🪶 Program log: Instruction: Transfer
🪶 Program log: Transfer 1000000 tokens

这就意味着,你的 Go 程序正在“听”整个 Solana 网络上的事件。


五、通过 gRPC 订阅(生产级)#

WSS 适合开发调试,但在高并发生产环境下,

gRPC 更加高效、安全,且支持强类型结构化数据。

solana-go 库中,我们可以这样实现:

package main

import (
	"context"
	"fmt"
	"log"

	"github.com/gagliardetto/solana-go"
	"github.com/gagliardetto/solana-go/rpc"
)

func main() {
	ctx := context.Background()
	client := rpc.New(rpc.DevNet_RPC)

	stream, err := client.LogsSubscribe(ctx, rpc.LogsSubscribeFilterMentions{
		Mentions: []solana.PublicKey{
			solana.MustPublicKeyFromBase58("TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"),
		},
	}, rpc.CommitmentConfirmed)
	if err != nil {
		log.Fatalf("gRPC 订阅失败: %v", err)
	}

	fmt.Println(" gRPC 实时监听中...")

	for {
		msg, err := stream.Recv()
		if err != nil {
			log.Fatalf("接收失败: %v", err)
		}
		fmt.Printf("[Slot %d] 日志数量: %d\n", msg.Context.Slot, len(msg.Value.Logs))
		for _, l := range msg.Value.Logs {
			fmt.Println("→", l)
		}
	}
}

注意:部分 RPC 服务商(如 Helius、Triton)会开放专门的 gRPC 端点。

建议生产系统接入这类服务,而非直接连接官方 Devnet。


六、常见事件类型示例#

Program事件示例日志
Token ProgramTransferProgram log: Instruction: Transfer
MintToProgram log: Instruction: MintTo
BurnProgram log: Instruction: Burn
System ProgramTransferProgram log: Instruction: Transfer
Metaplex NFT ProgramCreateMetadataAccountProgram log: Instruction: CreateMetadataAccount
UpdateMetadataProgram log: Instruction: UpdateMetadata

七、结构化日志解析#

日志虽然是字符串输出,但通过模式匹配或正则即可提取核心信息。

例如,我们可以检测 Instruction: Transfer 并捕获代币转账事件:

if strings.Contains(line, "Instruction: Transfer") {
	fmt.Println(" 检测到转账事件!")
}

生产系统中,可以进一步将日志解析为结构化 JSON,

存入数据库或推送至 Kafka,供下游服务使用。


八、监听账户变化(AccountSubscribe)#

除了日志,开发者还可以直接监听某个账户的状态变化:

stream, err := client.AccountSubscribe(ctx, walletAddress, "", rpc.CommitmentConfirmed)
if err != nil {
	log.Fatalf("订阅账户失败: %v", err)
}
for {
	msg, err := stream.Recv()
	if err != nil {
		log.Fatal("接收失败:", err)
	}
	fmt.Println("账户余额更新:", msg.Value.Lamports)
}

当钱包收到或发送 SOL 时,程序会立即收到通知。

这对于钱包类应用、收款服务尤其实用。


九、生产环境最佳实践#

在真实业务中,仅仅「打印日志」是不够的。

以下是常见的生产架构思路:

1. 使用消息队列(MQ)#

将事件推送到 Kafka / RabbitMQ / NATS,

供其他微服务异步消费,避免阻塞主线程。

2. 增量存储#

将已处理 Slot 记录到数据库中,

确保应用重启后能从中断处恢复监听。

3. 多通道监听#

同时监听多个 Program(Token、System、Metaplex),

并根据类型路由到不同的处理逻辑。

4. gRPC + WebSocket 混合#

在内部服务使用 gRPC 接口,

对外提供标准化的 WebSocket 通知流,便于前端接入。


十、练习与思考#

  1. 使用 gRPC 监听 Token Program 的日志,统计过去 1 小时内的转账次数。
  2. 将监听结果存入 PostgreSQL,并实现简单的区块索引。
  3. 尝试监听特定钱包的 Token Account 变化,实现“到账提醒”。
  4. 思考:如果要做一个 Solana 版的 Etherscan 监听模块,应该如何设计?

十一、小结#

能力方法实现
实时监听日志LogsSubscribe()捕获链上 Program 输出
监听账户变化AccountSubscribe()检测余额更新
gRPC 流式订阅client.LogsSubscribe()稳定高性能监听
稳定生产架构MQ + 增量同步实现可恢复监听系统
Solana 链上事件与交易监听
https://blog.ithuo.net/posts/solana-onchain-events-and-subscriptions/
Author
Derick
Published at
2024-12-17