{"content":{"title":"使用WebSockets和Solana Web3.js 2.0监控Solana账户","body":"## 概述\r\n\r\nSolana [最近宣布](https://blog.quicknode.com/solana-web3-js-2-0-a-new-chapter-in-solana-development/) Solana Web3.js 2.0，这是他们与Solana区块链交互的JavaScript库的一次重大更新。在许多其他功能中，Solana Web3.js 2.0引入了一种新的、更健壮的WebSocket订阅处理方式，用于监控链上事件。本指南将向你展示如何使用新的订阅系统实现账户监控，该系统比之前的版本提供了更好的类型安全和错误处理。\r\n\r\n#### 你将要做的事情\r\n\r\n在本指南中，你将学习如何：\r\n\r\n- 使用新的Web3.js 2.0 API设置WebSocket连接\r\n- 创建一个账户订阅，以监控Pump.fun费用账户的余额变更\r\n- 处理订阅清理和错误情况\r\n- 以用户友好的方式格式化和显示余额变更\r\n\r\n![脚本演示](https://img.learnblockchain.cn/2025/02/25/demo-b4ba8854bcbe871d34680edb62e50d2d.gif)\r\n\r\n#### 你将需要的东西\r\n\r\n- Node.js（建议版本20.0或更高）\r\n- npm或yarn包管理器\r\n- 安装TypeScript和ts-node\r\n\r\n## Web3.js 1.0的主要区别\r\n\r\n新的Web3.js 2.0订阅系统引入了几个改进：\r\n\r\n1. **类型安全**：新的API在整个过程中使用了TypeScript泛型和严格类型。\r\n2. **现代异步迭代**：使用`for await...of`循环而不是回调，符合现代[异步迭代协议](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Iteration_protocols#the_async_iterator_and_async_iterable_protocols)。\r\n3. **中止控制器集成**：内置支持使用AbortController进行订阅清理。如果你不熟悉，_AbortController_ 是一个内置的JavaScript类，可以让你中止异步操作，例如HTTP请求或WebSocket连接。\r\n4. **更好的错误处理**：改进了错误类型和处理机制。\r\n\r\n## 设置你的环境\r\n\r\n#### 1\\. 创建一个新的项目目录：\r\n\r\n```\r\nmkdir solana-subscriptions-v2 && cd solana-subscriptions-v2\r\n\r\n```\r\n\r\n#### 2\\. 初始化一个新的npm项目：\r\n\r\n```\r\nnpm init -y\r\n\r\n```\r\n\r\n#### 3\\. 安装所需的依赖：\r\n\r\n```\r\nnpm install @solana/web3.js@2\r\n\r\n```\r\n\r\n如果你没有全局安装开发依赖项，请安装：\r\n\r\n```\r\nnpm install --save-dev typescript ts-node @types/node\r\n\r\n```\r\n\r\n#### 4\\. 创建一个TypeScript配置文件（tsconfig.json）：\r\n\r\n```\r\ntsc --init\r\n\r\n```\r\n\r\n并用以下内容更新配置文件：\r\n\r\n```\r\n{\r\n  \"compilerOptions\": {\r\n    \"module\": \"NodeNext\",\r\n    \"moduleResolution\": \"NodeNext\",\r\n    \"noEmit\": true,\r\n    \"target\": \"ESNext\"\r\n  },\r\n}\r\n\r\n```\r\n\r\n## 创建账户监控\r\n\r\n创建一个名为`app.ts`的新文件，让我们一步一步实现账户监控系统。\r\n\r\n```\r\necho > app.ts\r\n\r\n```\r\n\r\n在代码编辑器中打开该文件，我们开始吧！\r\n\r\n#### 1\\. 导入所需的依赖：\r\n\r\n在`app.ts`文件中添加以下导入：\r\n\r\n```\r\nimport {\r\n    createSolanaRpcSubscriptions,\r\n    RpcSubscriptions,\r\n    SolanaRpcSubscriptionsApi,\r\n    address,\r\n    Address\r\n} from '@solana/web3.js';\r\n\r\n```\r\n\r\n简化调试的日志\r\n\r\n你现在可以访问RPC端点的日志，帮助你更有效地排查问题。如果你在RPC调用中遇到问题，只需在QuickNode仪表板中检查日志即可快速识别和解决问题。了解有关日志历史限制的更多信息，请参见[我们的定价页面。](https://www.quicknode.com/pricing#features)\r\n\r\n#### 2\\. 定义常量：\r\n\r\n在你的导入下面添加以下常量：\r\n\r\n```\r\nconst WSS_PROVIDER_URL = 'wss://your-quicknode-endpoint.example';\r\nconst LAMPORTS_PER_SOL = 1_000_000_000;\r\nconst PUMP_FUN_FEE_ACCOUNT = address(\"CebN5WGQ4jvEPvsVU4EoHEpgzq1VV7AbicfhtW4xC9iM\");\r\n\r\n```\r\n\r\n要在Solana上构建，你需要一个API端点来连接到网络。\r\n\r\n\r\n\r\n![Solana主网端点](https://img.learnblockchain.cn/2025/02/25/solana-mainnet-6bbd732b3c3cba30c41fbf9357c517bc.png)\r\n\r\n复制WSS提供者链接并更新你的`WSS_PROVIDER_URL`常量以匹配链接。\r\n\r\n我们将监控[Pump.fun](https://pump.fun/)费用账户（_CebN5WGQ4jvEPvsVU4EoHEpgzq1VV7AbicfhtW4xC9iM_）的余额变更，但你可以随意使用任何你喜欢的Solana账户。请注意，Solana Web3.js 2.0库要求我们使用`@solana/web3.js`库中的_Address_类型——我们可以使用库中的`address`函数从字符串创建_Address_。\r\n\r\n#### 3\\. 定义辅助函数：\r\n\r\n在你的常量下面添加以下辅助函数：\r\n\r\n```\r\nconst lamportsToSolString = (lamports: number, includeUnit = true): string => {\r\n    const solAmount = lamports / LAMPORTS_PER_SOL;\r\n    return `${solAmount.toLocaleString('en-US', {\r\n        minimumFractionDigits: 2,\r\n        maximumFractionDigits: 2\r\n    })} ${includeUnit ? 'SOL' : ''}`;\r\n};\r\n\r\n```\r\n\r\n这个函数将以两位小数格式化lamports为SOL，并在字符串中可选地包含单位（SOL）。\r\n\r\n#### 4\\. 定义接口：\r\n\r\n让我们创建一个接口来定义我们追踪函数的参数：\r\n\r\n```\r\ninterface TrackAccountArgs {\r\n    rpcSubscriptions: RpcSubscriptions<SolanaRpcSubscriptionsApi>;\r\n    accountAddress: Address;\r\n    abortSignal: AbortSignal;\r\n}\r\n\r\n```\r\n\r\n在前面的部分中，我们讨论了_Address_和_AbortSignal_，接下来我们快速谈谈_RpcSubscriptions_和_SolanaRpcSubscriptionsApi_。\r\n\r\n在新的Web3.js 2.0 API中，以前通过_Connection_类暴露的方法现在通过两个类暴露：_Rpc_和_RpcSubscriptions_，一个用于HTTP请求，一个用于WebSocket。_RpcSubscriptions_类提供了用于跟踪链上事件（例如，账户变更、程序变更、日志、槽等）的WebSocket方法。请查看我们的[文档](https://www.quicknode.com/docs/solana/accountSubscribe)以获取更多信息。\r\n\r\n#### 5\\. 创建账户跟踪函数：\r\n\r\n将以下内容添加到你的`app.ts`文件中。我们将在下面逐步解析：\r\n\r\n```\r\nasync function trackAccount({ rpcSubscriptions, accountAddress, abortSignal }: TrackAccountArgs) {\r\n    let lastLamports: number | null = null;\r\n\r\n    try {\r\n        const accountNotifications = await rpcSubscriptions\r\n            .accountNotifications(accountAddress, { commitment: 'confirmed' })\r\n            .subscribe({ abortSignal });\r\n\r\n        try {\r\n            for await (const notification of accountNotifications) {\r\n                const { slot } = notification.context;\r\n                const currentLamports = Number(notification.value.lamports);\r\n                const delta = lastLamports !== null ? currentLamports - lastLamports : 0;\r\n                const sign = delta > 0 ? '+' : delta < 0 ? '-' : ' ';\r\n                console.log(`   账户变更被检测到，槽号为 ${slot.toLocaleString()}。 新余额: ${lamportsToSolString(currentLamports)} (${sign}${lamportsToSolString(Math.abs(delta))})`);\r\n                lastLamports = currentLamports;\r\n            }\r\n        } catch (error) {\r\n            throw error;\r\n        }\r\n    } catch (error) {\r\n        throw error;\r\n    }\r\n}\r\n\r\n```\r\n\r\n让我们拆解一下：\r\n\r\n1. 首先，我们定义一个名为`lastLamports`的变量，并将其设置为`null`。这个变量将用于存储账户的最后已知余额，以便我们在收到新通知时计算增量。\r\n2. 然后，我们创建一个`try/catch`块来处理创建订阅时的错误。\r\n3. 在`try`块中，我们在`rpcSubscriptions`对象上调用`accountNotifications`方法（类似于v1库中的`onAccountChange`方法），传入`accountAddress`和`commitment`选项。我们还传入`abortSignal`，以便在需要时取消订阅。\r\n4. 接下来，我们创建一个`try/catch`块来处理处理通知时的错误。\r\n5. 在`try`块中，我们使用`for await...of`循环来迭代从订阅中收到的通知。我们从`context`中获取`slot`，并从每个通知的`value`中获取`lamports`，然后进行一些轻处理，将余额变化记录到控制台中。\r\n\r\n#### 6\\. 创建入口点：\r\n\r\n将以下内容添加到你的`app.ts`文件，以执行你的追踪函数：\r\n\r\n```\r\nasync function main() {\r\n    console.log(`💊 正在追踪 Pump.fun 费用账户: ${PUMP_FUN_FEE_ACCOUNT} 💊`);\r\n    const rpcSubscriptions = createSolanaRpcSubscriptions(WSS_PROVIDER_URL);\r\n    const abortController = new AbortController();\r\n    try {\r\n        await trackAccount({\r\n            rpcSubscriptions,\r\n            accountAddress: PUMP_FUN_FEE_ACCOUNT,\r\n            abortSignal: abortController.signal\r\n        });\r\n    } catch (e) {\r\n        console.log('订阅错误', e);\r\n    } finally {\r\n        abortController.abort();\r\n    }\r\n}\r\n\r\nmain();\r\n\r\n```\r\n\r\n我们实际上是在为我们的脚本创建一个入口点，当我们使用`ts-node`运行脚本时将执行它。我们将在文件底部调用`main`函数。\r\n`main`函数将使用我们的`WSS_PROVIDER_URL`创建一个`RpcSubscriptions`类的新实例，我们将用于创建我们的订阅。然后我们将调用`trackAccount`函数，传入`RpcSubscriptions`实例、`PUMP_FUN_FEE_ACCOUNT`地址和`abortController.signal`。\r\n\r\n现在，让我们运行我们的脚本。\r\n\r\n## 运行监控\r\n\r\n当你准备好后，使用ts-node运行你的脚本：\r\n\r\n```\r\nts-node app.ts\r\n\r\n```\r\n\r\n监控将开始跟踪指定账户的变更，并按以下格式显示余额变更：\r\n\r\n```\r\n账户变更被检测到，槽号为 301,428,932。 新余额: 265,598.16 SOL (+0.14 SOL)\r\n\r\n```\r\n\r\n干得不错！ 🚀 🚀 🚀\r\n\r\n## 计费和优化\r\n\r\nWebSocket方法的计费积分基于收到的响应数量，而不是创建的订阅数量。例如，如果你打开一个`accountNotifications`订阅并接收100个响应，你的账户将被计费2,000积分（[每响应20积分](https://www.quicknode.com/api-credits/sol#:~:text=20-,accountunsubscribe,-20) X 100个响应）。请检查[API积分页面](https://www.quicknode.com/api-credits/sol)以获取更新的计费费率。\r\n\r\n为了优化你的订阅并确保你没有为不必要的订阅或无关的响应付费，你应该考虑以下事项：\r\n\r\n- 使用_AbortController_或其他订阅逻辑在不需要时取消订阅。\r\n- 利用适用方法的过滤器以接收仅相关的数据。\r\n\r\n## 替代解决方案\r\n\r\nQuickNode提供几种从Solana获取实时数据的解决方案。查看以下选项以找到适合你用例的工具：\r\n\r\n- [WebSockets](https://www.quicknode.com/docs/solana/accountSubscribe?utm_source=internal&utm_campaign=guides&utm_content=monitor-solana-accounts-sw3js2)：正如本指南中讨论的那样，WebSockets提供直接连接到Solana节点的实时更新——这些对于简单应用程序和快速开发而言是理想的。\r\n- [Yellowstone gRPC Geyser插件](https://marketplace.quicknode.com/add-on/yellowstone-grpc-geyser-plugin?utm_source=internal&utm_campaign=guides&utm_content=monitor-solana-accounts-sw3js2)：Yellowstone gRPC Geyser插件提供一个强大的gRPC接口，用于流式传输Solana数据，内置过滤和历史数据支持。\r\n- [流](https://www.quicknode.com/streams/solana?utm_source=internal&utm_campaign=guides&utm_content=monitor-solana-accounts-sw3js2)：管理解决方案，可将Solana数据处理并路由到多个目标，内置过滤和历史数据支持。\r\n\r\n有关更多信息，请查看我们的[博客文章](https://blog.quicknode.com/access-real-time-solana-data-3-tools-compared/)。\r\n\r\n## 结论\r\n\r\nWeb3.js 2.0提供了一种更健壮、类型安全的方法来处理Solana WebSocket订阅。新的API使管理订阅、处理错误和正确清理资源变得更容易。当构建需要监控Solana区块链事件的应用程序时，这些新功能有助于创建更可靠和可维护的代码。\r\n\r\n#### 我们❤️反馈！\r\n\r\n[告诉我们](https://airtable.com/shrKKKP7O1Uw3ZcUB?prefill_Guide+Name=Monitor%20Solana%20Accounts%20Using%20WebSockets%20and%20Solana%20Web3.js%202.0) 如果你有任何反馈或新的主题请求。我们很乐意听到你的声音。\r\n\r\n有关更多信息，请查看：\r\n\r\n- [Solana Web3.js 2.0 GitHub](https://github.com/solana-labs/solana-web3.js)\r\n- [博客：访问实时Solana数据：3种工具比较](https://blog.quicknode.com/access-real-time-solana-data-3-tools-compared/)\r\n- [Solana WebSocket文档](https://www.quicknode.com/docs/solana/accountSubscribe)\r\n- [QuickNode API积分](https://www.quicknode.com/api-credits/sol)\r\n\r\n>- 原文链接： [quicknode.com/guides/sol...](https://www.quicknode.com/guides/solana-development/tooling/web3-2/subscriptions?utm_source=internal&utm_campaign=guides&utm_content=how-to-create-websocket-subscriptions-to-solana-blockchain-using-typescript)\r\n>- 登链社区 AI 助手，为大家转译优秀英文文章，如有翻译不通的地方，还请包涵～"},"author":{"user":"https://learnblockchain.cn/people/25306","address":null},"history":null,"timestamp":1740449055,"version":1}