sol-parser-sdk 0.5.17 → 0.5.19

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 (64) hide show
  1. package/README.md +46 -31
  2. package/README_CN.md +45 -30
  3. package/dist/accounts/pumpfun.js +15 -0
  4. package/dist/accounts/pumpswap.js +2 -0
  5. package/dist/core/account_dispatcher_rpc.d.ts +2 -1
  6. package/dist/core/account_dispatcher_rpc.js +94 -9
  7. package/dist/core/account_fill_pumpfun.js +20 -0
  8. package/dist/core/account_fill_pumpswap.d.ts +2 -0
  9. package/dist/core/account_fill_pumpswap.js +55 -6
  10. package/dist/core/account_fill_raydium.js +4 -0
  11. package/dist/core/dex_event.d.ts +149 -0
  12. package/dist/grpc/client.js +2 -0
  13. package/dist/grpc/log_instr_dedup.js +75 -7
  14. package/dist/grpc/order_buffer.d.ts +5 -0
  15. package/dist/grpc/order_buffer.js +53 -17
  16. package/dist/grpc/types.d.ts +4 -1
  17. package/dist/grpc/types.js +23 -0
  18. package/dist/index.d.ts +1 -0
  19. package/dist/index.js +1 -0
  20. package/dist/instr/inner.js +28 -1
  21. package/dist/instr/meteora_damm_ix.js +26 -0
  22. package/dist/instr/meteora_pools_ix.js +17 -9
  23. package/dist/instr/orca_whirlpool_ix.js +13 -9
  24. package/dist/instr/pumpfun_ix.js +84 -0
  25. package/dist/instr/pumpswap_ix.js +85 -10
  26. package/dist/instr/raydium_clmm_ix.js +5 -3
  27. package/dist/instr/raydium_cpmm_ix.js +11 -8
  28. package/dist/logs/meteora_damm.d.ts +7 -0
  29. package/dist/logs/meteora_damm.js +211 -0
  30. package/dist/logs/meteora_dbc.d.ts +3 -0
  31. package/dist/logs/meteora_dbc.js +23 -0
  32. package/dist/logs/meteora_dlmm.js +16 -4
  33. package/dist/logs/optimized_matcher.js +93 -10
  34. package/dist/logs/program_log_discriminators.d.ts +11 -0
  35. package/dist/logs/program_log_discriminators.js +11 -0
  36. package/dist/logs/pump.js +6 -3
  37. package/dist/logs/pump_amm.js +8 -2
  38. package/dist/logs/pump_upgrade.d.ts +68 -0
  39. package/dist/logs/pump_upgrade.js +128 -0
  40. package/dist/logs/raydium_cpmm.js +7 -3
  41. package/dist/rpc_transaction.js +84 -7
  42. package/dist/shredstream/alt_cache.d.ts +26 -0
  43. package/dist/shredstream/alt_cache.js +138 -0
  44. package/dist/shredstream/client.d.ts +11 -3
  45. package/dist/shredstream/client.js +76 -72
  46. package/dist/shredstream/config.d.ts +7 -3
  47. package/dist/shredstream/index.d.ts +3 -2
  48. package/dist/shredstream/index.js +5 -3
  49. package/dist/shredstream/instruction_parse.d.ts +1 -1
  50. package/dist/shredstream/instruction_parse.js +1 -0
  51. package/dist/simulation_route.d.ts +1 -1
  52. package/dist/simulation_route.js +17 -4
  53. package/dist/transaction_route.js +20 -1
  54. package/dist/wire_transaction.js +9 -10
  55. package/docs/USAGE.md +161 -0
  56. package/package.json +2 -5
  57. package/NATIVE_ALIGNMENT.md +0 -312
  58. package/NATIVE_DAMM_FACTORY_ALIGNMENT_20261004.md +0 -16
  59. package/NATIVE_DAMM_FACTORY_EVIDENCE_20261004.json +0 -55
  60. package/NATIVE_MIGRATION.md +0 -38
  61. package/NATIVE_PUMPFUN_NATIVE_DEBIT_ALIGNMENT_20261004.md +0 -18
  62. package/NATIVE_PUMPFUN_NATIVE_DEBIT_EVIDENCE_20261004.json +0 -57
  63. package/NATIVE_PUMPFUN_SETTLEMENT_ALIGNMENT_20261004.md +0 -11
  64. package/NATIVE_PUMPFUN_SETTLEMENT_EVIDENCE_20261004.json +0 -70
@@ -2,10 +2,10 @@
2
2
  * Jito ShredStream 超低延迟订阅(与 Rust `sol-parser-sdk::shredstream` 对齐)。
3
3
  *
4
4
  * 限制(与 Rust `shredstream/mod.rs` 文档一致):
5
- * - 仅静态账户键;使用 ALT 的交易账户列表不完整
5
+ * - ALT 仅使用预热缓存;启用缓存后 miss 会跳过该交易并记录 altCacheMissTransactions,不回放
6
6
  * - 无 inner instructions,无法 CPI 解析
7
7
  * - 无 block_time
8
- * - 无交易日志(program logs);客户端对每条交易走 **外层 `parseInstructionUnified`**(与 gRPC 指令解析同源),可产出 `DexEvent`(V0+ALT 若账户下标超出静态表则用默认 pubkey best-effort)
8
+ * - 无交易日志(program logs);客户端使用外层指令解析。未配置 ALT 缓存时保留静态账户 best-effort 行为。
9
9
  * - `metadata.tx_index` 为**单条 gRPC `Entry` 消息内**跨所有 Solana `Entry` 分组的连续下标(与 golang `shredstream_entries` 扁平 `ti` 对齐),非 slot 级全局序号
10
10
  */
11
11
  export { type ShredStreamConfig, defaultShredStreamConfig, lowLatencyShredStreamConfig, highThroughputShredStreamConfig, } from "./config.js";
@@ -15,3 +15,4 @@ export { fullAccountKeyStringsFromShredTx, loadAddressLookupTableAccounts } from
15
15
  export type { SubscribeEntriesRequest, ShredstreamEntryMessage } from "./proto_types.js";
16
16
  export { bincodeVecEntryCount, decodeEntriesBincodeFlat, decodeEntriesBincodeNested, decodeShredstreamEntriesBincode, type DecodedWireTransaction, } from "./entries_decode.js";
17
17
  export { wireBytesToShredWasmTx } from "./wire_to_shred_tx.js";
18
+ export { AddressLookupTableCache } from "./alt_cache.js";
@@ -1,14 +1,14 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.wireBytesToShredWasmTx = exports.decodeShredstreamEntriesBincode = exports.decodeEntriesBincodeNested = exports.decodeEntriesBincodeFlat = exports.bincodeVecEntryCount = exports.loadAddressLookupTableAccounts = exports.fullAccountKeyStringsFromShredTx = exports.dexEventsFromShredWasmTxWithFullKeys = exports.dexEventsFromShredWasmTx = exports.ShredEventQueue = exports.ShredStreamClient = exports.highThroughputShredStreamConfig = exports.lowLatencyShredStreamConfig = exports.defaultShredStreamConfig = void 0;
3
+ exports.AddressLookupTableCache = exports.wireBytesToShredWasmTx = exports.decodeShredstreamEntriesBincode = exports.decodeEntriesBincodeNested = exports.decodeEntriesBincodeFlat = exports.bincodeVecEntryCount = exports.loadAddressLookupTableAccounts = exports.fullAccountKeyStringsFromShredTx = exports.dexEventsFromShredWasmTxWithFullKeys = exports.dexEventsFromShredWasmTx = exports.ShredEventQueue = exports.ShredStreamClient = exports.highThroughputShredStreamConfig = exports.lowLatencyShredStreamConfig = exports.defaultShredStreamConfig = void 0;
4
4
  /**
5
5
  * Jito ShredStream 超低延迟订阅(与 Rust `sol-parser-sdk::shredstream` 对齐)。
6
6
  *
7
7
  * 限制(与 Rust `shredstream/mod.rs` 文档一致):
8
- * - 仅静态账户键;使用 ALT 的交易账户列表不完整
8
+ * - ALT 仅使用预热缓存;启用缓存后 miss 会跳过该交易并记录 altCacheMissTransactions,不回放
9
9
  * - 无 inner instructions,无法 CPI 解析
10
10
  * - 无 block_time
11
- * - 无交易日志(program logs);客户端对每条交易走 **外层 `parseInstructionUnified`**(与 gRPC 指令解析同源),可产出 `DexEvent`(V0+ALT 若账户下标超出静态表则用默认 pubkey best-effort)
11
+ * - 无交易日志(program logs);客户端使用外层指令解析。未配置 ALT 缓存时保留静态账户 best-effort 行为。
12
12
  * - `metadata.tx_index` 为**单条 gRPC `Entry` 消息内**跨所有 Solana `Entry` 分组的连续下标(与 golang `shredstream_entries` 扁平 `ti` 对齐),非 slot 级全局序号
13
13
  */
14
14
  var config_js_1 = require("./config.js");
@@ -31,3 +31,5 @@ Object.defineProperty(exports, "decodeEntriesBincodeNested", { enumerable: true,
31
31
  Object.defineProperty(exports, "decodeShredstreamEntriesBincode", { enumerable: true, get: function () { return entries_decode_js_1.decodeShredstreamEntriesBincode; } });
32
32
  var wire_to_shred_tx_js_1 = require("./wire_to_shred_tx.js");
33
33
  Object.defineProperty(exports, "wireBytesToShredWasmTx", { enumerable: true, get: function () { return wire_to_shred_tx_js_1.wireBytesToShredWasmTx; } });
34
+ var alt_cache_js_1 = require("./alt_cache.js");
35
+ Object.defineProperty(exports, "AddressLookupTableCache", { enumerable: true, get: function () { return alt_cache_js_1.AddressLookupTableCache; } });
@@ -1,7 +1,7 @@
1
1
  /**
2
2
  * ShredStream:外层编译指令 → `parseInstructionUnified`(与 gRPC 指令解析同源)。
3
3
  * `ShredWasmTx` 来自线格式交易经 `@solana/web3.js` 反序列化(见 `wire_to_shred_tx.ts`),非 WASM。
4
- * 有 `ShredStreamConfig.connection` 时可展开 V0 ALT;无 RPC 时用静态账户表 + 默认 pubkey best-effort。
4
+ * 客户端通过预热 ALT 缓存展开 V0 账户;connection 只供独立刷新使用。
5
5
  */
6
6
  import type { MessageHeader } from "@solana/web3.js";
7
7
  import { type DexEvent } from "../core/dex_event.js";
@@ -22,6 +22,7 @@ const UNKNOWN_PROGRAM_CANDIDATES = [
22
22
  program_ids_js_1.RAYDIUM_AMM_V4_PROGRAM_ID,
23
23
  program_ids_js_1.ORCA_WHIRLPOOL_PROGRAM_ID,
24
24
  program_ids_js_1.METEORA_POOLS_PROGRAM_ID,
25
+ program_ids_js_1.METEORA_DBC_PROGRAM_ID,
25
26
  program_ids_js_1.METEORA_DAMM_V2_PROGRAM_ID,
26
27
  program_ids_js_1.METEORA_DLMM_PROGRAM_ID,
27
28
  ];
@@ -1,6 +1,6 @@
1
1
  import { type TransactionRoute } from "./transaction_route.js";
2
2
  /** Original wire plus simulateTransaction response. Parsed SPL transfers and ATA setup.
3
3
  * Unsupported parsed CPI fail explicitly; raw/compiled CPI are preserved.
4
- * V0 ALT requires the existing compiled route API and loadedAddresses.
4
+ * V0 requires caller-resolved addresses in result.value.loadedAddresses.
5
5
  */
6
6
  export declare function analyzeSimulationRoutes(wire: Uint8Array, response: unknown, graduatedPools?: readonly string[]): TransactionRoute;
@@ -21,12 +21,10 @@ function unsigned(value, bits) {
21
21
  }
22
22
  /** Original wire plus simulateTransaction response. Parsed SPL transfers and ATA setup.
23
23
  * Unsupported parsed CPI fail explicitly; raw/compiled CPI are preserved.
24
- * V0 ALT requires the existing compiled route API and loadedAddresses.
24
+ * V0 requires caller-resolved addresses in result.value.loadedAddresses.
25
25
  */
26
26
  function analyzeSimulationRoutes(wire, response, graduatedPools = []) {
27
27
  const tx = (0, wire_transaction_js_1.decodeWireTransaction)(wire).transaction;
28
- if (tx.message.addressTableLookups.length)
29
- throw Error("V0 ALT addresses unavailable; use compiled route input with loadedAddresses");
30
28
  const root = response, value = root?.result?.value;
31
29
  if (root?.error ||
32
30
  !value ||
@@ -37,7 +35,22 @@ function analyzeSimulationRoutes(wire, response, graduatedPools = []) {
37
35
  if (value.innerInstructions !== null &&
38
36
  !Array.isArray(value.innerInstructions))
39
37
  throw Error("Invalid simulation inner instructions");
40
- const keys = tx.message.accountKeys;
38
+ const lookups = tx.message.addressTableLookups;
39
+ const loaded = value.loadedAddresses ?? { writable: [], readonly: [] };
40
+ if (lookups.length && value.loadedAddresses == null)
41
+ throw Error("V0 ALT addresses unavailable; provide loadedAddresses");
42
+ if (!loaded || typeof loaded !== "object" || Array.isArray(loaded))
43
+ throw Error("Invalid simulation loaded addresses");
44
+ for (const side of ["writable", "readonly"]) {
45
+ const expected = lookups.reduce((n, lookup) => n + lookup[side === "writable" ? "writableIndexes" : "readonlyIndexes"].length, 0);
46
+ if (!Array.isArray(loaded[side]) || loaded[side].length !== expected)
47
+ throw Error("Simulation loaded address count does not match wire lookups");
48
+ for (const key of loaded[side]) {
49
+ if (typeof key !== "string" || bs58_1.default.decode(key).length !== 32)
50
+ throw Error("Invalid simulation loaded public key");
51
+ }
52
+ }
53
+ const keys = [...tx.message.accountKeys, ...loaded.writable, ...loaded.readonly];
41
54
  const index = (key) => {
42
55
  if (typeof key !== "string")
43
56
  throw Error("Missing simulation account key");
@@ -47,10 +47,14 @@ const LAB = new Map([
47
47
  [hex([95, 200, 71, 34, 8, 9, 11, 166]), [false, false]],
48
48
  ]);
49
49
  const PUMP = new Map([
50
+ [hex([184, 23, 238, 97, 103, 197, 211, 61]), [true, false]],
51
+ [hex([194, 171, 28, 70, 104, 77, 91, 47]), [true, true]],
52
+ [hex([93, 246, 130, 60, 231, 233, 64, 178]), [false, true]],
50
53
  [hex([198, 46, 21, 82, 180, 217, 232, 112]), [true, true]],
51
54
  [hex([102, 6, 61, 18, 1, 218, 235, 234]), [true, false]],
52
55
  [hex([51, 230, 133, 164, 1, 127, 131, 173]), [false, true]],
53
56
  ]);
57
+ const CURVE_V3 = new Map([[hex([7, 5, 29, 196, 245, 23, 101, 80]), [true, false]], [hex([225, 247, 80, 30, 213, 179, 132, 136]), [true, true]], [hex([28, 146, 222, 119, 38, 196, 105, 213]), [false, true]]]);
54
58
  const CURVE_V2 = new Map([
55
59
  [hex([194, 171, 28, 70, 104, 77, 91, 47]), [true, true]],
56
60
  [hex([184, 23, 238, 97, 103, 197, 211, 61]), [true, false]],
@@ -132,7 +136,7 @@ function swap(ix, mints, graduated) {
132
136
  (protocol === "LaunchLab" ? LAB : PUMP).has(disc)) {
133
137
  const [buy, input] = (protocol === "LaunchLab" ? LAB : PUMP).get(disc);
134
138
  exact = input;
135
- if (n < (protocol === "LaunchLab" ? 18 : 21))
139
+ if (n < (protocol === "LaunchLab" ? 18 : (["b817ee6167c5d33d", "c2ab1c46684d5b2f", "5df6823ce7e940b2"].includes(disc) ? 17 : 21)))
136
140
  return null;
137
141
  source = a(buy ? 6 : 5);
138
142
  dest = a(buy ? 5 : 6);
@@ -148,6 +152,21 @@ function swap(ix, mints, graduated) {
148
152
  pair = buy ? [a(4), a(3)] : [a(3), a(4)];
149
153
  }
150
154
  }
155
+ else if (protocol === 'PumpFun' && CURVE_V3.has(disc) && n === 17 && d.length >= 24) {
156
+ const [buy, input] = CURVE_V3.get(disc);
157
+ exact = input;
158
+ pool = a(5);
159
+ user = a(8);
160
+ source = a(buy ? 10 : 9);
161
+ dest = a(buy ? 9 : 10);
162
+ pair = buy ? [a(2), a(1)] : [a(1), a(2)];
163
+ if (a(2) === exports.WSOL) {
164
+ if (buy)
165
+ source = user;
166
+ else
167
+ dest = user;
168
+ }
169
+ }
151
170
  else if (protocol === 'PumpFun' && CURVE_V2.has(disc) && n >= 16) {
152
171
  const [buy, input] = CURVE_V2.get(disc);
153
172
  exact = input;
@@ -145,25 +145,24 @@ function decodeWireTransaction(data, offset = 0, requireComplete = true) {
145
145
  throw new Error("Signature count does not match header");
146
146
  }
147
147
  if (header[0] > keys.length ||
148
- header[1] > header[0] ||
148
+ header[1] >= header[0] ||
149
149
  header[2] > keys.length - header[0])
150
150
  throw new Error("Invalid message header");
151
151
  if (version === 1) {
152
- if (header[1] >= header[0])
153
- throw new Error("V1 requires a writable fee payer");
154
152
  if (new Set(keys.map((k) => bs58_1.default.encode(k))).size !== keys.length)
155
153
  throw new Error("Duplicate V1 accounts");
156
- for (const ix of instructions)
157
- if (ix.programIdIndex === 0 ||
158
- ix.programIdIndex >= keys.length ||
159
- ix.accounts.some((i) => i >= keys.length))
160
- throw new Error("Invalid V1 instruction index");
161
154
  }
162
- const accountCount = keys.length + lookups.reduce((n, l) => n + l.writableIndexes.length + l.readonlyIndexes.length, 0);
155
+ let accountCount = keys.length;
156
+ for (const lookup of lookups) {
157
+ const count = lookup.writableIndexes.length + lookup.readonlyIndexes.length;
158
+ if (!count)
159
+ throw new Error("Empty address table lookup");
160
+ accountCount += count;
161
+ }
163
162
  if (accountCount > 256)
164
163
  throw new Error("Too many resolved account keys");
165
164
  for (const ix of instructions)
166
- if (ix.programIdIndex >= keys.length || ix.accounts.some(i => i >= accountCount))
165
+ if (ix.programIdIndex === 0 || ix.programIdIndex >= keys.length || ix.accounts.some(i => i >= accountCount))
167
166
  throw new Error("Invalid instruction index");
168
167
  if (requireComplete && r.pos !== data.length)
169
168
  throw new Error("Trailing transaction bytes");
package/docs/USAGE.md ADDED
@@ -0,0 +1,161 @@
1
+ # Sol Parser SDK nodejs usage
2
+
3
+ [English](../README.md) · [中文](../README_CN.md)
4
+
5
+ - [API compatibility](#api-compatibility)
6
+ - [gRPC account cache](#grpc-cache)
7
+ - [Simulation routes](#simulation-routes)
8
+
9
+ ## API compatibility
10
+
11
+ 以下说明当前公开行为和仍需调用方处理的边界;完整跨语言功能对齐仍在审核。
12
+
13
+ ### 元数据精度
14
+
15
+ Node 的 `EventMetadata.slot`、`tx_index`、`block_time_us`、`grpc_recv_us` 现在是 `bigint`。输入支持 bigint、十进制字符串及安全整数 Number;超出安全整数范围的 Number、浮点数、越界 u64/i64 拒绝。根入口导出 `exactU64`、`exactI64`、`exactInteger`。使用 `dexEventToJsonString` 或 `bigintToJsonReplacer` 输出 JSON;直接 JSON.stringify(bigint) 会失败。
16
+
17
+ ```ts
18
+ const slot = exactU64("18446744073709551615");
19
+ const latency = Number(BigInt(nowUs()) - event.metadata.grpc_recv_us);
20
+ console.log(JSON.stringify(event, bigintToJsonReplacer));
21
+ ```
22
+
23
+ 只在计算显示用延迟时将差值转为 Number。slot、index、时间及金额参与解析和排序时保留精确整数。
24
+
25
+ Go `EventMetadata` 保持 uint64/int64,JSON 输出上述四个字段为十进制字符串;Unmarshal 接受整数文本或十进制字符串,并检查范围,不接受 null/float/bool。Python `EventMetadata` 将十进制字符串归一为 int,拒绝 bool/float/越界;公开 JSON 工具输出上述元数据字段为字符串。各语言省略元数据整数时仍默认 0。未知实际净到账继续使用明确未知值,不能用 0 代替。
26
+
27
+ 其余全部事件整数/JSON 字段的行为审核尚未完成。
28
+
29
+ ### 事件别名
30
+
31
+ 三语言提供 `LaunchLabPoolCreateEvent`、`StonkFunPoolCreateEvent`、`LaunchLabTradeEvent`、`StonkFunTradeEvent`。它们复用已有 RaydiumLaunchlab 结构。平台归属仍需池配置或迁移记录,不能仅凭共享 program ID 断言 StonkFun。
32
+
33
+ ### gRPC 生命周期
34
+
35
+ 一个 client 同时管理一个 DEX 订阅。再次订阅会停止并等待原订阅;动态更新和停止串行进行。Node `sub.join()` / Go `sub.Join()` 等待全部 worker;Python `await client.stop()` 等待任务。Node 使用 grpc-js + proto-loader,安装 SDK 不再包含 Yellowstone Rust N-API 客户端依赖。
36
+
37
+ Node `sub.states` / `sub.status()`、Go `sub.States` / `sub.Status()`、Python `queue.states` / `queue.status()` 提供连接、重连、连续性中断、丢弃及停止状态;保留原事件和错误消费方式。状态通知有界,状态快照可读取当前累计信息。重连成功不会自动清除连续性中断。调用方需中止交易准备,在选定 fork 下重新验证缓存,再恢复 readiness。
38
+
39
+ 节点停止可能使事件消费结束或队列停止产出;Python 队列消费者同时观察状态以终止 get 等待。完整实时缓存恢复、新 array 发现和 commitment/fork 自动关联仍在实现。
40
+
41
+ 示例见 [gRPC 缓存接入](#grpc-cache);PublicNode 接入读取 GRPC_URL/GRPC_TOKEN,不打印凭据。
42
+
43
+
44
+ ### DAMM route 解析
45
+
46
+ 新增 legacy swap/swap2 route;mode 0/1 exact-in,mode 2 第一个金额为期望输出、第二个为最大输入,其它 mode unknown。失败保留意图、实际成交未知;Token-2022 缺净到账证据不填零。历史模拟 expected 新增 DAMM leg,调用方不可依赖旧的空 legs。
47
+
48
+ <a id="grpc-cache"></a>
49
+
50
+ ## gRPC account cache
51
+
52
+ 实时交易数据以 Rust sol-parser-sdk 的 Yellowstone gRPC 接口为准。三个原生 parser 使用相同来源:交易事件提供 pool/mint/方向线索,原始账户事件提供当前字节、owner、slot、write_version,Clock 提供 epoch/时间,BlockMeta 或显式 gRPC unary 提供 blockhash。
53
+
54
+ 此流程不使用 WebSocket。parser 没有 WebSocket 订阅接口;parser 的 SubscriptionHandle/Subscription 管理 gRPC 流。trade-sdk 中另有通用 WebSocket 兼容模块,其实现与测试不是 parser gRPC 能力的证明。
55
+
56
+ | 语言 | parser 实时入口 | 原始账户写入 trade cache |
57
+ | --- | --- | --- |
58
+ | Node.js | YellowstoneGrpc.subscribeDexEvents | SubscriptionAccountCache.updateFromParser(raw) |
59
+ | Python | YellowstoneGrpc.subscribe_dex_events | SubscriptionAccountCache.update_from_parser(raw) |
60
+ | Go | YellowstoneGrpc.SubscribeDexEvents | SubscriptionAccountCache.UpdateRaw(pubkey, owner, data, lamports, slot, writeVersion) |
61
+
62
+ Node/Python 传原始账户事件的 payload,而非最外层 DexEvent wrapper。Go 将事件公钥解析为 solana.PublicKey;解析失败必须返回错误。账户关闭(lamports=0)保留为 tombstone,不复用旧字节。不要从 swap 事件伪造账户完整状态、当前手续费或 slot。
63
+
64
+ 推荐接入顺序:
65
+
66
+ 1. 在 parser 订阅交易与 AccountRawSnapshot;raw snapshot 需要显式开启事件过滤。按已知 pool/config/vault/mint/tick/bin/Clock 地址订阅。路由候选是线索,用户可选择 SOL、WSOL、USDC 或股票作为支付/收款资产。
67
+ 2. 原始账户更新写入选定 fork 的本地 cache,保留真实版本。缺失配置/新 array 必须在交易前通过订阅或冷启动补齐;不会用 WebSocket 或热路径 RPC 自动兜底。
68
+ 3. 冻结 cache.snapshot(),替换为用户的钱包、金额与资产,使用 cached prepare/route。本地校验 owner、pool/mint 连通性、状态、费率、Token-2022 费用及 freshness 后报价和构建。本次买入或卖出是独立交易。
69
+ 4. 模拟可显式调用 simulateTransaction;这是验证步骤。读取模拟结果用 parser 的 simulation_routes 示例,不等于真实发送。
70
+
71
+ gRPC 流不保证首次提供所有静态账户。pool 已更新不等于 mint/config 已新鲜;不得把其它账户 slot 改成 Clock slot。重连后须确认缺失更新和 fork,缓存就绪前暂停构建。缺失数据或过期状态明确失败。
72
+
73
+ ### 完整示例
74
+
75
+ 两个 SDK 安装在各自环境中即可运行,也可分别在对应仓库运行。快照文件是两个示例间的明确接口,安装和运行不需要 Rust。
76
+
77
+ | 语言 | parser 仓库:gRPC 刷新 | trade 仓库:本地构建 |
78
+ | --- | --- | --- |
79
+ | Node.js | npx tsx examples/stonkfun_snapshot_refresh.ts /absolute/snapshot.json --require-pool-update | npx tsx examples/cached_trade.ts /absolute/snapshot.json |
80
+ | Python | python examples/stonkfun_snapshot_refresh.py /absolute/snapshot.json --require-pool-update | python examples/cached_trade.py /absolute/snapshot.json |
81
+ | Go | go run ./examples/stonkfun_snapshot_refresh /absolute/snapshot.json --require-pool-update | go run ./examples/cached_trade /absolute/snapshot.json |
82
+
83
+ 使用与 cached_trade 匹配的 accounts[]、legs[] 完整快照,例如现有 cached_tip_route_* 样本;更换自己的钱包、金额和本次方向。CPMM 的 named snapshot 应交给 cached_cpmm 示例。不要直接用历史快照实时交易。
84
+
85
+ parser 刷新读取 GRPC_URL / GRPC_TOKEN,保留没更新账户的原 slot,可能因 provider 没有发送 pool 更新而超时;trade 默认不联网。需要模拟时为 trade 示例增加 --simulate,并配置 RPC_URL。冷启动与显式模拟允许 RPC,交易报价和构建热路径禁止 RPC。
86
+
87
+ 本文件描述已实现的接入接口;完整自动重连恢复、fork 一致性策略、新 array 发现和全部协议缓存仍须逐项验证。之前十轮报告中的 WebSocket 修复仅属于 trade-sdk 通用模块,不计入这些 gRPC 对齐能力。
88
+
89
+ <a id="simulation-routes"></a>
90
+
91
+ ## Simulation routes
92
+
93
+ 运行trade侧cached_trade示例时,--simulate返回包含innerInstructions的模拟结果。内部SPL转账可能是jsonParsed,原来的compiled route入口仍会拒绝它。专用入口需要**原始交易wire和对应完整simulateTransaction响应**,仅离线处理证据,不查询RPC或重新构建签名交易。
94
+
95
+ ```sh
96
+ # Go
97
+ go run ./examples/simulation_routes solparser/testdata/cached_tip_routes_20261002.json route-buy
98
+ # Node
99
+ npx tsx examples/simulation_routes.ts examples/fixtures/cached_tip_routes_20261002.json route-buy
100
+ # Python
101
+ PYTHONPATH=. python examples/simulation_routes.py examples/fixtures/cached_tip_routes_20261002.json route-buy
102
+ ```
103
+
104
+ ```ts
105
+ import { analyzeSimulationRoutes } from "sol-parser-sdk";
106
+ const route = analyzeSimulationRoutes(wire, simulationResponse);
107
+ ```
108
+
109
+ ```python
110
+ from sol_parser import analyze_simulation_routes
111
+ route = analyze_simulation_routes(wire, simulation_response)
112
+ ```
113
+
114
+ SPL普通/checked/明确fee转账,以及首次ATA的getAccountDataSize/createAccount/initializeImmutableOwner/initializeAccount3的parsed表示被还原为分析证据;raw/compiled CPI原样保留位置和stack height。其它parsed指令明确报错。金额使用最小单位amount字段,不用uiAmount浮点值。缺失Token-2022手续费的净到账保持未知;失败交易实际成交字段为空。tip不会被当成WSOL充值。含ALT的V0需要原有compiled route入口与显式loadedAddresses;此入口不查询lookup table。调用方必须提供与wire对应的可信模拟响应。
115
+
116
+ 保存快照与模拟仅用于复现,不是实时状态。Go parser 已补齐 AnalyzeSimulationRoutes,接受 wire、完整模拟响应的 []byte 和可选 graduatedPools;安装和使用不依赖 Rust。
117
+
118
+ ```go
119
+ route, err := solparser.AnalyzeSimulationRoutes(wire, simulationResponseJSON, nil)
120
+ if err != nil { return err }
121
+ // route.Legs 的 ActualOutputAmount == nil 表示净到账未知,不能当作零。
122
+ ```
123
+
124
+ 当前银行模拟复现:`examples/fixtures/simulation_routes_live_20261002.json`。包括因旧保护价格触发滑点错误的买入和成功卖出;失败时只保留意图,actual amount为空。Go可以用相同文件路径运行上述示例。保存wire用replaceRecentBlockhash重新模拟并不等于当前状态重新报价。
125
+
126
+ 首次 ATA 模拟:`examples/fixtures/simulation_ata_20261002.json`,含 classic SPL、Token-2022 股票和带建账户的单池/三跳卖出。setup指令用于mint推断,不计入成交转账;getAccountDataSize仅支持空列表或immutableOwner扩展,其它扩展明确报错。compiled route入口现在也要求meta.err显式存在,缺失状态不再默认为成功。
127
+
128
+
129
+ 2026-10-03: Prefunded ATA creation is also supported: System transfer, allocate and assign CPI are reconstructed and validated. getAccountDataSize accepts empty/immutableOwner extension lists only; unknown parsed instructions remain errors. Wire and compiled-route indexes, duplicate CPI groups and token-balance indexes are validated before event reconstruction. No RPC is used by the parser. Mainnet prefunded-ATA evidence is covered by the review10 fixture tests.
130
+
131
+
132
+ #### 第二批审查证据(2026-10-03)
133
+
134
+ `fixtures/batch2_cpmm_simulations_20261003.json` 含独立买入/卖出的真实主网模拟响应与原 wire,两个模拟均成功,三语言解析结果一致。买入实际输入 1000000、输出 13486034;卖出实际输入 1000000,但响应未明确 Token-2022 转账费,净到账保持未知。不要用快照报价填充实际成交字段。根目录第二批十轮审查证据同时保留触发滑点保护的延迟快照与历史三跳重放。
135
+
136
+
137
+ ### CPMM LP 模拟中的铸造/销毁 CPI(2026-10-06)
138
+
139
+ 三语言支持 parsed SPL/Token-2022 的 mintTo、burn、mintToChecked、burnChecked,校验精确 u64 金额、u8 decimals、多签账户与原始指令位置。它们保留为供分析的指令,不计入转账或 swap 成交;LP 顶层操作仍可能出现在 unknown_invocations,不意味着已新增 LP 事件 API。首次 ATA、资金准备 swap、LP 操作可在同笔模拟中解析。未知 Token-2022 净到账仍为 null。
140
+
141
+ 真实主网模拟样例:examples/fixtures/cpmm_lp_simulations_20261006.json,包含 LP 存入与取出。在各语言仓库运行:
142
+
143
+ ```sh
144
+ # Go
145
+ go run ./examples/simulation_routes examples/fixtures/cpmm_lp_simulations_20261006.json
146
+ # Node
147
+ npx tsx examples/simulation_routes.ts examples/fixtures/cpmm_lp_simulations_20261006.json
148
+ # Python
149
+ PYTHONPATH=. python examples/simulation_routes.py examples/fixtures/cpmm_lp_simulations_20261006.json
150
+ ```
151
+
152
+ 该样例包含 sigVerify=false 的资金准备前缀,仅作执行验证,不可广播。新交易的完整原始响应可由 Rust CPMM_SIMULATION_DIR 保存后,用相同示例解析。
153
+
154
+
155
+ ## Pump upgrade (October 2026)
156
+
157
+ Compact Pump v3 and PumpSwap v2 trades use their new 17-account layouts. New typed events cover `PumpFunPostCompleteBuy`, `PumpFunComplete`, `PumpFunSweepBondingCurveFee` and `PumpSwapSweepPoolFee`, with program-scoped log and CPI parsing. Historical SOL CompleteEvent payloads remain supported. Curve/pool retained fees and synthetic counters are exposed; optional historical tails default to zero.
158
+
159
+ For a synthetic completing buy, retain TradeEvent **and** PostCompleteBuyEvent and aggregate execution amounts within the same invocation. CompleteEvent is the completion notification. For `multi_hop_swap`, retain each venue's trade events; different venues are not merged into one fill. The multi-hop intent decoder exposes the fixed user accounts, input/minimum limits and 5 roles per hop; these limits are not actual executed amounts. Streamer forwards the typed events and account fields through its parser bridge (its re-exported `parser_sdk` also provides the intent decoder).
160
+
161
+ Reference: [pump-public-docs](https://github.com/pump-fun/pump-public-docs/tree/8cda1fa30ea658b20909d8aedf002047119388d2). Validation uses offline official IDL fixtures; no live trade is sent by the tests.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "sol-parser-sdk",
3
- "version": "0.5.17",
3
+ "version": "0.5.19",
4
4
  "description": "High-performance Solana DEX event parser for Node.js/TypeScript (Yellowstone gRPC, log parsing)",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
@@ -10,10 +10,7 @@
10
10
  "README_CN.md",
11
11
  "LICENSE",
12
12
  ".env.example",
13
- "NATIVE_ALIGNMENT.md",
14
- "NATIVE_MIGRATION.md",
15
- "NATIVE_*_ALIGNMENT_*.md",
16
- "NATIVE_*_EVIDENCE_*.json"
13
+ "docs/USAGE.md"
17
14
  ],
18
15
  "repository": {
19
16
  "type": "git",