sol-parser-sdk-nodejs 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (114) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +449 -0
  3. package/README_CN.md +343 -0
  4. package/dist/accounts/mod.d.ts +11 -0
  5. package/dist/accounts/mod.js +53 -0
  6. package/dist/accounts/nonce.d.ts +5 -0
  7. package/dist/accounts/nonce.js +32 -0
  8. package/dist/accounts/pumpswap.d.ts +8 -0
  9. package/dist/accounts/pumpswap.js +192 -0
  10. package/dist/accounts/token.d.ts +5 -0
  11. package/dist/accounts/token.js +98 -0
  12. package/dist/accounts/types.d.ts +9 -0
  13. package/dist/accounts/types.js +2 -0
  14. package/dist/accounts/utils.d.ts +1 -0
  15. package/dist/accounts/utils.js +12 -0
  16. package/dist/core/account_dispatcher_rpc.d.ts +11 -0
  17. package/dist/core/account_dispatcher_rpc.js +155 -0
  18. package/dist/core/account_fill_bonk.d.ts +4 -0
  19. package/dist/core/account_fill_bonk.js +20 -0
  20. package/dist/core/account_fill_meteora.d.ts +14 -0
  21. package/dist/core/account_fill_meteora.js +26 -0
  22. package/dist/core/account_fill_orca.d.ts +5 -0
  23. package/dist/core/account_fill_orca.js +18 -0
  24. package/dist/core/account_fill_pumpfun.d.ts +7 -0
  25. package/dist/core/account_fill_pumpfun.js +75 -0
  26. package/dist/core/account_fill_pumpswap.d.ts +10 -0
  27. package/dist/core/account_fill_pumpswap.js +78 -0
  28. package/dist/core/account_fill_raydium.d.ts +20 -0
  29. package/dist/core/account_fill_raydium.js +104 -0
  30. package/dist/core/clock.d.ts +2 -0
  31. package/dist/core/clock.js +7 -0
  32. package/dist/core/common_filler_rpc.d.ts +6 -0
  33. package/dist/core/common_filler_rpc.js +21 -0
  34. package/dist/core/dex_event.d.ts +1009 -0
  35. package/dist/core/dex_event.js +14 -0
  36. package/dist/core/error.d.ts +43 -0
  37. package/dist/core/error.js +16 -0
  38. package/dist/core/json_utils.d.ts +8 -0
  39. package/dist/core/json_utils.js +18 -0
  40. package/dist/core/metadata.d.ts +10 -0
  41. package/dist/core/metadata.js +13 -0
  42. package/dist/core/rpc_invoke_map.d.ts +23 -0
  43. package/dist/core/rpc_invoke_map.js +157 -0
  44. package/dist/core/unified_parser.d.ts +19 -0
  45. package/dist/core/unified_parser.js +48 -0
  46. package/dist/grpc/client.d.ts +52 -0
  47. package/dist/grpc/client.js +309 -0
  48. package/dist/grpc/client_stub.d.ts +5 -0
  49. package/dist/grpc/client_stub.js +5 -0
  50. package/dist/grpc/types.d.ts +132 -0
  51. package/dist/grpc/types.js +304 -0
  52. package/dist/grpc/yellowstone_parse.d.ts +9 -0
  53. package/dist/grpc/yellowstone_parse.js +46 -0
  54. package/dist/index.d.ts +18 -0
  55. package/dist/index.js +99 -0
  56. package/dist/instr/bonk_ix.d.ts +5 -0
  57. package/dist/instr/bonk_ix.js +52 -0
  58. package/dist/instr/meteora_damm_ix.d.ts +6 -0
  59. package/dist/instr/meteora_damm_ix.js +349 -0
  60. package/dist/instr/mod.d.ts +15 -0
  61. package/dist/instr/mod.js +95 -0
  62. package/dist/instr/orca_whirlpool_ix.d.ts +5 -0
  63. package/dist/instr/orca_whirlpool_ix.js +73 -0
  64. package/dist/instr/program_ids.d.ts +14 -0
  65. package/dist/instr/program_ids.js +18 -0
  66. package/dist/instr/pumpfun_ix.d.ts +5 -0
  67. package/dist/instr/pumpfun_ix.js +167 -0
  68. package/dist/instr/pumpswap_ix.d.ts +5 -0
  69. package/dist/instr/pumpswap_ix.js +215 -0
  70. package/dist/instr/raydium_amm_v4_ix.d.ts +6 -0
  71. package/dist/instr/raydium_amm_v4_ix.js +46 -0
  72. package/dist/instr/raydium_clmm_ix.d.ts +5 -0
  73. package/dist/instr/raydium_clmm_ix.js +85 -0
  74. package/dist/instr/raydium_cpmm_ix.d.ts +5 -0
  75. package/dist/instr/raydium_cpmm_ix.js +63 -0
  76. package/dist/instr/utils.d.ts +10 -0
  77. package/dist/instr/utils.js +33 -0
  78. package/dist/logs/meteora_amm.d.ts +8 -0
  79. package/dist/logs/meteora_amm.js +113 -0
  80. package/dist/logs/meteora_damm.d.ts +6 -0
  81. package/dist/logs/meteora_damm.js +509 -0
  82. package/dist/logs/meteora_dlmm.d.ts +6 -0
  83. package/dist/logs/meteora_dlmm.js +201 -0
  84. package/dist/logs/optimized_matcher.d.ts +5 -0
  85. package/dist/logs/optimized_matcher.js +194 -0
  86. package/dist/logs/orca.d.ts +6 -0
  87. package/dist/logs/orca.js +148 -0
  88. package/dist/logs/program_data.d.ts +2 -0
  89. package/dist/logs/program_data.js +22 -0
  90. package/dist/logs/program_log_discriminators.d.ts +59 -0
  91. package/dist/logs/program_log_discriminators.js +67 -0
  92. package/dist/logs/pump.d.ts +12 -0
  93. package/dist/logs/pump.js +251 -0
  94. package/dist/logs/pump_amm.d.ts +9 -0
  95. package/dist/logs/pump_amm.js +418 -0
  96. package/dist/logs/raydium_amm.d.ts +9 -0
  97. package/dist/logs/raydium_amm.js +224 -0
  98. package/dist/logs/raydium_clmm.d.ts +8 -0
  99. package/dist/logs/raydium_clmm.js +141 -0
  100. package/dist/logs/raydium_cpmm.d.ts +7 -0
  101. package/dist/logs/raydium_cpmm.js +121 -0
  102. package/dist/logs/raydium_launchpad.d.ts +12 -0
  103. package/dist/logs/raydium_launchpad.js +92 -0
  104. package/dist/parser_alias.d.ts +3 -0
  105. package/dist/parser_alias.js +2 -0
  106. package/dist/rpc_parser.d.ts +16 -0
  107. package/dist/rpc_parser.js +36 -0
  108. package/dist/rpc_transaction.d.ts +25 -0
  109. package/dist/rpc_transaction.js +111 -0
  110. package/dist/util/binary.d.ts +14 -0
  111. package/dist/util/binary.js +82 -0
  112. package/dist/warmup.d.ts +3 -0
  113. package/dist/warmup.js +23 -0
  114. package/package.json +44 -0
package/README_CN.md ADDED
@@ -0,0 +1,343 @@
1
+ <div align="center">
2
+ <h1>⚡ Sol Parser SDK - Node.js</h1>
3
+ <h3><em>高性能 Solana DEX 事件解析器,专为 Node.js/TypeScript 设计</em></h3>
4
+ </div>
5
+
6
+ <p align="center">
7
+ <strong>通过 Yellowstone gRPC 实时解析 Solana DEX 事件的 Node.js/TypeScript 库</strong>
8
+ </p>
9
+
10
+ <p align="center">
11
+ <a href="https://www.npmjs.com/package/sol-parser-sdk-nodejs">
12
+ <img src="https://img.shields.io/badge/npm-sol--parser--sdk--nodejs-red.svg" alt="npm">
13
+ </a>
14
+ <a href="https://github.com/0xfnzero/sol-parser-sdk-nodejs/blob/main/LICENSE">
15
+ <img src="https://img.shields.io/badge/license-MIT-blue.svg" alt="License">
16
+ </a>
17
+ </p>
18
+
19
+ <p align="center">
20
+ <img src="https://img.shields.io/badge/Node.js-339933?style=for-the-badge&logo=node.js&logoColor=white" alt="Node.js">
21
+ <img src="https://img.shields.io/badge/TypeScript-3178C6?style=for-the-badge&logo=typescript&logoColor=white" alt="TypeScript">
22
+ <img src="https://img.shields.io/badge/Solana-9945FF?style=for-the-badge&logo=solana&logoColor=white" alt="Solana">
23
+ <img src="https://img.shields.io/badge/gRPC-4285F4?style=for-the-badge&logo=grpc&logoColor=white" alt="gRPC">
24
+ </p>
25
+
26
+ <p align="center">
27
+ <a href="./README_CN.md">中文</a> |
28
+ <a href="./README.md">English</a> |
29
+ <a href="https://fnzero.dev/">官网</a> |
30
+ <a href="https://t.me/fnzero_group">Telegram</a> |
31
+ <a href="https://discord.gg/vuazbGkqQE">Discord</a>
32
+ </p>
33
+
34
+ ---
35
+
36
+ ## 📦 SDK 版本
37
+
38
+ 本 SDK 提供多种语言版本:
39
+
40
+ | 语言 | 仓库 | 描述 |
41
+ |------|------|------|
42
+ | **Rust** | [sol-parser-sdk](https://github.com/0xfnzero/sol-parser-sdk) | 超低延迟,SIMD 优化 |
43
+ | **Node.js** | [sol-parser-sdk-nodejs](https://github.com/0xfnzero/sol-parser-sdk-nodejs) | TypeScript/JavaScript,Node.js 支持 |
44
+ | **Python** | [sol-parser-sdk-python](https://github.com/0xfnzero/sol-parser-sdk-python) | 原生 async/await 支持 |
45
+ | **Go** | [sol-parser-sdk-golang](https://github.com/0xfnzero/sol-parser-sdk-golang) | 并发安全,goroutine 支持 |
46
+
47
+ ---
48
+
49
+ ## 📊 性能亮点
50
+
51
+ ### ⚡ 实时解析
52
+ - **零延迟** 基于日志的事件解析
53
+ - **gRPC 流式传输** 支持 Yellowstone/Geyser 协议
54
+ - **多协议** 单次订阅同时监听多个 DEX
55
+ - **事件类型过滤** 精准解析所需事件
56
+
57
+ ### 🏗️ 支持的协议
58
+ - ✅ **PumpFun** - Meme 代币交易
59
+ - ✅ **PumpSwap** - PumpFun 交换协议
60
+ - ✅ **Raydium AMM V4** - 自动做市商
61
+ - ✅ **Raydium CLMM** - 集中流动性
62
+ - ✅ **Raydium CPMM** - 集中池
63
+ - ✅ **Orca Whirlpool** - 集中流动性 AMM
64
+ - ✅ **Meteora DAMM V2** - 动态 AMM
65
+ - ✅ **Meteora DLMM** - 动态流动性做市商
66
+ - ✅ **Bonk Launchpad** - 代币发射平台
67
+
68
+ ---
69
+
70
+ ## 🔥 快速开始
71
+
72
+ ### 安装
73
+
74
+ ```bash
75
+ git clone https://github.com/0xfnzero/sol-parser-sdk-nodejs
76
+ cd sol-parser-sdk-nodejs
77
+ npm install --ignore-scripts
78
+ npm run build
79
+ ```
80
+
81
+ ### 性能测试
82
+
83
+ 在**本包根目录**(`sol-parser-sdk-ts/`)执行,需先 `npm run build`:
84
+
85
+ ```bash
86
+ # 集成测试:PumpFun + PumpSwap,含账户填充的 DexEvent(与 Rust gRPC 路径一致)
87
+ GRPC_URL=https://solana-yellowstone-grpc.publicnode.com:443 GRPC_TOKEN=你的token npm run test:grpc
88
+
89
+ # PumpFun 详细性能指标(单事件明细 + 每 10 秒统计)
90
+ GRPC_TOKEN=你的token node examples/pumpfun_with_metrics.mjs
91
+
92
+ # PumpSwap 详细性能指标(单事件明细 + 每 10 秒统计)
93
+ GRPC_TOKEN=你的token node examples/pumpswap_with_metrics.mjs
94
+
95
+ # PumpSwap 超低延迟测试
96
+ GRPC_TOKEN=你的token node examples/pumpswap_low_latency.mjs
97
+ ```
98
+
99
+ ### 环境变量(gRPC 示例)
100
+
101
+ | 变量 | 说明 |
102
+ |------|------|
103
+ | **`GRPC_URL`** | Yellowstone gRPC 端点(优先使用)。例:`https://solana-yellowstone-grpc.publicnode.com:443` |
104
+ | **`GRPC_TOKEN`** | 对应端点的 `x-token`(优先使用) |
105
+ | **`GEYSER_ENDPOINT`** | 与 `GRPC_URL` 同义,兼容旧配置 |
106
+ | **`GEYSER_API_TOKEN`** | 与 `GRPC_TOKEN` 同义,兼容旧配置 |
107
+ | **`MAX_EVENTS`** | `*_grpc_json.mjs` 与 `npm run test:grpc`:解析满 N 条事件后退出;`0` 表示持续运行直到 Ctrl+C |
108
+ | **`TIMEOUT_MS`** | 仅 `npm run test:grpc`:运行 N 毫秒后自动退出;`0` 表示不超时(可与 `MAX_EVENTS` 同时设,先满足任一条件即退出) |
109
+ | **`JSON_PRETTY`** | `npm run test:grpc`:设为 `1` 或 `true` 时多行缩进打印(默认单行紧凑 JSON) |
110
+ | **`JSON_MAX_CHARS`** | `npm run test:grpc`:每条事件 JSON 最大字符数;不设或 `0` 表示不截断 |
111
+
112
+ 部分脚本对公共节点带默认 token;生产环境请显式设置 `GRPC_TOKEN`。
113
+
114
+ ### 示例列表
115
+
116
+ 以下命令均在**本包根目录**执行,且已 `npm run build`。
117
+
118
+ | 描述 | 运行命令 | 源码 |
119
+ |------|----------|------|
120
+ | **本包脚本** | | |
121
+ | gRPC 集成测试(PumpFun + PumpSwap,账户填充后的 DexEvent) | `npm run test:grpc` | [scripts/test-grpc-ts.mjs](https://github.com/0xfnzero/sol-parser-sdk-nodejs/blob/main/scripts/test-grpc-ts.mjs) |
122
+ | 调试:打印 meta / 日志结构 | `npm run debug:grpc` | [scripts/debug-grpc-ts.mjs](https://github.com/0xfnzero/sol-parser-sdk-nodejs/blob/main/scripts/debug-grpc-ts.mjs) |
123
+ | **PumpFun** | | |
124
+ | gRPC 订阅并输出**完整 JSON** DexEvent(字段与 Rust 对齐) | `node examples/pumpfun_grpc_json.mjs` | [examples/pumpfun_grpc_json.mjs](https://github.com/0xfnzero/sol-parser-sdk-nodejs/blob/main/examples/pumpfun_grpc_json.mjs) |
125
+ | PumpFun 事件解析 + 性能指标 | `node examples/pumpfun_with_metrics.mjs` | [examples/pumpfun_with_metrics.mjs](https://github.com/0xfnzero/sol-parser-sdk-nodejs/blob/main/examples/pumpfun_with_metrics.mjs) |
126
+ | PumpFun 交易类型过滤 | `node examples/pumpfun_trade_filter.mjs` | [examples/pumpfun_trade_filter.mjs](https://github.com/0xfnzero/sol-parser-sdk-nodejs/blob/main/examples/pumpfun_trade_filter.mjs) |
127
+ | PumpFun 快速连接测试 | `node examples/pumpfun_quick_test.mjs` | [examples/pumpfun_quick_test.mjs](https://github.com/0xfnzero/sol-parser-sdk-nodejs/blob/main/examples/pumpfun_quick_test.mjs) |
128
+ | **PumpSwap** | | |
129
+ | gRPC 订阅并输出**完整 JSON** DexEvent(字段与 Rust 对齐) | `node examples/pumpswap_grpc_json.mjs` | [examples/pumpswap_grpc_json.mjs](https://github.com/0xfnzero/sol-parser-sdk-nodejs/blob/main/examples/pumpswap_grpc_json.mjs) |
130
+ | PumpSwap 事件 + 性能统计 | `node examples/pumpswap_with_metrics.mjs` | [examples/pumpswap_with_metrics.mjs](https://github.com/0xfnzero/sol-parser-sdk-nodejs/blob/main/examples/pumpswap_with_metrics.mjs) |
131
+ | PumpSwap 超低延迟 | `node examples/pumpswap_low_latency.mjs` | [examples/pumpswap_low_latency.mjs](https://github.com/0xfnzero/sol-parser-sdk-nodejs/blob/main/examples/pumpswap_low_latency.mjs) |
132
+ | **Meteora DAMM** | | |
133
+ | Meteora DAMM V2 事件 | `node examples/meteora_damm_grpc.mjs` | [examples/meteora_damm_grpc.mjs](https://github.com/0xfnzero/sol-parser-sdk-nodejs/blob/main/examples/meteora_damm_grpc.mjs) |
134
+ | **多协议** | | |
135
+ | 同时订阅所有 DEX 协议 | `node examples/multi_protocol_grpc.mjs` | [examples/multi_protocol_grpc.mjs](https://github.com/0xfnzero/sol-parser-sdk-nodejs/blob/main/examples/multi_protocol_grpc.mjs) |
136
+ | **工具 / 测试** | | |
137
+ | 验证 onUpdate 同步抛错不会打断 gRPC 流 | `node examples/grpc_onupdate_error_test.mjs` | [examples/grpc_onupdate_error_test.mjs](https://github.com/0xfnzero/sol-parser-sdk-nodejs/blob/main/examples/grpc_onupdate_error_test.mjs) |
138
+ | 通过签名解析交易(RPC,非 gRPC) | `TX_SIGNATURE=<sig> node examples/parse_tx_by_signature.mjs` | [examples/parse_tx_by_signature.mjs](https://github.com/0xfnzero/sol-parser-sdk-nodejs/blob/main/examples/parse_tx_by_signature.mjs) |
139
+
140
+ ### 基本用法
141
+
142
+ **推荐(与 Rust gRPC `parse_logs` 一致):** 在解析 `Program data` 日志后,用订阅里的 **`transactionRaw` + `metaRaw`** 做账户填充,得到完整 `DexEvent`(如 PumpSwap 的 `base_mint`、池子 ATA 等):
143
+
144
+ ```javascript
145
+ import { createRequire } from "module";
146
+ import { fileURLToPath } from "url";
147
+ import path from "path";
148
+
149
+ const __dirname = path.dirname(fileURLToPath(import.meta.url));
150
+ const require = createRequire(import.meta.url);
151
+ const {
152
+ YellowstoneGrpc,
153
+ parseDexEventsFromGrpcTransactionInfo,
154
+ dexEventToJsonString,
155
+ } = require(path.join(__dirname, "../dist/index.js"));
156
+
157
+ const ENDPOINT =
158
+ process.env.GRPC_URL ||
159
+ process.env.GEYSER_ENDPOINT ||
160
+ "https://solana-yellowstone-grpc.publicnode.com:443";
161
+ const X_TOKEN =
162
+ process.env.GRPC_TOKEN || process.env.GEYSER_API_TOKEN || "";
163
+
164
+ const client = new YellowstoneGrpc(ENDPOINT, X_TOKEN);
165
+
166
+ const filter = {
167
+ account_include: [
168
+ "6EF8rrecthR5Dkzon8Nwu78hRvfCKubJ14M5uBEwF6P", // PumpFun
169
+ "pAMMBay6oceH9fJKBRHGP5D4bD4sWpmSwMn52FMfXEA", // PumpSwap(Pump AMM)
170
+ ],
171
+ account_exclude: [],
172
+ account_required: [],
173
+ vote: false,
174
+ failed: false,
175
+ };
176
+
177
+ const sub = await client.subscribeTransactions(filter, {
178
+ onUpdate: (update) => {
179
+ if (!update.transaction?.transaction) return;
180
+ const txInfo = update.transaction.transaction;
181
+ const slot = update.transaction.slot;
182
+ if (!txInfo.transactionRaw || !txInfo.metaRaw) return;
183
+
184
+ const events = parseDexEventsFromGrpcTransactionInfo(txInfo, slot, undefined);
185
+ for (const ev of events) {
186
+ console.log(dexEventToJsonString(ev, 2));
187
+ }
188
+ },
189
+ onError: (err) => console.error("错误:", err.message),
190
+ onEnd: () => console.log("流已结束"),
191
+ });
192
+
193
+ console.log(`已订阅: ${sub.id}`);
194
+ ```
195
+
196
+ **仅日志(更轻):** `parseLogsOnly(logs, signature, slot, blockTimeUs)` 不需要 `transactionRaw`;部分账户字段可能仍为占位零地址,除非自行对 `VersionedTransaction.message` + `meta` 调用 `applyAccountFillsToLogEvents`。
197
+
198
+ ---
199
+
200
+ ## 🏗️ 支持的协议与事件
201
+
202
+ ### 事件类型
203
+ 每个协议均支持:
204
+ - 📈 **交易/兑换事件** - 买入/卖出交易
205
+ - 💧 **流动性事件** - 存入/提取
206
+ - 🏊 **池子事件** - 池子创建/初始化
207
+ - 🎯 **仓位事件** - 开仓/平仓(CLMM)
208
+
209
+ ### PumpFun 事件
210
+ - `PumpFunBuy` - 买入代币
211
+ - `PumpFunSell` - 卖出代币
212
+ - `PumpFunBuyExactSolIn` - 指定 SOL 数量买入
213
+ - `PumpFunCreate` - 创建新代币
214
+ - `PumpFunTrade` - 通用交易(兜底)
215
+
216
+ ### PumpSwap 事件
217
+ - `PumpSwapBuy` - 通过池子买入代币
218
+ - `PumpSwapSell` - 通过池子卖出代币
219
+ - `PumpSwapCreatePool` - 创建流动性池
220
+ - `PumpSwapLiquidityAdded` - 添加流动性
221
+ - `PumpSwapLiquidityRemoved` - 移除流动性
222
+
223
+ ### Raydium 事件
224
+ - `RaydiumAmmV4Swap` - AMM V4 兑换
225
+ - `RaydiumClmmSwap` - CLMM 兑换
226
+ - `RaydiumCpmmSwap` - CPMM 兑换
227
+
228
+ ### Orca 事件
229
+ - `OrcaWhirlpoolSwap` - Whirlpool 兑换
230
+
231
+ ### Meteora 事件
232
+ - `MeteoraDammV2Swap` - DAMM V2 兑换
233
+ - `MeteoraDammV2AddLiquidity` - 添加流动性
234
+ - `MeteoraDammV2RemoveLiquidity` - 移除流动性
235
+ - `MeteoraDammV2CreatePosition` - 创建仓位
236
+ - `MeteoraDammV2ClosePosition` - 关闭仓位
237
+
238
+ ### Bonk 事件
239
+ - `BonkTrade` - Bonk Launchpad 交易
240
+
241
+ ---
242
+
243
+ ## 📁 项目结构
244
+
245
+ ```
246
+ sol-parser-sdk-ts/ (npm 包名:sol-parser-sdk-nodejs)
247
+ ├── src/
248
+ │ ├── core/
249
+ │ │ ├── unified_parser.ts # parseLogsOnly 等
250
+ │ │ ├── dex_event.ts # DexEvent 类型定义
251
+ │ │ ├── rpc_invoke_map.ts # 程序 invoke 映射、accountKeyToBase58
252
+ │ │ └── json_utils.ts # dexEventToJsonString
253
+ │ ├── grpc/
254
+ │ │ ├── client.ts # YellowstoneGrpc 客户端
255
+ │ │ ├── yellowstone_parse.ts # parseDexEventsFromGrpcTransactionInfo
256
+ │ │ └── types.ts # ClientConfig、TransactionFilter 等
257
+ │ ├── rpc_transaction.ts # parseRpcTransaction、applyAccountFillsToLogEvents
258
+ │ ├── logs/
259
+ │ │ └── optimized_matcher.ts # 日志解析(所有协议)
260
+ │ ├── instr/
261
+ │ │ └── *.ts # 指令解析器
262
+ │ └── index.ts # 公共 API 导出
263
+ ├── dist/ # 编译后的 JavaScript
264
+ ├── scripts/
265
+ │ ├── test-grpc-ts.mjs # npm run test:grpc
266
+ │ └── debug-grpc-ts.mjs # npm run debug:grpc
267
+ ├── examples/
268
+ │ ├── pumpfun_grpc_json.mjs
269
+ │ ├── pumpswap_grpc_json.mjs
270
+ │ ├── grpc_onupdate_error_test.mjs
271
+ │ ├── pumpfun_with_metrics.mjs
272
+ │ ├── pumpfun_trade_filter.mjs
273
+ │ ├── pumpfun_quick_test.mjs
274
+ │ ├── pumpswap_with_metrics.mjs
275
+ │ ├── pumpswap_low_latency.mjs
276
+ │ ├── meteora_damm_grpc.mjs
277
+ │ ├── multi_protocol_grpc.mjs
278
+ │ └── parse_tx_by_signature.mjs
279
+ └── package.json
280
+ ```
281
+
282
+ ---
283
+
284
+ ## 🔧 高级用法
285
+
286
+ ### 自定义 gRPC 端点
287
+
288
+ ```javascript
289
+ const ENDPOINT =
290
+ process.env.GRPC_URL ||
291
+ process.env.GEYSER_ENDPOINT ||
292
+ "https://solana-yellowstone-grpc.publicnode.com:443";
293
+ const TOKEN =
294
+ process.env.GRPC_TOKEN || process.env.GEYSER_API_TOKEN || "";
295
+ const client = new YellowstoneGrpc(ENDPOINT, TOKEN);
296
+ ```
297
+
298
+ ### gRPC 辅助 API(与 Rust 对齐)
299
+
300
+ - **`parseDexEventsFromGrpcTransactionInfo(txInfo, slot, options?)`** — 先解析日志,再执行与 Rust gRPC 相同的账户/数据填充。需要 `txInfo.transactionRaw` 与 `txInfo.metaRaw`。
301
+ - **`applyAccountFillsToLogEvents(events, message, meta)`** — 若你已有日志解析结果,用手里的 message + meta 补全字段。
302
+ - **`parseRpcTransaction` / `parseTransactionFromRpc`** — 完整 RPC 交易解析(指令 + 日志 + 填充)。
303
+
304
+ ### 取消订阅
305
+
306
+ ```javascript
307
+ const sub = await client.subscribeTransactions(filter, callbacks);
308
+
309
+ // 稍后取消:
310
+ client.unsubscribe(sub.id);
311
+ ```
312
+
313
+ ### JSON 序列化
314
+
315
+ ```javascript
316
+ const { dexEventToJsonString } = require("./dist/index.js");
317
+
318
+ for (const ev of events) {
319
+ // 正确处理 BigInt 序列化
320
+ console.log(dexEventToJsonString(ev));
321
+ }
322
+ ```
323
+
324
+ ### 性能建议
325
+
326
+ 1. **使用事件过滤** — 按程序 ID 过滤可获得 60-80% 性能提升
327
+ 2. **gRPC 场景优先 `parseDexEventsFromGrpcTransactionInfo`** — 在提供 `transactionRaw` + `metaRaw` 时,`DexEvent` 字段与 Rust SDK 一致(mint、池子账户等)
328
+ 3. **仅解析一次日志** — `parseLogsOnly` 热路径无堆分配
329
+ 4. **避免对 BigInt 使用 JSON.stringify** — 请使用 `dexEventToJsonString`
330
+ 5. **监控延迟** — 生产环境检查 `metadata.grpc_recv_us`
331
+
332
+ ---
333
+
334
+ ## 📄 许可证
335
+
336
+ MIT License
337
+
338
+ ## 📞 联系我们
339
+
340
+ - **仓库**: https://github.com/0xfnzero/sol-parser-sdk-nodejs
341
+ - **官网**: https://fnzero.dev/
342
+ - **Telegram**: https://t.me/fnzero_group
343
+ - **Discord**: https://discord.gg/vuazbGkqQE
@@ -0,0 +1,11 @@
1
+ import type { EventMetadata } from "../core/metadata.js";
2
+ import type { DexEvent } from "../core/dex_event.js";
3
+ import type { AccountData } from "./types.js";
4
+ import type { EventTypeFilter } from "../grpc/types.js";
5
+ export type { AccountData } from "./types.js";
6
+ export { parseNonceAccount, isNonceAccount } from "./nonce.js";
7
+ export { parseTokenAccount } from "./token.js";
8
+ export { parsePumpswapGlobalConfig, parsePumpswapPool, parsePumpswapAccount, isGlobalConfigAccount, isPoolAccount, } from "./pumpswap.js";
9
+ export { hasDiscriminator } from "./utils.js";
10
+ /** 账户数据统一解析入口 */
11
+ export declare function parseAccountUnified(account: AccountData, metadata: EventMetadata, eventTypeFilter?: EventTypeFilter): DexEvent | null;
@@ -0,0 +1,53 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.hasDiscriminator = exports.isPoolAccount = exports.isGlobalConfigAccount = exports.parsePumpswapAccount = exports.parsePumpswapPool = exports.parsePumpswapGlobalConfig = exports.parseTokenAccount = exports.isNonceAccount = exports.parseNonceAccount = void 0;
4
+ exports.parseAccountUnified = parseAccountUnified;
5
+ const program_ids_js_1 = require("../instr/program_ids.js");
6
+ const nonce_js_1 = require("./nonce.js");
7
+ const token_js_1 = require("./token.js");
8
+ const pumpswap_js_1 = require("./pumpswap.js");
9
+ var nonce_js_2 = require("./nonce.js");
10
+ Object.defineProperty(exports, "parseNonceAccount", { enumerable: true, get: function () { return nonce_js_2.parseNonceAccount; } });
11
+ Object.defineProperty(exports, "isNonceAccount", { enumerable: true, get: function () { return nonce_js_2.isNonceAccount; } });
12
+ var token_js_2 = require("./token.js");
13
+ Object.defineProperty(exports, "parseTokenAccount", { enumerable: true, get: function () { return token_js_2.parseTokenAccount; } });
14
+ var pumpswap_js_2 = require("./pumpswap.js");
15
+ Object.defineProperty(exports, "parsePumpswapGlobalConfig", { enumerable: true, get: function () { return pumpswap_js_2.parsePumpswapGlobalConfig; } });
16
+ Object.defineProperty(exports, "parsePumpswapPool", { enumerable: true, get: function () { return pumpswap_js_2.parsePumpswapPool; } });
17
+ Object.defineProperty(exports, "parsePumpswapAccount", { enumerable: true, get: function () { return pumpswap_js_2.parsePumpswapAccount; } });
18
+ Object.defineProperty(exports, "isGlobalConfigAccount", { enumerable: true, get: function () { return pumpswap_js_2.isGlobalConfigAccount; } });
19
+ Object.defineProperty(exports, "isPoolAccount", { enumerable: true, get: function () { return pumpswap_js_2.isPoolAccount; } });
20
+ var utils_js_1 = require("./utils.js");
21
+ Object.defineProperty(exports, "hasDiscriminator", { enumerable: true, get: function () { return utils_js_1.hasDiscriminator; } });
22
+ const ACCOUNT_EVENT_TYPES = [
23
+ "TokenAccount",
24
+ "NonceAccount",
25
+ "AccountPumpSwapGlobalConfig",
26
+ "AccountPumpSwapPool",
27
+ ];
28
+ /** 账户数据统一解析入口 */
29
+ function parseAccountUnified(account, metadata, eventTypeFilter) {
30
+ if (account.data.length === 0)
31
+ return null;
32
+ if (eventTypeFilter?.include_only) {
33
+ const shouldParse = eventTypeFilter.include_only.some((t) => ACCOUNT_EVENT_TYPES.includes(t));
34
+ if (!shouldParse)
35
+ return null;
36
+ }
37
+ if (account.owner === program_ids_js_1.PUMPSWAP_PROGRAM_ID && eventTypeFilter) {
38
+ if (eventTypeFilter.shouldInclude("AccountPumpSwapGlobalConfig") ||
39
+ eventTypeFilter.shouldInclude("AccountPumpSwapPool")) {
40
+ const ev = (0, pumpswap_js_1.parsePumpswapAccount)(account, metadata);
41
+ if (ev)
42
+ return ev;
43
+ }
44
+ }
45
+ if ((0, nonce_js_1.isNonceAccount)(account.data)) {
46
+ if (eventTypeFilter && !eventTypeFilter.shouldInclude("NonceAccount"))
47
+ return null;
48
+ return (0, nonce_js_1.parseNonceAccount)(account, metadata);
49
+ }
50
+ if (eventTypeFilter && !eventTypeFilter.shouldInclude("TokenAccount"))
51
+ return null;
52
+ return (0, token_js_1.parseTokenAccount)(account, metadata);
53
+ }
@@ -0,0 +1,5 @@
1
+ import type { EventMetadata } from "../core/metadata.js";
2
+ import type { DexEvent } from "../core/dex_event.js";
3
+ import type { AccountData } from "./types.js";
4
+ export declare function isNonceAccount(data: Uint8Array): boolean;
5
+ export declare function parseNonceAccount(account: AccountData, metadata: EventMetadata): DexEvent | null;
@@ -0,0 +1,32 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.isNonceAccount = isNonceAccount;
7
+ exports.parseNonceAccount = parseNonceAccount;
8
+ const utils_js_1 = require("./utils.js");
9
+ const bs58_1 = __importDefault(require("bs58"));
10
+ const NONCE_SIZE = 80;
11
+ const NONCE_DISC = Uint8Array.from([1, 0, 0, 0, 1, 0, 0, 0]);
12
+ function isNonceAccount(data) {
13
+ return data.length >= 8 && (0, utils_js_1.hasDiscriminator)(data, NONCE_DISC);
14
+ }
15
+ function parseNonceAccount(account, metadata) {
16
+ const { data } = account;
17
+ if (data.length !== NONCE_SIZE)
18
+ return null;
19
+ const authority = bs58_1.default.encode(data.subarray(8, 40));
20
+ const nonce = bs58_1.default.encode(data.subarray(40, 72));
21
+ const ev = {
22
+ metadata,
23
+ pubkey: account.pubkey,
24
+ executable: account.executable,
25
+ lamports: account.lamports,
26
+ owner: account.owner,
27
+ rent_epoch: account.rent_epoch,
28
+ nonce,
29
+ authority,
30
+ };
31
+ return { NonceAccount: ev };
32
+ }
@@ -0,0 +1,8 @@
1
+ import type { EventMetadata } from "../core/metadata.js";
2
+ import type { DexEvent } from "../core/dex_event.js";
3
+ import type { AccountData } from "./types.js";
4
+ export declare function isGlobalConfigAccount(data: Uint8Array): boolean;
5
+ export declare function isPoolAccount(data: Uint8Array): boolean;
6
+ export declare function parsePumpswapGlobalConfig(account: AccountData, metadata: EventMetadata): DexEvent | null;
7
+ export declare function parsePumpswapPool(account: AccountData, metadata: EventMetadata): DexEvent | null;
8
+ export declare function parsePumpswapAccount(account: AccountData, metadata: EventMetadata): DexEvent | null;
@@ -0,0 +1,192 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.isGlobalConfigAccount = isGlobalConfigAccount;
4
+ exports.isPoolAccount = isPoolAccount;
5
+ exports.parsePumpswapGlobalConfig = parsePumpswapGlobalConfig;
6
+ exports.parsePumpswapPool = parsePumpswapPool;
7
+ exports.parsePumpswapAccount = parsePumpswapAccount;
8
+ const utils_js_1 = require("./utils.js");
9
+ const binary_js_1 = require("../util/binary.js");
10
+ const program_ids_js_1 = require("../instr/program_ids.js");
11
+ const GLOBAL_DISC = Uint8Array.from([149, 8, 156, 202, 160, 252, 176, 217]);
12
+ const POOL_DISC = Uint8Array.from([241, 154, 109, 4, 17, 177, 109, 188]);
13
+ const GLOBAL_BODY = 634;
14
+ const POOL_BODY = 244;
15
+ function isGlobalConfigAccount(data) {
16
+ return (0, utils_js_1.hasDiscriminator)(data, GLOBAL_DISC);
17
+ }
18
+ function isPoolAccount(data) {
19
+ return (0, utils_js_1.hasDiscriminator)(data, POOL_DISC);
20
+ }
21
+ function parsePumpswapGlobalConfig(account, metadata) {
22
+ if (account.data.length < 8 + GLOBAL_BODY)
23
+ return null;
24
+ if (!isGlobalConfigAccount(account.data))
25
+ return null;
26
+ const d = account.data.subarray(8);
27
+ let o = 0;
28
+ const admin = (0, binary_js_1.readPubkey)(d, o);
29
+ if (admin === null)
30
+ return null;
31
+ o += 32;
32
+ const lp_fee_basis_points = (0, binary_js_1.readU64LE)(d, o);
33
+ if (lp_fee_basis_points === null)
34
+ return null;
35
+ o += 8;
36
+ const protocol_fee_basis_points = (0, binary_js_1.readU64LE)(d, o);
37
+ if (protocol_fee_basis_points === null)
38
+ return null;
39
+ o += 8;
40
+ const disable_flags = (0, binary_js_1.readU8)(d, o);
41
+ if (disable_flags === null)
42
+ return null;
43
+ o += 1;
44
+ const protocol_fee_recipients = [];
45
+ for (let i = 0; i < 8; i++) {
46
+ const pk = (0, binary_js_1.readPubkey)(d, o);
47
+ if (pk === null)
48
+ return null;
49
+ protocol_fee_recipients.push(pk);
50
+ o += 32;
51
+ }
52
+ const coin_creator_fee_basis_points = (0, binary_js_1.readU64LE)(d, o);
53
+ if (coin_creator_fee_basis_points === null)
54
+ return null;
55
+ o += 8;
56
+ const admin_set_coin_creator_authority = (0, binary_js_1.readPubkey)(d, o);
57
+ if (admin_set_coin_creator_authority === null)
58
+ return null;
59
+ o += 32;
60
+ const whitelist_pda = (0, binary_js_1.readPubkey)(d, o);
61
+ if (whitelist_pda === null)
62
+ return null;
63
+ o += 32;
64
+ const reserved_fee_recipient = (0, binary_js_1.readPubkey)(d, o);
65
+ if (reserved_fee_recipient === null)
66
+ return null;
67
+ o += 32;
68
+ const mayhem_b = (0, binary_js_1.readU8)(d, o);
69
+ if (mayhem_b === null)
70
+ return null;
71
+ const mayhem_mode_enabled = mayhem_b !== 0;
72
+ o += 1;
73
+ const reserved_fee_recipients = [];
74
+ for (let i = 0; i < 7; i++) {
75
+ const pk = (0, binary_js_1.readPubkey)(d, o);
76
+ if (pk === null)
77
+ return null;
78
+ reserved_fee_recipients.push(pk);
79
+ o += 32;
80
+ }
81
+ const global_config = {
82
+ admin,
83
+ lp_fee_basis_points,
84
+ protocol_fee_basis_points,
85
+ disable_flags,
86
+ protocol_fee_recipients,
87
+ coin_creator_fee_basis_points,
88
+ admin_set_coin_creator_authority,
89
+ whitelist_pda,
90
+ reserved_fee_recipient,
91
+ mayhem_mode_enabled,
92
+ reserved_fee_recipients,
93
+ };
94
+ const ev = {
95
+ metadata,
96
+ pubkey: account.pubkey,
97
+ executable: account.executable,
98
+ lamports: account.lamports,
99
+ owner: account.owner,
100
+ rent_epoch: account.rent_epoch,
101
+ global_config,
102
+ };
103
+ return { PumpSwapGlobalConfigAccount: ev };
104
+ }
105
+ function parsePumpswapPool(account, metadata) {
106
+ if (account.data.length < 8 + POOL_BODY)
107
+ return null;
108
+ if (!isPoolAccount(account.data))
109
+ return null;
110
+ const d = account.data.subarray(8);
111
+ let o = 0;
112
+ const pool_bump = (0, binary_js_1.readU8)(d, o);
113
+ if (pool_bump === null)
114
+ return null;
115
+ o += 1;
116
+ const index = (0, binary_js_1.readU16LE)(d, o);
117
+ if (index === null)
118
+ return null;
119
+ o += 2;
120
+ const creator = (0, binary_js_1.readPubkey)(d, o);
121
+ if (creator === null)
122
+ return null;
123
+ o += 32;
124
+ const base_mint = (0, binary_js_1.readPubkey)(d, o);
125
+ if (base_mint === null)
126
+ return null;
127
+ o += 32;
128
+ const quote_mint = (0, binary_js_1.readPubkey)(d, o);
129
+ if (quote_mint === null)
130
+ return null;
131
+ o += 32;
132
+ const lp_mint = (0, binary_js_1.readPubkey)(d, o);
133
+ if (lp_mint === null)
134
+ return null;
135
+ o += 32;
136
+ const pool_base_token_account = (0, binary_js_1.readPubkey)(d, o);
137
+ if (pool_base_token_account === null)
138
+ return null;
139
+ o += 32;
140
+ const pool_quote_token_account = (0, binary_js_1.readPubkey)(d, o);
141
+ if (pool_quote_token_account === null)
142
+ return null;
143
+ o += 32;
144
+ const lp_supply = (0, binary_js_1.readU64LE)(d, o);
145
+ if (lp_supply === null)
146
+ return null;
147
+ o += 8;
148
+ const coin_creator = (0, binary_js_1.readPubkey)(d, o);
149
+ if (coin_creator === null)
150
+ return null;
151
+ o += 32;
152
+ const mayhem = (0, binary_js_1.readU8)(d, o);
153
+ if (mayhem === null)
154
+ return null;
155
+ o += 1;
156
+ const cashback = (0, binary_js_1.readU8)(d, o);
157
+ if (cashback === null)
158
+ return null;
159
+ const pool = {
160
+ pool_bump,
161
+ index,
162
+ creator,
163
+ base_mint,
164
+ quote_mint,
165
+ lp_mint,
166
+ pool_base_token_account,
167
+ pool_quote_token_account,
168
+ lp_supply,
169
+ coin_creator,
170
+ is_mayhem_mode: mayhem !== 0,
171
+ is_cashback_coin: cashback !== 0,
172
+ };
173
+ const ev = {
174
+ metadata,
175
+ pubkey: account.pubkey,
176
+ executable: account.executable,
177
+ lamports: account.lamports,
178
+ owner: account.owner,
179
+ rent_epoch: account.rent_epoch,
180
+ pool,
181
+ };
182
+ return { PumpSwapPoolAccount: ev };
183
+ }
184
+ function parsePumpswapAccount(account, metadata) {
185
+ if (account.owner !== program_ids_js_1.PUMPSWAP_PROGRAM_ID)
186
+ return null;
187
+ if (isGlobalConfigAccount(account.data))
188
+ return parsePumpswapGlobalConfig(account, metadata);
189
+ if (isPoolAccount(account.data))
190
+ return parsePumpswapPool(account, metadata);
191
+ return null;
192
+ }
@@ -0,0 +1,5 @@
1
+ import type { EventMetadata } from "../core/metadata.js";
2
+ import type { DexEvent } from "../core/dex_event.js";
3
+ import type { AccountData } from "./types.js";
4
+ /** SPL Token 账户解析(快速路径 + 扩展长度回退) */
5
+ export declare function parseTokenAccount(account: AccountData, metadata: EventMetadata): DexEvent | null;