sol-parser-sdk 0.5.9 → 0.5.11

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.
@@ -45,51 +45,18 @@ const bs58_1 = __importDefault(require("bs58"));
45
45
  const metadata_js_1 = require("../core/metadata.js");
46
46
  const mod_js_1 = require("../accounts/mod.js");
47
47
  const geyser_connect_js_1 = require("./geyser_connect.js");
48
+ const async_event_queue_js_1 = require("./async_event_queue.js");
48
49
  const order_buffer_js_1 = require("./order_buffer.js");
49
50
  const subscribe_builder_js_1 = require("./subscribe_builder.js");
50
51
  const yellowstone_parse_js_1 = require("./yellowstone_parse.js");
51
52
  const types_js_1 = require("./types.js");
52
- class AsyncEventQueue {
53
- maxSize;
54
- items = [];
55
- waiters = [];
56
- closed = false;
57
- constructor(maxSize) {
58
- this.maxSize = maxSize;
59
- }
60
- push(item) {
61
- if (this.closed)
62
- return;
63
- const waiter = this.waiters.shift();
64
- if (waiter) {
65
- waiter({ value: item, done: false });
66
- return;
67
- }
68
- if (this.items.length < this.maxSize) {
69
- this.items.push(item);
70
- }
71
- }
72
- close() {
73
- if (this.closed)
74
- return;
75
- this.closed = true;
76
- for (const waiter of this.waiters.splice(0)) {
77
- waiter({ value: undefined, done: true });
78
- }
79
- }
80
- next() {
81
- const item = this.items.shift();
82
- if (item !== undefined) {
83
- return Promise.resolve({ value: item, done: false });
84
- }
85
- if (this.closed) {
86
- return Promise.resolve({ value: undefined, done: true });
87
- }
88
- return new Promise((resolve) => this.waiters.push(resolve));
89
- }
90
- [Symbol.asyncIterator]() {
91
- return this;
92
- }
53
+ function cancelSubscribeStream(stream) {
54
+ if (!stream)
55
+ return;
56
+ // grpc-js emits CANCELLED through `error`; guard the short setup window before
57
+ // the normal lifecycle listeners have been installed.
58
+ stream.once("error", () => { });
59
+ stream.cancel();
93
60
  }
94
61
  /** Yellowstone gRPC 客户端包装器 */
95
62
  class YellowstoneGrpc {
@@ -105,6 +72,9 @@ class YellowstoneGrpc {
105
72
  ...gc,
106
73
  keepAliveIntervalMs: config.keep_alive_interval_ms,
107
74
  keepAliveTimeoutMs: config.keep_alive_timeout_ms,
75
+ initialReconnectBackoffMs: config.retry_delay_ms,
76
+ maxReconnectBackoffMs: 60_000,
77
+ flowControlWindowBytes: config.flow_control_window_bytes,
108
78
  }));
109
79
  }
110
80
  /** 连接到 gRPC 服务器 */
@@ -302,6 +272,22 @@ class YellowstoneGrpc {
302
272
  const metadata = (0, metadata_js_1.makeMetadata)(signatureBytes.length > 0 ? bs58_1.default.encode(signatureBytes) : "", Number(update.slot), 0, blockTimeUs, grpcRecvUs);
303
273
  return (0, mod_js_1.parseAccountUnified)(account, metadata, eventTypeFilter);
304
274
  }
275
+ parseAccountEventRaw(update, grpcRecvUs, eventTypeFilter, blockTimeUs) {
276
+ const acc = update.account;
277
+ if (!acc)
278
+ return null;
279
+ const signatureBytes = YellowstoneGrpc.toBytes(acc.txnSignature);
280
+ const account = {
281
+ pubkey: bs58_1.default.encode(YellowstoneGrpc.toBytes(acc.pubkey)),
282
+ executable: acc.executable,
283
+ lamports: YellowstoneGrpc.toBigInt(acc.lamports),
284
+ owner: bs58_1.default.encode(YellowstoneGrpc.toBytes(acc.owner)),
285
+ rent_epoch: YellowstoneGrpc.toBigInt(acc.rentEpoch),
286
+ data: YellowstoneGrpc.toBytes(acc.data),
287
+ };
288
+ const metadata = (0, metadata_js_1.makeMetadata)(signatureBytes.length > 0 ? bs58_1.default.encode(signatureBytes) : "", YellowstoneGrpc.protobufNumber(update.slot), 0, blockTimeUs, grpcRecvUs);
289
+ return (0, mod_js_1.parseAccountUnified)(account, metadata, eventTypeFilter);
290
+ }
305
291
  initialSubscribeRequest(filter) {
306
292
  return {
307
293
  transactions: {
@@ -332,7 +318,7 @@ class YellowstoneGrpc {
332
318
  const streamHolder = { current: null };
333
319
  const cancel = () => {
334
320
  isCancelled = true;
335
- streamHolder.current?.end();
321
+ cancelSubscribeStream(streamHolder.current);
336
322
  streamHolder.current = null;
337
323
  };
338
324
  this.subscribers.set(id, { filter, callbacks, cancel });
@@ -344,6 +330,10 @@ class YellowstoneGrpc {
344
330
  while (!isCancelled) {
345
331
  try {
346
332
  const stream = await this.client.subscribe();
333
+ if (isCancelled) {
334
+ cancelSubscribeStream(stream);
335
+ break;
336
+ }
347
337
  streamHolder.current = stream;
348
338
  await new Promise((resolve, reject) => {
349
339
  stream.write(this.initialSubscribeRequest(filter), (err) => {
@@ -353,7 +343,6 @@ class YellowstoneGrpc {
353
343
  resolve();
354
344
  });
355
345
  });
356
- backoffMs = this.config.retry_delay_ms;
357
346
  await new Promise((resolve, reject) => {
358
347
  const cleanup = () => {
359
348
  stream.removeAllListeners();
@@ -364,6 +353,7 @@ class YellowstoneGrpc {
364
353
  stream.on("data", (update) => {
365
354
  if (isCancelled)
366
355
  return;
356
+ backoffMs = this.config.retry_delay_ms;
367
357
  if (update.ping) {
368
358
  stream.write(this.subscribePingPongRequest(), (werr) => {
369
359
  if (werr && callbacks.onError && !isCancelled) {
@@ -396,6 +386,8 @@ class YellowstoneGrpc {
396
386
  catch (err) {
397
387
  if (isCancelled)
398
388
  break;
389
+ cancelSubscribeStream(streamHolder.current);
390
+ streamHolder.current = null;
399
391
  const e = err instanceof Error ? err : new Error(String(err));
400
392
  callbacks.onError?.(e);
401
393
  if (!autoReconnect)
@@ -415,7 +407,7 @@ class YellowstoneGrpc {
415
407
  }
416
408
  }
417
409
  finally {
418
- streamHolder.current?.end();
410
+ cancelSubscribeStream(streamHolder.current);
419
411
  streamHolder.current = null;
420
412
  this.subscribers.delete(id);
421
413
  }
@@ -432,15 +424,26 @@ class YellowstoneGrpc {
432
424
  * 订阅并直接产出 `DexEvent`。
433
425
  *
434
426
  * 交易更新走统一交易解析(外层/内层指令 + 日志 + 数据填充);账户更新走 `parseAccountUnified`。
435
- * 事件队列满时丢弃新事件,避免回调阻塞 gRPC 读循环,保持低延迟。
427
+ * 事件队列满时按配置丢弃事件,避免回调阻塞 gRPC 读循环并让丢弃可观测。
436
428
  */
437
429
  async subscribeDexEvents(transactionFilters = [], accountFilters = [], eventTypeFilter, options) {
438
430
  await this.connect();
439
431
  const id = `dex_${Date.now()}_${Math.random().toString(36).slice(2)}`;
440
- const events = new AsyncEventQueue(Math.max(1, this.config.buffer_size));
441
- const errors = new AsyncEventQueue(Math.max(1, this.config.buffer_size));
432
+ const events = new async_event_queue_js_1.AsyncEventQueue(Math.max(1, this.config.buffer_size), options?.queueOverflowStrategy);
433
+ const errors = new async_event_queue_js_1.AsyncEventQueue(Math.max(1, this.config.buffer_size));
442
434
  const order = new order_buffer_js_1.OrderDispatcher(this.config);
443
- const emitEvent = (event) => events.push(event);
435
+ let nextDropWarningAt = 0;
436
+ const reportError = (error) => errors.push(error);
437
+ const emitEvent = (event) => {
438
+ if (events.push(event))
439
+ return;
440
+ const now = Date.now();
441
+ if (now < nextDropWarningAt)
442
+ return;
443
+ nextDropWarningAt = now + 10_000;
444
+ reportError(new Error(`Dex event queue full; dropped=${events.dropped()} buffer_size=${this.config.buffer_size} ` +
445
+ `strategy=${options?.queueOverflowStrategy ?? "drop-newest"}`));
446
+ };
444
447
  let flushTimer;
445
448
  if (order.needsTimer) {
446
449
  const intervalMs = this.config.order_mode === "MicroBatch"
@@ -452,9 +455,27 @@ class YellowstoneGrpc {
452
455
  let currentTransactionFilters = transactionFilters;
453
456
  let currentAccountFilters = accountFilters;
454
457
  const streamHolder = { current: null };
458
+ let cancelReconnectDelay;
459
+ const waitBeforeReconnect = (delayMs) => new Promise((resolve) => {
460
+ let settled = false;
461
+ const finish = () => {
462
+ if (settled)
463
+ return;
464
+ settled = true;
465
+ clearTimeout(timer);
466
+ if (cancelReconnectDelay === finish)
467
+ cancelReconnectDelay = undefined;
468
+ resolve();
469
+ };
470
+ const timer = setTimeout(finish, delayMs);
471
+ cancelReconnectDelay = finish;
472
+ if (isCancelled)
473
+ finish();
474
+ });
455
475
  const cancel = () => {
456
476
  isCancelled = true;
457
- streamHolder.current?.end();
477
+ cancelReconnectDelay?.();
478
+ cancelSubscribeStream(streamHolder.current);
458
479
  streamHolder.current = null;
459
480
  if (flushTimer)
460
481
  clearInterval(flushTimer);
@@ -485,6 +506,10 @@ class YellowstoneGrpc {
485
506
  while (!isCancelled) {
486
507
  try {
487
508
  const stream = await this.client.subscribe();
509
+ if (isCancelled) {
510
+ cancelSubscribeStream(stream);
511
+ break;
512
+ }
488
513
  streamHolder.current = stream;
489
514
  await new Promise((resolve, reject) => {
490
515
  stream.write((0, subscribe_builder_js_1.buildSubscribeRequest)(currentTransactionFilters, currentAccountFilters), (err) => {
@@ -494,7 +519,6 @@ class YellowstoneGrpc {
494
519
  resolve();
495
520
  });
496
521
  });
497
- backoffMs = this.config.retry_delay_ms;
498
522
  await new Promise((resolve, reject) => {
499
523
  const cleanup = () => {
500
524
  stream.removeAllListeners();
@@ -505,33 +529,44 @@ class YellowstoneGrpc {
505
529
  stream.on("data", (update) => {
506
530
  if (isCancelled)
507
531
  return;
508
- order.flushDue(emitEvent);
532
+ backoffMs = this.config.retry_delay_ms;
533
+ if (order.needsTimer)
534
+ order.flushDue(emitEvent);
509
535
  if (update.ping) {
510
536
  stream.write(this.subscribePingPongRequest(), (werr) => {
511
537
  if (werr && !isCancelled) {
512
- errors.push(werr instanceof Error ? werr : new Error(String(werr)));
538
+ reportError(werr instanceof Error ? werr : new Error(String(werr)));
513
539
  }
514
540
  });
515
541
  return;
516
542
  }
517
543
  try {
518
544
  const grpcRecvUs = Math.floor(Date.now() * 1000);
519
- const converted = this.convertUpdate(update);
520
- const tx = converted.transaction?.transaction;
545
+ const blockTimeUs = YellowstoneGrpc.timestampToMicros(update.createdAt ?? update.created_at);
546
+ const txUpdate = update.transaction;
547
+ const tx = txUpdate?.transaction;
521
548
  if (tx) {
522
- const fallbackSlot = YellowstoneGrpc.protobufNumber(converted.transaction?.slot ?? 0);
549
+ const slot = txUpdate?.slot ?? 0n;
550
+ const fallbackSlot = YellowstoneGrpc.protobufNumber(slot);
523
551
  const fallbackTxIndex = YellowstoneGrpc.protobufNumber(tx.index ?? 0);
524
- const txEvents = (0, yellowstone_parse_js_1.parseDexEventsFromGrpcTransactionInfo)(tx, converted.transaction?.slot ?? 0n, { blockTimeUs: converted.createdAtUs, grpcRecvUs, eventTypeFilter });
552
+ const txInfo = {
553
+ signature: tx.signature,
554
+ isVote: tx.isVote,
555
+ transactionRaw: tx.transaction,
556
+ metaRaw: tx.meta,
557
+ index: (tx.index ?? 0n),
558
+ };
559
+ const txEvents = (0, yellowstone_parse_js_1.parseDexEventsFromGrpcTransactionInfo)(txInfo, slot, { blockTimeUs, grpcRecvUs, eventTypeFilter });
525
560
  order.pushTransactionEvents(txEvents, fallbackSlot, fallbackTxIndex, emitEvent);
526
561
  }
527
- if (converted.account) {
528
- const ev = this.parseAccountEvent(converted.account, grpcRecvUs, eventTypeFilter, converted.createdAtUs);
562
+ if (update.account) {
563
+ const ev = this.parseAccountEventRaw(update.account, grpcRecvUs, eventTypeFilter, blockTimeUs);
529
564
  if (ev)
530
565
  emitEvent(ev);
531
566
  }
532
567
  }
533
568
  catch (err) {
534
- errors.push(err instanceof Error ? err : new Error(String(err)));
569
+ reportError(err instanceof Error ? err : new Error(String(err)));
535
570
  }
536
571
  });
537
572
  stream.on("error", (err) => {
@@ -547,25 +582,28 @@ class YellowstoneGrpc {
547
582
  catch (err) {
548
583
  if (isCancelled)
549
584
  break;
550
- errors.push(err instanceof Error ? err : new Error(String(err)));
585
+ cancelSubscribeStream(streamHolder.current);
586
+ streamHolder.current = null;
587
+ reportError(err instanceof Error ? err : new Error(String(err)));
551
588
  if (!autoReconnect)
552
589
  break;
553
- await new Promise((r) => setTimeout(r, backoffMs));
590
+ await waitBeforeReconnect(backoffMs);
554
591
  backoffMs = Math.min(backoffMs * 2, maxBackoffMs);
555
592
  continue;
556
593
  }
557
594
  if (isCancelled || !autoReconnect)
558
595
  break;
559
- await new Promise((r) => setTimeout(r, backoffMs));
596
+ await waitBeforeReconnect(backoffMs);
560
597
  backoffMs = Math.min(backoffMs * 2, maxBackoffMs);
561
598
  }
562
599
  }
563
600
  finally {
564
- streamHolder.current?.end();
601
+ cancelSubscribeStream(streamHolder.current);
565
602
  streamHolder.current = null;
566
603
  if (flushTimer)
567
604
  clearInterval(flushTimer);
568
- order.flushAll(emitEvent);
605
+ if (!isCancelled)
606
+ order.flushAll(emitEvent);
569
607
  events.close();
570
608
  errors.close();
571
609
  this.dexSubscribers.delete(id);
@@ -18,8 +18,14 @@ export interface GeyserConnectConfig {
18
18
  keepAliveIntervalMs?: number;
19
19
  /** gRPC keepalive 应答超时(毫秒) */
20
20
  keepAliveTimeoutMs?: number;
21
+ /** gRPC 连接初始重试退避(毫秒) */
22
+ initialReconnectBackoffMs?: number;
23
+ /** gRPC 连接最大重试退避(毫秒) */
24
+ maxReconnectBackoffMs?: number;
25
+ /** HTTP/2 本地接收窗口(字节) */
26
+ flowControlWindowBytes?: number;
21
27
  }
22
- /** 与 Rust `GeyserConnectConfig::default` 一致 */
28
+ /** 公共字段与 Rust `GeyserConnectConfig::default` 一致,并包含 Node gRPC 通道默认值。 */
23
29
  export declare function defaultGeyserConnectConfig(): GeyserConnectConfig;
24
30
  /** 与 `ClientConfig` / `grpc.keepalive_*` 对齐,用于长连接抗 NAT/负载均衡 idle 断开 */
25
31
  export declare function geyserGrpcChannelOptions(config?: GeyserConnectConfig): ChannelOptions;
@@ -7,7 +7,7 @@ exports.defaultGeyserConnectConfig = defaultGeyserConnectConfig;
7
7
  exports.geyserGrpcChannelOptions = geyserGrpcChannelOptions;
8
8
  exports.connectYellowstoneGeyser = connectYellowstoneGeyser;
9
9
  const yellowstone_grpc_1 = __importDefault(require("@triton-one/yellowstone-grpc"));
10
- /** 与 Rust `GeyserConnectConfig::default` 一致 */
10
+ /** 公共字段与 Rust `GeyserConnectConfig::default` 一致,并包含 Node gRPC 通道默认值。 */
11
11
  function defaultGeyserConnectConfig() {
12
12
  return {
13
13
  connectTimeoutMs: 8000,
@@ -15,6 +15,9 @@ function defaultGeyserConnectConfig() {
15
15
  xToken: undefined,
16
16
  keepAliveIntervalMs: 30_000,
17
17
  keepAliveTimeoutMs: 5000,
18
+ initialReconnectBackoffMs: 1000,
19
+ maxReconnectBackoffMs: 60_000,
20
+ flowControlWindowBytes: 1024 * 1024,
18
21
  };
19
22
  }
20
23
  /** 与 `ClientConfig` / `grpc.keepalive_*` 对齐,用于长连接抗 NAT/负载均衡 idle 断开 */
@@ -29,6 +32,9 @@ function geyserGrpcChannelOptions(config = defaultGeyserConnectConfig()) {
29
32
  "grpc.keepalive_timeout_ms": timeout,
30
33
  /** 无活跃 RPC 时仍发 keepalive,避免长时间仅订阅时被中间设备掐断 */
31
34
  "grpc.keepalive_permit_without_calls": 1,
35
+ "grpc.initial_reconnect_backoff_ms": config.initialReconnectBackoffMs,
36
+ "grpc.max_reconnect_backoff_ms": config.maxReconnectBackoffMs,
37
+ "grpc-node.flow_control_window": config.flowControlWindowBytes,
32
38
  };
33
39
  }
34
40
  /**
@@ -62,7 +62,7 @@ function nextOccurrence(base, counts) {
62
62
  counts.set(base, current + 1);
63
63
  return current;
64
64
  }
65
- function dedupeKey(ev, pumpfunLaneCounts) {
65
+ function dedupeKey(ev, occurrenceCounts) {
66
66
  const name = eventName(ev);
67
67
  const data = payload(ev);
68
68
  if (!data)
@@ -70,7 +70,7 @@ function dedupeKey(ev, pumpfunLaneCounts) {
70
70
  if (PUMPFUN_TRADE_NAMES.has(name)) {
71
71
  const lane = ixLane(data.ix_name);
72
72
  const base = `${data.mint}|${data.user}|${Boolean(data.is_buy)}|${lane}`;
73
- const occurrence = nextOccurrence(base, pumpfunLaneCounts);
73
+ const occurrence = nextOccurrence(`PumpFun|${base}`, occurrenceCounts);
74
74
  return `PumpFunTrade|${base}|${occurrence}`;
75
75
  }
76
76
  switch (name) {
@@ -98,12 +98,28 @@ function dedupeKey(ev, pumpfunLaneCounts) {
98
98
  return `PumpSwapLiquidityAdded|${data.pool}|${data.user}`;
99
99
  case "PumpSwapLiquidityRemoved":
100
100
  return `PumpSwapLiquidityRemoved|${data.pool}|${data.user}`;
101
- case "RaydiumClmmSwap":
102
- return `RaydiumClmmSwap|${data.pool_state}|${Boolean(data.zero_for_one)}`;
103
- case "RaydiumAmmV4Swap":
104
- return `RaydiumAmmV4Swap|${data.amm}`;
105
- case "MeteoraDlmmSwap":
106
- return `MeteoraDlmmSwap|${data.pool}|${data.from}|${Boolean(data.swap_for_y)}`;
101
+ case "RaydiumClmmSwap": {
102
+ const base = String(data.pool_state);
103
+ return `RaydiumClmmSwap|${base}|${nextOccurrence(`RaydiumClmm|${base}`, occurrenceCounts)}`;
104
+ }
105
+ case "RaydiumCpmmSwap": {
106
+ const base = String(data.pool_id);
107
+ return `RaydiumCpmmSwap|${base}|${nextOccurrence(`RaydiumCpmm|${base}`, occurrenceCounts)}`;
108
+ }
109
+ case "RaydiumAmmV4Swap": {
110
+ const base = data.max_amount_in !== 0n && data.max_amount_in !== 0
111
+ ? `out|${data.amount_out}`
112
+ : `in|${data.amount_in}`;
113
+ return `RaydiumAmmV4Swap|${base}|${nextOccurrence(`RaydiumAmmV4|${base}`, occurrenceCounts)}`;
114
+ }
115
+ case "OrcaWhirlpoolSwap": {
116
+ const base = String(data.whirlpool);
117
+ return `OrcaWhirlpoolSwap|${base}|${nextOccurrence(`OrcaWhirlpool|${base}`, occurrenceCounts)}`;
118
+ }
119
+ case "MeteoraDlmmSwap": {
120
+ const base = `${data.pool}|${data.from}|${Boolean(data.swap_for_y)}`;
121
+ return `MeteoraDlmmSwap|${base}|${nextOccurrence(`MeteoraDlmm|${base}`, occurrenceCounts)}`;
122
+ }
107
123
  default:
108
124
  return null;
109
125
  }
@@ -282,6 +298,8 @@ function mergeRaydiumAmmV4Swap(log, ix) {
282
298
  "serum_vault_signer",
283
299
  "user_source_token_account",
284
300
  "user_destination_token_account",
301
+ "user_source_owner",
302
+ "amm",
285
303
  ]) {
286
304
  fillString(log, key, ix);
287
305
  }
@@ -376,16 +394,16 @@ function mergeGrpcInstructionIntoLog(logEvent, ixEvent) {
376
394
  function dedupeLogInstructionEvents(logEvents, instructionEvents) {
377
395
  const out = [];
378
396
  const indexByKey = new Map();
379
- const logPumpfunLaneCounts = new Map();
380
- const ixPumpfunLaneCounts = new Map();
397
+ const logOccurrenceCounts = new Map();
398
+ const ixOccurrenceCounts = new Map();
381
399
  for (const ev of logEvents) {
382
- const key = dedupeKey(ev, logPumpfunLaneCounts);
400
+ const key = dedupeKey(ev, logOccurrenceCounts);
383
401
  if (key)
384
402
  indexByKey.set(key, out.length);
385
403
  out.push(ev);
386
404
  }
387
405
  for (const ev of instructionEvents) {
388
- const key = dedupeKey(ev, ixPumpfunLaneCounts);
406
+ const key = dedupeKey(ev, ixOccurrenceCounts);
389
407
  if (!key) {
390
408
  out.push(ev);
391
409
  continue;
@@ -98,16 +98,18 @@ export interface ClientConfig {
98
98
  keep_alive_interval_ms: number;
99
99
  keep_alive_timeout_ms: number;
100
100
  buffer_size: number;
101
+ /** Node gRPC HTTP/2 receive window. Larger values reduce stalls on bursty streams. */
102
+ flow_control_window_bytes?: number;
101
103
  order_mode: OrderMode;
102
104
  order_timeout_ms: number;
103
105
  micro_batch_us: number;
104
106
  }
105
- /** 与 Rust `grpc::config` 中 `StreamingConfig` 别名(即 `ClientConfig`)一致 */
107
+ /** 与 Rust `grpc::config` 中 `StreamingConfig` 别名一致,并包含可选 Node gRPC 参数。 */
106
108
  export type StreamingConfig = ClientConfig;
107
109
  export declare function defaultClientConfig(): ClientConfig;
108
- /** 与 Rust `ClientConfig::low_latency` 一致 */
110
+ /** Rust `ClientConfig::low_latency` 的 Node 调优版本,使用更小的 JS 对象队列。 */
109
111
  export declare function lowLatencyClientConfig(): ClientConfig;
110
- /** 与 Rust `ClientConfig::high_throughput` 一致 */
112
+ /** Rust `ClientConfig::high_throughput` 的 Node 调优版本,使用更小的 JS 对象队列。 */
111
113
  export declare function highThroughputClientConfig(): ClientConfig;
112
114
  export interface TransactionFilter {
113
115
  account_include: string[];
@@ -42,12 +42,13 @@ function defaultClientConfig() {
42
42
  keep_alive_interval_ms: 30000,
43
43
  keep_alive_timeout_ms: 5000,
44
44
  buffer_size: 8192,
45
+ flow_control_window_bytes: 1024 * 1024,
45
46
  order_mode: "Unordered",
46
47
  order_timeout_ms: 100,
47
48
  micro_batch_us: 100,
48
49
  };
49
50
  }
50
- /** 与 Rust `ClientConfig::low_latency` 一致 */
51
+ /** Rust `ClientConfig::low_latency` 的 Node 调优版本,使用更小的 JS 对象队列。 */
51
52
  function lowLatencyClientConfig() {
52
53
  return {
53
54
  enable_metrics: false,
@@ -60,12 +61,13 @@ function lowLatencyClientConfig() {
60
61
  keep_alive_interval_ms: 10_000,
61
62
  keep_alive_timeout_ms: 2000,
62
63
  buffer_size: 16384,
64
+ flow_control_window_bytes: 16 * 1024 * 1024,
63
65
  order_mode: "Unordered",
64
66
  order_timeout_ms: 50,
65
67
  micro_batch_us: 50,
66
68
  };
67
69
  }
68
- /** 与 Rust `ClientConfig::high_throughput` 一致 */
70
+ /** Rust `ClientConfig::high_throughput` 的 Node 调优版本,使用更小的 JS 对象队列。 */
69
71
  function highThroughputClientConfig() {
70
72
  return {
71
73
  enable_metrics: true,
@@ -78,6 +80,7 @@ function highThroughputClientConfig() {
78
80
  keep_alive_interval_ms: 60_000,
79
81
  keep_alive_timeout_ms: 10_000,
80
82
  buffer_size: 32768,
83
+ flow_control_window_bytes: 16 * 1024 * 1024,
81
84
  order_mode: "Unordered",
82
85
  order_timeout_ms: 200,
83
86
  micro_batch_us: 200,
package/dist/index.d.ts CHANGED
@@ -41,7 +41,7 @@ export { parseInstructionUnified, parsePumpfunInstruction, parsePumpswapInstruct
41
41
  /** Rust `instr` 根模块蛇形命名别名 */
42
42
  export { parse_instruction_unified, parse_pumpfun_instruction, parse_pumpswap_instruction, parse_meteora_damm_instruction, parse_meteora_dlmm_instruction, parse_meteora_pools_instruction, parse_pump_fees_instruction, } from "./instr/rust_aliases.js";
43
43
  export * as programIds from "./instr/program_ids.js";
44
- export { YellowstoneGrpc, type SubscribeCallbacks, type DexEventSubscription } from "./grpc/client.js";
44
+ export { YellowstoneGrpc, type SubscribeCallbacks, type DexEventSubscription, type SubscribeDexEventsOptions, type EventQueueOverflowStrategy, } from "./grpc/client.js";
45
45
  export { connectYellowstoneGeyser, defaultGeyserConnectConfig, geyserGrpcChannelOptions, type GeyserConnectConfig, } from "./grpc/geyser_connect.js";
46
46
  /** 与 Rust `sol_parser_sdk::grpc::event_parser` 模块对应 */
47
47
  export * as event_parser from "./grpc/event_parser.js";
@@ -48,7 +48,7 @@ function filterDexEvent(ev, filter) {
48
48
  ? (0, types_js_1.eventTypeFilterNormalizeDexEvent)(filter, ev)
49
49
  : null;
50
50
  }
51
- function pumpFeesEventDisc(disc) {
51
+ function eventCpiDiscriminator(disc) {
52
52
  if (bytesEq(disc, 0, EVENT_CPI_PREFIX))
53
53
  return (0, binary_js_1.readU64LE)(disc, 8);
54
54
  if (bytesEq(disc, 8, EVENT_CPI_SUFFIX))
@@ -116,14 +116,16 @@ const LOG = {
116
116
  METEORA_DAMM_INITIALIZE_POOL: [228, 50, 246, 85, 203, 66, 134, 37],
117
117
  METEORA_DAMM_CREATE_POSITION: [156, 15, 119, 198, 29, 181, 221, 55],
118
118
  METEORA_DAMM_CLOSE_POSITION: [20, 145, 144, 68, 143, 142, 214, 178],
119
- METEORA_DLMM_SWAP: [143, 190, 90, 218, 196, 30, 51, 222],
120
- METEORA_DLMM_ADD_LIQUIDITY: [181, 157, 89, 67, 143, 182, 52, 72],
121
- METEORA_DLMM_REMOVE_LIQUIDITY: [80, 85, 209, 72, 24, 206, 35, 178],
122
- METEORA_DLMM_INITIALIZE_POOL: [95, 180, 10, 172, 84, 174, 232, 40],
119
+ METEORA_DLMM_SWAP: [81, 108, 227, 190, 205, 208, 10, 196],
120
+ METEORA_DLMM_SWAP2: [46, 116, 82, 215, 148, 27, 84, 77],
121
+ METEORA_DLMM_ADD_LIQUIDITY: [31, 94, 125, 90, 227, 52, 61, 186],
122
+ METEORA_DLMM_REMOVE_LIQUIDITY: [116, 244, 97, 232, 103, 31, 152, 58],
123
+ METEORA_DLMM_INITIALIZE_POOL: [185, 74, 252, 125, 27, 215, 188, 111],
123
124
  METEORA_DLMM_INITIALIZE_BIN_ARRAY: [11, 18, 155, 194, 33, 115, 238, 119],
124
- METEORA_DLMM_CREATE_POSITION: [123, 233, 11, 43, 146, 180, 97, 119],
125
- METEORA_DLMM_CLOSE_POSITION: [94, 168, 102, 45, 59, 122, 137, 54],
126
- METEORA_DLMM_CLAIM_FEE: [152, 70, 208, 111, 104, 91, 44, 1],
125
+ METEORA_DLMM_CREATE_POSITION: [144, 142, 252, 84, 157, 53, 37, 121],
126
+ METEORA_DLMM_CLOSE_POSITION: [255, 196, 16, 107, 28, 202, 53, 128],
127
+ METEORA_DLMM_CLAIM_FEE: [75, 122, 154, 48, 140, 74, 123, 163],
128
+ METEORA_DLMM_CLAIM_FEE2: [232, 171, 242, 97, 58, 77, 35, 45],
127
129
  RAYDIUM_LAUNCHLAB_POOL_CREATE: [151, 215, 226, 9, 118, 161, 115, 174],
128
130
  RAYDIUM_LAUNCHLAB_TRADE: [189, 219, 127, 211, 78, 230, 97, 238],
129
131
  };
@@ -200,6 +202,28 @@ const IX = {
200
202
  [80, 85, 209, 72, 24, 206, 177, 108],
201
203
  [95, 180, 10, 172, 84, 174, 232, 40],
202
204
  ],
205
+ METEORA_DLMM: [
206
+ [181, 157, 89, 67, 143, 182, 52, 72],
207
+ [228, 162, 78, 28, 70, 219, 116, 115],
208
+ [169, 32, 79, 137, 136, 232, 70, 137],
209
+ [112, 191, 101, 171, 28, 144, 127, 187],
210
+ [123, 134, 81, 0, 49, 68, 98, 98],
211
+ [174, 90, 35, 115, 186, 40, 147, 226],
212
+ [35, 86, 19, 185, 78, 212, 75, 211],
213
+ [45, 154, 237, 210, 221, 15, 166, 92],
214
+ [73, 59, 36, 120, 237, 83, 108, 198],
215
+ [219, 192, 234, 71, 190, 191, 102, 80],
216
+ [143, 19, 242, 145, 213, 15, 104, 115],
217
+ [46, 82, 125, 146, 85, 141, 228, 153],
218
+ [80, 85, 209, 72, 24, 206, 177, 108],
219
+ [230, 215, 82, 127, 241, 101, 227, 146],
220
+ [248, 198, 158, 145, 225, 117, 135, 200],
221
+ [65, 75, 63, 76, 235, 91, 91, 136],
222
+ [250, 73, 101, 33, 38, 207, 75, 184],
223
+ [43, 215, 247, 132, 137, 60, 243, 81],
224
+ [56, 173, 230, 208, 173, 228, 156, 205],
225
+ [74, 98, 192, 214, 177, 51, 75, 51],
226
+ ],
203
227
  };
204
228
  function firstByteIn(data, allowed) {
205
229
  return data.length > 0 && allowed.includes(data[0]);
@@ -213,7 +237,7 @@ function normalInstructionDataMayParse(programId, data) {
213
237
  if (programId === program_ids_js_1.RAYDIUM_AMM_V4_PROGRAM_ID)
214
238
  return firstByteIn(data, [1, 3, 4, 7, 9, 11]);
215
239
  if (programId === program_ids_js_1.METEORA_DLMM_PROGRAM_ID)
216
- return firstByteIn(data, [0, 1, 2, 7, 8, 11, 13, 14]);
240
+ return headIn(data, IX.METEORA_DLMM);
217
241
  if (programId === program_ids_js_1.METEORA_DAMM_V2_PROGRAM_ID) {
218
242
  return discEq(data, LOG.METEORA_DAMM_INITIALIZE_POOL);
219
243
  }
@@ -243,7 +267,7 @@ function parseInnerCompiledInstructionIfSupported(data, accounts, signature, slo
243
267
  return (0, mod_js_1.parseInstructionUnified)(data, accounts, signature, slot, txIndex, blockTimeUs, grpcRecvUs, filter, programId);
244
268
  }
245
269
  function parsePumpFeesInner(disc, data, metadata) {
246
- const eventDisc = pumpFeesEventDisc(disc);
270
+ const eventDisc = eventCpiDiscriminator(disc);
247
271
  if (eventDisc === null)
248
272
  return null;
249
273
  if (eventDisc === disc8(LOG.PUMP_FEES_CREATE_FEE_SHARING_CONFIG)) {
@@ -431,12 +455,10 @@ function parseInnerInstructionUnified(instructionData, accounts, signature, slot
431
455
  else if (programId === program_ids_js_1.METEORA_DLMM_PROGRAM_ID) {
432
456
  if (filter && !(0, types_js_1.eventTypeFilterIncludesMeteoraDlmm)(filter))
433
457
  return null;
434
- if (!discTailEq(disc, EVENT_CPI_SUFFIX))
458
+ const eventDisc = eventCpiDiscriminator(disc);
459
+ if (eventDisc === null)
435
460
  return null;
436
- const decoded = new Uint8Array(8 + data.length);
437
- decoded.set(disc.subarray(0, 8), 0);
438
- decoded.set(data, 8);
439
- ev = (0, meteora_dlmm_js_1.parseDlmmFromDecoded)(decoded, metadata);
461
+ ev = (0, meteora_dlmm_js_1.parseDlmmEventFromData)(eventDisc, data, metadata);
440
462
  }
441
463
  else if (programId === program_ids_js_1.RAYDIUM_LAUNCHLAB_PROGRAM_ID) {
442
464
  if (filter && !(0, types_js_1.eventTypeFilterIncludesRaydiumLaunchlab)(filter))