本文介绍了如何使用Solana的Websocket订阅功能来监听区块链上的实时变化,并详细讲解了如何设置环境、创建订阅、取消订阅以及优化订阅的方法。
有几种方法可以监控 Solana 上的实时变化。如果你不确定哪种方法适合你的使用案例,请查看我们的 博客文章,了解选项的比较。如果你准备开始使用 Solana Web3js(版本 1.x)进行 WebSockets 开发,请继续阅读(如果你更喜欢版本 2.x,请查看我们的指南, 这里)!
创建事件监听器是有效地提醒你的应用程序或用户你正在监控的内容已经变化的一种方式。Solana 有几个内置的实用事件监听器,也称为订阅,使得在 Solana 区块链上监听变化变得轻而易举。你不知道如何使用它吗?以下是一些可能会派上用场的示例:一个 Discord 机器人寻找与链上程序的交互(例如,销售机器人),一个 dApp 检查用户交易的错误,或者用户钱包余额变化时的手机通知。
想要视频演示吗?请跟随 Sahil 学习如何在 4 分钟内创建 Websocket 订阅以连接 Solana 区块链。
在本指南中,你将学习如何使用几种 Solana 事件监听器方法和 QuickNode 的 Websocket 端点 (WSS://) 监听链上的变化。具体来说,你将创建一个简单的 TypeScript 应用程序以跟踪一个帐户(或钱包)的变化。接下来,你将学习如何使用 Solana 的取消订阅方法从应用程序中移除一个监听器。我们还将简要介绍一些 Solana 的其他监听器方法的基础知识。
在终端中创建一个新项目目录:
mkdir solana-subscriptions
cd solana-subscriptions
创建一个文件,app.ts:
echo > app.ts
使用 "yes" 标志初始化你的项目以使用默认值创建新包:
yarn init --yes
# 或者
npm init --yes
yarn add @solana/web3.js@1
# 或者
npm install @solana/web3.js@1
在你喜欢的代码编辑器中打开 app.ts,在第 1 行从 Solana Web3 库中导入 Connection、PublicKey 和 LAMPORTS_PER_SOL:
import { Connection, PublicKey, LAMPORTS_PER_SOL } from "@solana/web3.js";
简化调试的日志
你现在可以访问 RPC 端点的日志,帮助你更有效地排查问题。如果你在 RPC 调用中遇到问题,只需检查 QuickNode 信息面板中的日志,以快速识别和解决问题。了解更多关于日志历史限制的内容,请访问 我们的定价页面
好的!我们已经准备好开始了。
要在 Solana 上构建,你需要一个 API 端点来连接网络。你可以使用公共节点或部署并管理自己的基础设施;但是,如果你想要 8 倍更快的响应时间,你可以把繁重的工作交给我们。
我们将使用一个 Solana Devnet 节点。复制 HTTP 提供程序 和 WSS 提供程序链接:
在 app.js 的第 3 行和第 4 行上创建两个新变量来存储这些 URL:
const WSS_ENDPOINT = 'wss://example.solana-devnet.quiknode.pro/000/'; // 替换为你的 URL
const HTTP_ENDPOINT = 'https://example.solana-devnet.quiknode.pro/000/'; // 替换为你的 URL
在第 5 行上,创建一个新的 Connection 连接到 Solana:
const solanaConnection = new Connection(HTTP_ENDPOINT, { wsEndpoint: WSS_ENDPOINT });
如果你以前创建过与 Solana 的 Connection 实例,你可能会注意到我们的参数有所不同,特别是包含了 {wsEndpoint:WSS_ENDPOINT}
。让我们更深入地探讨。
Connection 类的 构造函数 允许我们传递一个可选的 commitmentOrConfig。我们可以包括一些有趣的选项,使用 ConnectionConfig,但今天我们要关注可选参数 wsEndpoint。这是一个选项,你可以提供指向完整节点 JSON RPC PubSub Websocket 端点的 URL。在我们的情况下,那是我们之前定义的 WSS 端点 WSS_ENDPOINT。
如果你不传递 wsEndpoint 会发生什么?well,Solana 为此提供了一个函数 makeWebsocketUrl,它将你的端点 URL 中的 https 替换为 wss 或 http 替换为 ws ( source)。由于所有 QuickNode HTTP 端点都有一个与之对应的的相同身份验证令牌的 WSS 端点,因此你可以省略此参数,除非你想使用单独的端点进行 Websocket 查询。
让我们创建一些订阅吧!
要在 Solana 上跟踪一个钱包,我们需要在 solanaConnection 上调用 onAccountChange 方法。我们将传递 ACCOUNT_TO_WATCH,我们要监控的钱包的公钥,以及一个回调函数。我们创建了一个简单的日志,提醒我们检测到事件并记录账户的新余额。在你的 solanaConnection 声明后的第 7 行添加以下代码片段:
(async () => {
const ACCOUNT_TO_WATCH = new PublicKey('vines1vzrYbzLMRdu58ou5XTby4qAqVRLmqo36NKPTg'); // 替换为你自己的钱包地址
const subscriptionId = await solanaConnection.onAccountChange(
ACCOUNT_TO_WATCH,
(updatedAccountInfo) =>
console.log(`---事件通知 ${ACCOUNT_TO_WATCH.toString()}--- \n新帐户余额:`, updatedAccountInfo.lamports / LAMPORTS_PER_SOL, ' SOL'),
"confirmed"
);
console.log('正在启动 web socket,订阅 ID: ', subscriptionId);
})()
这段代码可以直接运行,但我们会添加一个额外的功能来帮助我们测试它是否正常工作。我们通过添加一个 sleep 函数(以添加时间延迟)和一个 airdrop 请求来实现这一点。在第 6 行的 async 代码块之前,添加:
const sleep = (ms:number) => {
return new Promise(resolve => setTimeout(resolve, ms));
}
然后在 async 代码块中,"Starting web socket" 日志之后添加这个 airdrop 调用:
await sleep(10000); // 等待 10 秒以进行 socket 测试
await solanaConnection.requestAirdrop(ACCOUNT_TO_WATCH, LAMPORTS_PER_SOL);
你的代码将在 socket 启动后有效地等待 10 秒,然后请求钱包进行 airdrop(注意:这仅适用于 devnet 和 testnet)。
我们的代码现在看起来像这样:
import { Connection, PublicKey, LAMPORTS_PER_SOL } from "@solana/web3.js";
const WSS_ENDPOINT = 'wss://example.solana-devnet.quiknode.pro/000/'; // 替换为你的 URL
const HTTP_ENDPOINT = 'https://example.solana-devnet.quiknode.pro/000/'; // 替换为你的 URL
const solanaConnection = new Connection(HTTP_ENDPOINT, { wsEndpoint: WSS_ENDPOINT });
const sleep = (ms:number) => {
return new Promise(resolve => setTimeout(resolve, ms));
}
(async () => {
const ACCOUNT_TO_WATCH = new PublicKey('vines1vzrYbzLMRdu58ou5XTby4qAqVRLmqo36NKPTg');
const subscriptionId = await solanaConnection.onAccountChange(
ACCOUNT_TO_WATCH,
(updatedAccountInfo) =>
console.log(`---事件通知 ${ACCOUNT_TO_WATCH.toString()}--- \n新帐户余额:`, updatedAccountInfo.lamports / LAMPORTS_PER_SOL, ' SOL'),
"confirmed"
);
console.log('正在启动 web socket,订阅 ID: ', subscriptionId);
await sleep(10000); // 等待 10 秒以进行 socket 测试
await solanaConnection.requestAirdrop(ACCOUNT_TO_WATCH, LAMPORTS_PER_SOL);
})()
让我们测试一下。在终端中输入 ts-node app.ts 来启动你的 WebSocket!大约 10 秒后,你应该会看到一个终端回调日志,如下所示:
solana-subscriptions % ts-node app.ts
正在启动 web socket,订阅 ID: 0
---事件通知 for vines1vzrYbzLMRdu58ou5XTby4qAqVRLmqo36NKPTg---
新帐户余额: 88790.51694709 SOL
很好!你应该会注意到,即使在事件通知后,你的应用程序仍保持打开状态。这是因为我们的订阅仍然正在监听我们账户的变化。我们需要一种方法来 取消订阅 监听器。按 Ctrl+C 停止该过程。
Solana 创建了一个内置方法,可用于从我们的帐户变更监听器中取消订阅 removeAccountChangeListener。该方法仅接受有效的 subscriptionId (数字)作为参数。在你的 async 块内,在 airdrop 之后添加另一个 sleep,以允许时间处理事务,然后调用 removeAccountChangeListener:
await sleep(10000); // 等待 10 秒以进行 socket 测试
await solanaConnection.removeAccountChangeListener(subscriptionId);
console.log(`Websocket ID: ${subscriptionId} 已关闭。`);
现在再次运行你的代码,你将看到相同的序列,后跟关闭的 WebSocket:
solana-subscriptions % ts-node app.ts
正在启动 web socket,订阅 ID: 0
---事件通知 for vines1vzrYbzLMRdu58ou5XTby4qAqVRLmqo36NKPTg---
新帐户余额: 88791.51694709 SOL
Websocket ID: 0 已关闭。
做得不错!如你所见,这在你希望在某件事情发生后禁用听众时非常有用(例如时间流逝、实现特定阈值、达到通知次数等)。
我们已将此脚本的最终代码发布在 我们的 Github 仓库 以供参考。
Solana 还有几种其他类似的 WebSocket 订阅/取消订阅方法,也很有用。我们将在本节中简要描述它们。
*注意:所有 取消订阅方法都需要 subscriptionId (数字)参数。有关这些方法的更多信息,请查看我们的文档 quicknode.com/docs/solana。你可以通过对 app.js 进行小的修改来尝试其他订阅。
WebSocket 方法的计费积分基于收到的响应数量,而不是订阅数量。例如,如果你打开一个 accountChange
订阅并收到 100 个响应,你的账户将被收取 5,000 积分( 每个响应 50 积分 X 100 响应)。
为了优化你的订阅并确保你没有为不必要的订阅或无关的响应付费,你应该考虑以下几点:
以下方法用于从订阅中移除监听器:
accountUnsubscribe
:取消订阅账户更新。logsUnsubscribe
:取消订阅日志。programUnsubscribe
:取消订阅程序账户更新。slotUnsubscribe
:取消订阅槽更新。signatureUnsubscribe
:取消订阅签名更新。rootUnsubscribe
:取消订阅根更新。logsSubscribe
方法允许你过滤事务日志,可以显著减少你收到的响应数量。以下是一个示例:
// 订阅所有日志(昂贵且不推荐):
const subscriptionId = await connection.onLogs(
'all',
(logs) => {
console.log('新日志:', logs);
}
);
// 仅订阅提到特定地址的日志:
const EXAMPLE_PUBLIC_KEY = new PublicKey('YOUR_ADDRESS_HERE');
const specificAddressSubscriptionId = await connection.onLogs(
EXAMPLE_PUBLIC_KEY,
(logs) => {
console.log('提到特定地址的日志:', logs);
}
);
programSubscribe
方法功能强大,但可能会生成许多响应。使用过滤器来缩小你接收的数据:
const filters = [
{
memcmp: {
offset: 0, // 指定账户数据中的偏移量
bytes: 'base58_encoded_bytes_here' // 指定要比较的字节
}
},
{
dataSize: 128 // 如果你的账户数据是固定的,可以通过数据大小过滤——更新此值以匹配你账户的数据大小
}
];
const subscriptionId = await connection.onProgramAccountChange(
new PublicKey('Your_Program_ID_Here'),
(accountInfo) => {
console.log('账户已更改:', accountInfo);
},
'confirmed',
filters
);
在这个示例中,我们使用了 memcmp
和 dataSize
过滤器。memcmp
过滤器在账户数据的特定偏移量比较一系列字节,而 dataSize
过滤器则根据数据大小过滤账户。这一组合可以显著减少你收到的响应数量,专注于匹配你特定标准的账户。有关使用 GetProgramAccountsFilter 的更多信息,请参见 Solana RPC 文档。
确保过滤器的具体性:你的过滤器越具体,收到的无关响应就越少。
根据你的使用情况使用适当的承诺级别:更高的承诺级别(例如 "finalized")可能会导致更新较少,但更确定。
定期审核并移除未使用的订阅:审核你的活动订阅,并删除任何不再需要的订阅。
监控你的使用情况:跟踪你接收的响应数量,必要时调整你的过滤器或订阅策略( QuickNode 信息面板)。
通过实施这些优化策略,你可以确保只接收所需的数据,从而最大限度地减少不必要的计费积分消耗,同时保持你 Solana 应用程序的有效性。
QuickNode 提供多种解决方案,以获取 Solana 的实时数据。查看以下选项,找到适合你用例的工具:
有关更多信息,请查看我们的 博客文章。
做得不错!现在你应该掌握如何使用 Solana 的 WebSocket 订阅。你将如何使用 Solana WebSockets?我们希望看到你正在创建的内容!在 Discord 或 Twitter 与我们分享你的应用程序。
要了解更多信息,请查看我们其他的 Solana 教程 这里,如果你在使用 WebSocket 订阅中获得乐趣,请考虑订阅我们的 新闻通讯。
- 原文链接: quicknode.com/guides/sol...
- 登链社区 AI 助手,为大家转译优秀英文文章,如有翻译不通的地方,还请包涵~
如果觉得我的文章对您有用,请随意打赏。你的支持将鼓励我继续创作!