sol-parser-sdk-nodejs 0.4.0 → 0.5.5

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 (74) hide show
  1. package/README.md +22 -4
  2. package/README_CN.md +22 -4
  3. package/dist/accounts/mod.d.ts +1 -0
  4. package/dist/accounts/mod.js +53 -6
  5. package/dist/accounts/pumpfun.d.ts +16 -0
  6. package/dist/accounts/pumpfun.js +538 -0
  7. package/dist/accounts/rust_aliases.d.ts +1 -0
  8. package/dist/accounts/rust_aliases.js +3 -1
  9. package/dist/core/account_dispatcher_rpc.js +5 -26
  10. package/dist/core/account_fill_meteora.d.ts +1 -2
  11. package/dist/core/account_fill_meteora.js +0 -2
  12. package/dist/core/account_fill_pumpfun.js +92 -13
  13. package/dist/core/account_fill_pumpswap.js +50 -0
  14. package/dist/core/account_fill_raydium_launchlab.d.ts +4 -0
  15. package/dist/core/account_fill_raydium_launchlab.js +20 -0
  16. package/dist/core/dex_event.d.ts +283 -17
  17. package/dist/core/pumpfun_fee_enrich.d.ts +5 -0
  18. package/dist/core/pumpfun_fee_enrich.js +131 -0
  19. package/dist/core/unified_parser.js +2 -0
  20. package/dist/grpc/client.d.ts +26 -1
  21. package/dist/grpc/client.js +279 -0
  22. package/dist/grpc/log_instr_dedup.d.ts +2 -0
  23. package/dist/grpc/log_instr_dedup.js +378 -0
  24. package/dist/grpc/order_buffer.d.ts +27 -0
  25. package/dist/grpc/order_buffer.js +166 -0
  26. package/dist/grpc/program_ids.d.ts +5 -3
  27. package/dist/grpc/program_ids.js +12 -5
  28. package/dist/grpc/types.d.ts +17 -6
  29. package/dist/grpc/types.js +265 -153
  30. package/dist/grpc/yellowstone_parse.d.ts +3 -1
  31. package/dist/grpc/yellowstone_parse.js +13 -10
  32. package/dist/index.d.ts +9 -7
  33. package/dist/index.js +33 -7
  34. package/dist/instr/meteora_damm_ix.js +4 -1
  35. package/dist/instr/meteora_dlmm_ix.d.ts +2 -0
  36. package/dist/instr/meteora_dlmm_ix.js +134 -0
  37. package/dist/instr/meteora_pools_ix.d.ts +2 -0
  38. package/dist/instr/meteora_pools_ix.js +78 -0
  39. package/dist/instr/mod.d.ts +4 -1
  40. package/dist/instr/mod.js +45 -18
  41. package/dist/instr/program_ids.d.ts +1 -5
  42. package/dist/instr/program_ids.js +4 -6
  43. package/dist/instr/pump_fees_ix.d.ts +2 -0
  44. package/dist/instr/pump_fees_ix.js +166 -0
  45. package/dist/instr/pumpfun_ix.js +272 -1
  46. package/dist/instr/pumpswap_ix.js +36 -2
  47. package/dist/instr/raydium_clmm_ix.js +73 -52
  48. package/dist/instr/raydium_launchlab_ix.d.ts +8 -0
  49. package/dist/instr/raydium_launchlab_ix.js +125 -0
  50. package/dist/instr/rust_aliases.d.ts +3 -0
  51. package/dist/instr/rust_aliases.js +7 -1
  52. package/dist/logs/discriminator_lut.d.ts +1 -1
  53. package/dist/logs/discriminator_lut.js +2 -0
  54. package/dist/logs/optimized_matcher.js +130 -25
  55. package/dist/logs/program_log_discriminators.d.ts +10 -0
  56. package/dist/logs/program_log_discriminators.js +10 -0
  57. package/dist/logs/pump.d.ts +2 -0
  58. package/dist/logs/pump.js +122 -2
  59. package/dist/logs/pump_amm.js +1 -1
  60. package/dist/logs/pump_fees.d.ts +23 -0
  61. package/dist/logs/pump_fees.js +364 -0
  62. package/dist/logs/raydium_launchlab.d.ts +10 -0
  63. package/dist/logs/raydium_launchlab.js +84 -0
  64. package/dist/rpc_transaction.d.ts +2 -0
  65. package/dist/rpc_transaction.js +14 -6
  66. package/dist/shredstream/client.d.ts +4 -1
  67. package/dist/shredstream/client.js +18 -14
  68. package/dist/shredstream/index.d.ts +1 -1
  69. package/dist/shredstream/index.js +1 -1
  70. package/dist/shredstream/instruction_parse.d.ts +3 -3
  71. package/dist/shredstream/instruction_parse.js +38 -13
  72. package/dist/util/market.d.ts +18 -0
  73. package/dist/util/market.js +54 -0
  74. package/package.json +1 -1
@@ -13,7 +13,9 @@ const bs58_1 = __importDefault(require("bs58"));
13
13
  const web3_js_1 = require("@solana/web3.js");
14
14
  const account_dispatcher_rpc_js_1 = require("./core/account_dispatcher_rpc.js");
15
15
  const common_filler_rpc_js_1 = require("./core/common_filler_rpc.js");
16
+ const pumpfun_fee_enrich_js_1 = require("./core/pumpfun_fee_enrich.js");
16
17
  const rpc_invoke_map_js_1 = require("./core/rpc_invoke_map.js");
18
+ const log_instr_dedup_js_1 = require("./grpc/log_instr_dedup.js");
17
19
  const mod_js_1 = require("./instr/mod.js");
18
20
  const optimized_matcher_js_1 = require("./logs/optimized_matcher.js");
19
21
  const DEFAULT_PK = web3_js_1.PublicKey.default.toBase58();
@@ -71,6 +73,7 @@ function applyRpcFills(events, msg, meta) {
71
73
  */
72
74
  function applyAccountFillsToLogEvents(events, msg, meta) {
73
75
  applyRpcFills(events, msg, meta);
76
+ (0, pumpfun_fee_enrich_js_1.enrichPumpfunSameTxPostMerge)(events);
74
77
  }
75
78
  /**
76
79
  * 解析已获取的 `VersionedTransactionResponse`(顺序:指令 → 日志 → 账户/数据填充)。
@@ -88,21 +91,26 @@ function parseRpcTransaction(tx, signature, filter, options) {
88
91
  }
89
92
  const meta = tx.meta ?? null;
90
93
  const slot = tx.slot;
91
- const blockTimeUs = tx.blockTime != null ? tx.blockTime * 1_000_000 : undefined;
94
+ const blockTimeUs = options?.blockTimeUs ?? (tx.blockTime != null ? tx.blockTime * 1_000_000 : undefined);
92
95
  const grpcRecvUs = options?.grpcRecvUs ?? Math.floor(Date.now() * 1000);
96
+ const txIndex = options?.txIndex ?? 0;
93
97
  const rb = recentBlockhashBytes(msg.recentBlockhash);
94
- const events = [];
95
- parseOuterAndInnerInstructions(msg, meta, signature, slot, 0, blockTimeUs, grpcRecvUs, filter, events);
98
+ const instructionEvents = [];
99
+ parseOuterAndInnerInstructions(msg, meta, signature, slot, txIndex, blockTimeUs, grpcRecvUs, filter, instructionEvents);
100
+ const logEvents = [];
96
101
  let isCreatedBuy = false;
97
102
  for (const log of meta?.logMessages ?? []) {
98
- const e = (0, optimized_matcher_js_1.parseLogOptimized)(log, signature, slot, 0, blockTimeUs, grpcRecvUs, filter, isCreatedBuy, rb);
103
+ const e = (0, optimized_matcher_js_1.parseLogOptimized)(log, signature, slot, txIndex, blockTimeUs, grpcRecvUs, filter, isCreatedBuy, rb);
99
104
  if (e) {
100
105
  if ("PumpFunCreate" in e || "PumpFunCreateV2" in e)
101
106
  isCreatedBuy = true;
102
- events.push(e);
107
+ logEvents.push(e);
103
108
  }
104
109
  }
105
- applyRpcFills(events, msg, meta);
110
+ applyRpcFills(instructionEvents, msg, meta);
111
+ applyRpcFills(logEvents, msg, meta);
112
+ const events = (0, log_instr_dedup_js_1.dedupeLogInstructionEvents)(logEvents, instructionEvents);
113
+ (0, pumpfun_fee_enrich_js_1.enrichPumpfunSameTxPostMerge)(events);
106
114
  return { ok: true, events };
107
115
  }
108
116
  var account_dispatcher_rpc_js_2 = require("./core/account_dispatcher_rpc.js");
@@ -1,4 +1,5 @@
1
1
  import { type DexEvent } from "../core/dex_event.js";
2
+ import type { EventTypeFilter } from "../grpc/types.js";
2
3
  import { type ShredStreamConfig } from "./config.js";
3
4
  /** 订阅周期内累计(用于排查「无 gRPC 包 / 解码失败 / 无 DEX 事件」) */
4
5
  export type ShredStreamReceiveStats = {
@@ -48,7 +49,9 @@ export declare class ShredStreamClient {
48
49
  * 订阅 DEX 事件(自动重连);返回队列供轮询消费(与 Rust `subscribe` 一致)。
49
50
  * 重连循环使用订阅时刻的配置快照(与 Rust 在 `tokio::spawn` 前 `config.clone()` 一致)。
50
51
  */
51
- subscribe(): Promise<ShredEventQueue>;
52
+ subscribe(eventTypeFilter?: EventTypeFilter): Promise<ShredEventQueue>;
53
+ /** 与 Rust `subscribe_with_filter` 对齐:在解析热路径按事件类型预过滤。 */
54
+ subscribeWithFilter(eventTypeFilter?: EventTypeFilter): Promise<ShredEventQueue>;
52
55
  /** 停止订阅并中止当前流 */
53
56
  stop(): Promise<void>;
54
57
  private runReconnectLoop;
@@ -193,7 +193,7 @@ class ShredStreamClient {
193
193
  * 订阅 DEX 事件(自动重连);返回队列供轮询消费(与 Rust `subscribe` 一致)。
194
194
  * 重连循环使用订阅时刻的配置快照(与 Rust 在 `tokio::spawn` 前 `config.clone()` 一致)。
195
195
  */
196
- async subscribe() {
196
+ async subscribe(eventTypeFilter) {
197
197
  await this.stop();
198
198
  this.receiveStats = {
199
199
  entryMessagesReceived: 0,
@@ -205,9 +205,13 @@ class ShredStreamClient {
205
205
  const ac = new AbortController();
206
206
  this.loopAbort = ac;
207
207
  const configSnapshot = { ...this.config };
208
- this.loopPromise = this.runReconnectLoop(queue, ac.signal, configSnapshot);
208
+ this.loopPromise = this.runReconnectLoop(queue, ac.signal, configSnapshot, eventTypeFilter);
209
209
  return queue;
210
210
  }
211
+ /** 与 Rust `subscribe_with_filter` 对齐:在解析热路径按事件类型预过滤。 */
212
+ async subscribeWithFilter(eventTypeFilter) {
213
+ return this.subscribe(eventTypeFilter);
214
+ }
211
215
  /** 停止订阅并中止当前流 */
212
216
  async stop() {
213
217
  this.loopAbort?.abort();
@@ -219,7 +223,7 @@ class ShredStreamClient {
219
223
  this.loopPromise = null;
220
224
  }
221
225
  }
222
- async runReconnectLoop(queue, signal, config) {
226
+ async runReconnectLoop(queue, signal, config, eventTypeFilter) {
223
227
  let delay = config.reconnect_delay_ms;
224
228
  let attempts = 0;
225
229
  while (!signal.aborted) {
@@ -230,7 +234,7 @@ class ShredStreamClient {
230
234
  }
231
235
  attempts += 1;
232
236
  try {
233
- await this.streamOnce(queue, signal, config);
237
+ await this.streamOnce(queue, signal, config, eventTypeFilter);
234
238
  delay = config.reconnect_delay_ms;
235
239
  attempts = 0;
236
240
  }
@@ -247,7 +251,7 @@ class ShredStreamClient {
247
251
  }
248
252
  }
249
253
  /** 与 Rust `stream_events` + `connect_with_config`(tonic Endpoint / Grpc 消息上限)一致 */
250
- streamOnce(queue, signal, config) {
254
+ streamOnce(queue, signal, config, eventTypeFilter) {
251
255
  if (signal.aborted)
252
256
  return Promise.resolve();
253
257
  const target = grpcTarget(this.endpoint);
@@ -272,7 +276,7 @@ class ShredStreamClient {
272
276
  if (signal.aborted)
273
277
  return;
274
278
  try {
275
- void this.processEntryMessage(entry, queue);
279
+ void this.processEntryMessage(entry, queue, eventTypeFilter);
276
280
  }
277
281
  catch (err) {
278
282
  console.error("processEntryMessage:", err);
@@ -293,7 +297,7 @@ class ShredStreamClient {
293
297
  });
294
298
  });
295
299
  }
296
- async processEntryMessage(entry, queue) {
300
+ async processEntryMessage(entry, queue, eventTypeFilter) {
297
301
  this.receiveStats.entryMessagesReceived += 1;
298
302
  const recvUs = (0, unified_parser_js_1.nowUs)();
299
303
  const slotNum = toSlotNumber(entry.slot);
@@ -312,18 +316,18 @@ class ShredStreamClient {
312
316
  const conn = this.config.connection;
313
317
  if (conn) {
314
318
  try {
315
- await this.processEntryMessageWithAlt(decoded, slotNum, recvUs, queue, bytes.length, conn);
319
+ await this.processEntryMessageWithAlt(decoded, slotNum, recvUs, queue, bytes.length, conn, eventTypeFilter);
316
320
  }
317
321
  catch (e) {
318
322
  console.warn(`[shredstream] ALT/RPC 解析失败 slot=${slotNum}:`, e);
319
- this.processEntryMessageSync(decoded, slotNum, recvUs, queue, bytes.length);
323
+ this.processEntryMessageSync(decoded, slotNum, recvUs, queue, bytes.length, eventTypeFilter);
320
324
  }
321
325
  return;
322
326
  }
323
- this.processEntryMessageSync(decoded, slotNum, recvUs, queue, bytes.length);
327
+ this.processEntryMessageSync(decoded, slotNum, recvUs, queue, bytes.length, eventTypeFilter);
324
328
  }
325
329
  /** 无 RPC:仅用静态账户表 */
326
- processEntryMessageSync(decoded, slotNum, recvUs, queue, entriesBytesLen) {
330
+ processEntryMessageSync(decoded, slotNum, recvUs, queue, entriesBytesLen, eventTypeFilter) {
327
331
  let txTotal = 0;
328
332
  let evTotal = 0;
329
333
  /** 与 golang `shredstream_entries` 一致:单条 gRPC 消息内跨所有 Solana Entry 的连续下标 */
@@ -336,7 +340,7 @@ class ShredStreamClient {
336
340
  const txIndex = globalTxIndex++;
337
341
  if (!tx?.signature)
338
342
  continue;
339
- const events = (0, instruction_parse_js_1.dexEventsFromShredWasmTx)(tx, slotNum, txIndex, recvUs, undefined);
343
+ const events = (0, instruction_parse_js_1.dexEventsFromShredWasmTx)(tx, slotNum, txIndex, recvUs, eventTypeFilter);
340
344
  evTotal += events.length;
341
345
  for (const ev of events) {
342
346
  setGrpcRecvUsMut(ev, recvUs);
@@ -351,7 +355,7 @@ class ShredStreamClient {
351
355
  }
352
356
  }
353
357
  /** 拉取 ALT 后完整账户表解析 */
354
- async processEntryMessageWithAlt(decoded, slotNum, recvUs, queue, entriesBytesLen, conn) {
358
+ async processEntryMessageWithAlt(decoded, slotNum, recvUs, queue, entriesBytesLen, conn, eventTypeFilter) {
355
359
  const altKeys = new Set();
356
360
  for (const outer of decoded) {
357
361
  for (const tx of outer) {
@@ -373,7 +377,7 @@ class ShredStreamClient {
373
377
  if (!tx?.signature)
374
378
  continue;
375
379
  const fullKeys = (0, alt_lookup_js_1.fullAccountKeyStringsFromShredTx)(tx, altMap);
376
- const events = (0, instruction_parse_js_1.dexEventsFromShredWasmTxWithFullKeys)(tx, fullKeys, slotNum, txIndex, recvUs, undefined);
380
+ const events = (0, instruction_parse_js_1.dexEventsFromShredWasmTxWithFullKeys)(tx, fullKeys, slotNum, txIndex, recvUs, eventTypeFilter);
377
381
  evTotal += events.length;
378
382
  for (const ev of events) {
379
383
  setGrpcRecvUsMut(ev, recvUs);
@@ -5,7 +5,7 @@
5
5
  * - 仅静态账户键;使用 ALT 的交易账户列表不完整
6
6
  * - 无 inner instructions,无法 CPI 解析
7
7
  * - 无 block_time
8
- * - 无交易日志(program logs);客户端对每条交易走 **外层 `parseInstructionUnified`**(与 gRPC 指令解析同源),可产出 `DexEvent`(V0+ALT 若账户下标超出静态表则跳过该指令)
8
+ * - 无交易日志(program logs);客户端对每条交易走 **外层 `parseInstructionUnified`**(与 gRPC 指令解析同源),可产出 `DexEvent`(V0+ALT 若账户下标超出静态表则用默认 pubkey 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";
@@ -8,7 +8,7 @@ exports.wireBytesToShredWasmTx = exports.decodeShredstreamEntriesBincode = expor
8
8
  * - 仅静态账户键;使用 ALT 的交易账户列表不完整
9
9
  * - 无 inner instructions,无法 CPI 解析
10
10
  * - 无 block_time
11
- * - 无交易日志(program logs);客户端对每条交易走 **外层 `parseInstructionUnified`**(与 gRPC 指令解析同源),可产出 `DexEvent`(V0+ALT 若账户下标超出静态表则跳过该指令)
11
+ * - 无交易日志(program logs);客户端对每条交易走 **外层 `parseInstructionUnified`**(与 gRPC 指令解析同源),可产出 `DexEvent`(V0+ALT 若账户下标超出静态表则用默认 pubkey 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");
@@ -1,10 +1,10 @@
1
1
  /**
2
2
  * ShredStream:外层编译指令 → `parseInstructionUnified`(与 gRPC 指令解析同源)。
3
3
  * `ShredWasmTx` 来自线格式交易经 `@solana/web3.js` 反序列化(见 `wire_to_shred_tx.ts`),非 WASM。
4
- * 完整账户列表需含 V0 ALT 展开,见 `alt_lookup.ts` + `ShredStreamConfig.connection`。
4
+ * `ShredStreamConfig.connection` 时可展开 V0 ALT;无 RPC 时用静态账户表 + 默认 pubkey best-effort。
5
5
  */
6
6
  import type { MessageHeader } from "@solana/web3.js";
7
- import type { DexEvent } from "../core/dex_event.js";
7
+ import { type DexEvent } from "../core/dex_event.js";
8
8
  import type { EventTypeFilter } from "../grpc/types.js";
9
9
  export type ShredWasmCompiledIx = {
10
10
  programIdIndex: number;
@@ -30,5 +30,5 @@ export type ShredWasmTx = {
30
30
  * 使用已解析的完整账户表(静态+ALT)解析外层指令。
31
31
  */
32
32
  export declare function dexEventsFromShredWasmTxWithFullKeys(tx: ShredWasmTx, fullAccountKeys: string[], slot: number, txIndex: number, grpcRecvUs: number, eventTypeFilter?: EventTypeFilter): DexEvent[];
33
- /** 仅静态账户表(无 RPC 时;V0+ALT 交易多数指令无法解析) */
33
+ /** 仅静态账户表(无 RPC 时;V0+ALT 缺失账户以默认 pubkey 占位) */
34
34
  export declare function dexEventsFromShredWasmTx(tx: ShredWasmTx, slot: number, txIndex: number, grpcRecvUs: number, eventTypeFilter?: EventTypeFilter): DexEvent[];
@@ -2,12 +2,37 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.dexEventsFromShredWasmTxWithFullKeys = dexEventsFromShredWasmTxWithFullKeys;
4
4
  exports.dexEventsFromShredWasmTx = dexEventsFromShredWasmTx;
5
+ const dex_event_js_1 = require("../core/dex_event.js");
6
+ const pumpfun_fee_enrich_js_1 = require("../core/pumpfun_fee_enrich.js");
5
7
  const mod_js_1 = require("../instr/mod.js");
8
+ const program_ids_js_1 = require("../instr/program_ids.js");
6
9
  function asU8(b) {
7
10
  if (b instanceof Uint8Array)
8
11
  return b;
9
12
  return Uint8Array.from(b);
10
13
  }
14
+ const UNKNOWN_PROGRAM_CANDIDATES = [
15
+ program_ids_js_1.PUMPFUN_PROGRAM_ID,
16
+ program_ids_js_1.PUMPSWAP_PROGRAM_ID,
17
+ program_ids_js_1.PUMP_FEES_PROGRAM_ID,
18
+ program_ids_js_1.RAYDIUM_LAUNCHLAB_PROGRAM_ID,
19
+ program_ids_js_1.RAYDIUM_CPMM_PROGRAM_ID,
20
+ program_ids_js_1.RAYDIUM_CLMM_PROGRAM_ID,
21
+ program_ids_js_1.RAYDIUM_AMM_V4_PROGRAM_ID,
22
+ program_ids_js_1.ORCA_WHIRLPOOL_PROGRAM_ID,
23
+ program_ids_js_1.METEORA_POOLS_PROGRAM_ID,
24
+ program_ids_js_1.METEORA_DAMM_V2_PROGRAM_ID,
25
+ program_ids_js_1.METEORA_DLMM_PROGRAM_ID,
26
+ ];
27
+ const SHRED_DEFAULT_PUBKEY = (0, dex_event_js_1.defaultPubkey)();
28
+ function ixAccountStrings(fullAccountKeys, accBytes) {
29
+ const accountStrs = [];
30
+ for (let i = 0; i < accBytes.length; i++) {
31
+ const ai = accBytes[i];
32
+ accountStrs.push(ai < fullAccountKeys.length ? fullAccountKeys[ai] : SHRED_DEFAULT_PUBKEY);
33
+ }
34
+ return accountStrs;
35
+ }
11
36
  /**
12
37
  * 使用已解析的完整账户表(静态+ALT)解析外层指令。
13
38
  */
@@ -18,30 +43,30 @@ function dexEventsFromShredWasmTxWithFullKeys(tx, fullAccountKeys, slot, txIndex
18
43
  const out = [];
19
44
  for (const rawIx of ixs) {
20
45
  const pidIdx = rawIx.programIdIndex;
21
- if (!Number.isFinite(pidIdx) || pidIdx < 0 || pidIdx >= fullAccountKeys.length)
46
+ if (!Number.isFinite(pidIdx) || pidIdx < 0)
22
47
  continue;
23
- const programId = fullAccountKeys[pidIdx];
24
48
  const accBytes = asU8(rawIx.accounts);
25
49
  const data = asU8(rawIx.data);
26
- const accountStrs = [];
27
- let oob = false;
28
- for (let i = 0; i < accBytes.length; i++) {
29
- const ai = accBytes[i];
30
- if (ai >= fullAccountKeys.length) {
31
- oob = true;
32
- break;
50
+ const accountStrs = ixAccountStrings(fullAccountKeys, accBytes);
51
+ if (pidIdx >= fullAccountKeys.length) {
52
+ for (const programId of UNKNOWN_PROGRAM_CANDIDATES) {
53
+ const ev = (0, mod_js_1.parseInstructionUnified)(data, accountStrs, tx.signature, slot, txIndex, undefined, grpcRecvUs, eventTypeFilter, programId);
54
+ if (ev) {
55
+ out.push(ev);
56
+ break;
57
+ }
33
58
  }
34
- accountStrs.push(fullAccountKeys[ai]);
35
- }
36
- if (oob)
37
59
  continue;
60
+ }
61
+ const programId = fullAccountKeys[pidIdx];
38
62
  const ev = (0, mod_js_1.parseInstructionUnified)(data, accountStrs, tx.signature, slot, txIndex, undefined, grpcRecvUs, eventTypeFilter, programId);
39
63
  if (ev)
40
64
  out.push(ev);
41
65
  }
66
+ (0, pumpfun_fee_enrich_js_1.enrichPumpfunSameTxPostMerge)(out);
42
67
  return out;
43
68
  }
44
- /** 仅静态账户表(无 RPC 时;V0+ALT 交易多数指令无法解析) */
69
+ /** 仅静态账户表(无 RPC 时;V0+ALT 缺失账户以默认 pubkey 占位) */
45
70
  function dexEventsFromShredWasmTx(tx, slot, txIndex, grpcRecvUs, eventTypeFilter) {
46
71
  return dexEventsFromShredWasmTxWithFullKeys(tx, tx.accounts, slot, txIndex, grpcRecvUs, eventTypeFilter);
47
72
  }
@@ -0,0 +1,18 @@
1
+ export type NumericAmount = bigint | number | string;
2
+ export type NormalizedTradeSide = "Buy" | "Sell";
3
+ /**
4
+ * Convert a Q64.64 sqrt price into quote-token units per one base token.
5
+ */
6
+ export declare function sqrtPriceX64ToPrice(sqrtPriceX64: NumericAmount, baseDecimals: number, quoteDecimals: number): number;
7
+ /**
8
+ * Compute quote-token price per one base token from raw vault balances.
9
+ */
10
+ export declare function vaultPriceFromBalances(baseRaw: NumericAmount, quoteRaw: NumericAmount, baseDecimals: number, quoteDecimals: number): number | undefined;
11
+ /**
12
+ * Positive watched-token delta means Buy; negative means Sell.
13
+ */
14
+ export declare function normalizeBuySellFromTokenDelta(tokenDelta: NumericAmount): NormalizedTradeSide | undefined;
15
+ /**
16
+ * If input is quote, the user buys base. If input is base, the user sells base.
17
+ */
18
+ export declare function normalizeBuySellFromInputMint(inputMint: string, baseMint: string, quoteMint: string): NormalizedTradeSide | undefined;
@@ -0,0 +1,54 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.sqrtPriceX64ToPrice = sqrtPriceX64ToPrice;
4
+ exports.vaultPriceFromBalances = vaultPriceFromBalances;
5
+ exports.normalizeBuySellFromTokenDelta = normalizeBuySellFromTokenDelta;
6
+ exports.normalizeBuySellFromInputMint = normalizeBuySellFromInputMint;
7
+ function amountToNumber(value) {
8
+ if (typeof value === "bigint")
9
+ return Number(value);
10
+ if (typeof value === "number")
11
+ return value;
12
+ if (value.trim() === "")
13
+ throw new Error("amount must not be empty");
14
+ return Number(value);
15
+ }
16
+ /**
17
+ * Convert a Q64.64 sqrt price into quote-token units per one base token.
18
+ */
19
+ function sqrtPriceX64ToPrice(sqrtPriceX64, baseDecimals, quoteDecimals) {
20
+ const sqrt = amountToNumber(sqrtPriceX64) / 2 ** 64;
21
+ return sqrt * sqrt * 10 ** (baseDecimals - quoteDecimals);
22
+ }
23
+ /**
24
+ * Compute quote-token price per one base token from raw vault balances.
25
+ */
26
+ function vaultPriceFromBalances(baseRaw, quoteRaw, baseDecimals, quoteDecimals) {
27
+ const base = amountToNumber(baseRaw);
28
+ if (base === 0)
29
+ return undefined;
30
+ return (amountToNumber(quoteRaw) / base) * 10 ** (baseDecimals - quoteDecimals);
31
+ }
32
+ /**
33
+ * Positive watched-token delta means Buy; negative means Sell.
34
+ */
35
+ function normalizeBuySellFromTokenDelta(tokenDelta) {
36
+ const delta = amountToNumber(tokenDelta);
37
+ if (delta > 0)
38
+ return "Buy";
39
+ if (delta < 0)
40
+ return "Sell";
41
+ return undefined;
42
+ }
43
+ /**
44
+ * If input is quote, the user buys base. If input is base, the user sells base.
45
+ */
46
+ function normalizeBuySellFromInputMint(inputMint, baseMint, quoteMint) {
47
+ if (baseMint === quoteMint)
48
+ return undefined;
49
+ if (inputMint === quoteMint)
50
+ return "Buy";
51
+ if (inputMint === baseMint)
52
+ return "Sell";
53
+ return undefined;
54
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "sol-parser-sdk-nodejs",
3
- "version": "0.4.0",
3
+ "version": "0.5.5",
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",