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
@@ -0,0 +1,131 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.enrichCreateV2FromCreateEvents = enrichCreateV2FromCreateEvents;
4
+ exports.enrichCreateV2ObservedFeeRecipient = enrichCreateV2ObservedFeeRecipient;
5
+ exports.enrichPumpfunTradesFromCreateInstructions = enrichPumpfunTradesFromCreateInstructions;
6
+ exports.enrichPumpfunSameTxPostMerge = enrichPumpfunSameTxPostMerge;
7
+ const dex_event_js_1 = require("./dex_event.js");
8
+ function pumpfunTradeEvent(ev) {
9
+ if ("PumpFunTrade" in ev)
10
+ return ev.PumpFunTrade;
11
+ if ("PumpFunBuy" in ev)
12
+ return ev.PumpFunBuy;
13
+ if ("PumpFunSell" in ev)
14
+ return ev.PumpFunSell;
15
+ if ("PumpFunBuyExactSolIn" in ev)
16
+ return ev.PumpFunBuyExactSolIn;
17
+ return null;
18
+ }
19
+ function pumpfunCreateFlags(ev) {
20
+ let c = null;
21
+ if ("PumpFunCreate" in ev)
22
+ c = ev.PumpFunCreate;
23
+ if ("PumpFunCreateV2" in ev)
24
+ c = ev.PumpFunCreateV2;
25
+ if (!c || !c.mint || c.mint === (0, dex_event_js_1.defaultPubkey)())
26
+ return null;
27
+ return [c.mint, c.is_cashback_enabled, c.is_mayhem_mode];
28
+ }
29
+ function fillString(dst, key, value) {
30
+ const zero = (0, dex_event_js_1.defaultPubkey)();
31
+ if ((!dst[key] || dst[key] === zero) && value && value !== zero) {
32
+ dst[key] = value;
33
+ }
34
+ }
35
+ function fillBigint(dst, key, value) {
36
+ if (dst[key] === 0n && value !== 0n) {
37
+ dst[key] = value;
38
+ }
39
+ }
40
+ function enrichCreateV2FromCreateEvents(events) {
41
+ const zero = (0, dex_event_js_1.defaultPubkey)();
42
+ const creates = new Map();
43
+ for (const ev of events) {
44
+ if (!("PumpFunCreate" in ev))
45
+ continue;
46
+ const c = ev.PumpFunCreate;
47
+ if (c.mint && c.mint !== zero && !creates.has(c.mint)) {
48
+ creates.set(c.mint, c);
49
+ }
50
+ }
51
+ if (creates.size === 0)
52
+ return;
53
+ for (const ev of events) {
54
+ if (!("PumpFunCreateV2" in ev))
55
+ continue;
56
+ const c2 = ev.PumpFunCreateV2;
57
+ const c = creates.get(c2.mint);
58
+ if (!c)
59
+ continue;
60
+ fillString(c2, "name", c.name);
61
+ fillString(c2, "symbol", c.symbol);
62
+ fillString(c2, "uri", c.uri);
63
+ fillString(c2, "bonding_curve", c.bonding_curve);
64
+ fillString(c2, "user", c.user);
65
+ fillString(c2, "creator", c.creator);
66
+ fillString(c2, "token_program", c.token_program);
67
+ fillString(c2, "quote_mint", c.quote_mint);
68
+ fillBigint(c2, "timestamp", c.timestamp);
69
+ fillBigint(c2, "virtual_token_reserves", c.virtual_token_reserves);
70
+ fillBigint(c2, "virtual_sol_reserves", c.virtual_sol_reserves);
71
+ fillBigint(c2, "real_token_reserves", c.real_token_reserves);
72
+ fillBigint(c2, "token_total_supply", c.token_total_supply);
73
+ fillBigint(c2, "virtual_quote_reserves", c.virtual_quote_reserves);
74
+ c2.is_mayhem_mode ||= c.is_mayhem_mode;
75
+ c2.is_cashback_enabled ||= c.is_cashback_enabled;
76
+ }
77
+ }
78
+ function enrichCreateV2ObservedFeeRecipient(events) {
79
+ const zero = (0, dex_event_js_1.defaultPubkey)();
80
+ const mintToFee = new Map();
81
+ for (const ev of events) {
82
+ const t = pumpfunTradeEvent(ev);
83
+ if (!t || !t.mint || t.mint === zero || !t.fee_recipient || t.fee_recipient === zero) {
84
+ continue;
85
+ }
86
+ const buyLike = ("PumpFunTrade" in ev && t.is_buy) ||
87
+ "PumpFunBuy" in ev ||
88
+ "PumpFunBuyExactSolIn" in ev;
89
+ if (buyLike && !mintToFee.has(t.mint)) {
90
+ mintToFee.set(t.mint, t.fee_recipient);
91
+ }
92
+ }
93
+ if (mintToFee.size === 0)
94
+ return;
95
+ for (const ev of events) {
96
+ if (!("PumpFunCreateV2" in ev))
97
+ continue;
98
+ const c = ev.PumpFunCreateV2;
99
+ if (!c.observed_fee_recipient || c.observed_fee_recipient === zero) {
100
+ c.observed_fee_recipient = mintToFee.get(c.mint) ?? c.observed_fee_recipient;
101
+ }
102
+ }
103
+ }
104
+ function enrichPumpfunTradesFromCreateInstructions(events) {
105
+ const flags = new Map();
106
+ for (const ev of events) {
107
+ const f = pumpfunCreateFlags(ev);
108
+ if (f && !flags.has(f[0]))
109
+ flags.set(f[0], [f[1], f[2]]);
110
+ }
111
+ if (flags.size === 0)
112
+ return;
113
+ for (const ev of events) {
114
+ const t = pumpfunTradeEvent(ev);
115
+ if (!t || !t.mint || t.mint === (0, dex_event_js_1.defaultPubkey)())
116
+ continue;
117
+ const f = flags.get(t.mint);
118
+ if (!f)
119
+ continue;
120
+ const [cashbackEnabled, mayhemMode] = f;
121
+ t.is_cashback_coin ||= cashbackEnabled;
122
+ t.mayhem_mode ||= mayhemMode;
123
+ if (cashbackEnabled)
124
+ t.track_volume = true;
125
+ }
126
+ }
127
+ function enrichPumpfunSameTxPostMerge(events) {
128
+ enrichCreateV2FromCreateEvents(events);
129
+ enrichCreateV2ObservedFeeRecipient(events);
130
+ enrichPumpfunTradesFromCreateInstructions(events);
131
+ }
@@ -8,6 +8,7 @@ exports.parseTransactionEventsStreaming = parseTransactionEventsStreaming;
8
8
  exports.parseLogsStreaming = parseLogsStreaming;
9
9
  exports.parseTransactionWithStreamingListener = parseTransactionWithStreamingListener;
10
10
  exports.parseLog = parseLog;
11
+ const pumpfun_fee_enrich_js_1 = require("./pumpfun_fee_enrich.js");
11
12
  const optimized_matcher_js_1 = require("../logs/optimized_matcher.js");
12
13
  const clock_js_1 = require("./clock.js");
13
14
  Object.defineProperty(exports, "nowUs", { enumerable: true, get: function () { return clock_js_1.nowUs; } });
@@ -22,6 +23,7 @@ function parseLogsOnly(logs, signature, slot, blockTimeUs, txIndex = 0) {
22
23
  if (e)
23
24
  out.push(e);
24
25
  }
26
+ (0, pumpfun_fee_enrich_js_1.enrichPumpfunSameTxPostMerge)(out);
25
27
  return out;
26
28
  }
27
29
  function parseTransactionWithListener(instructionData, accounts, logs, signature, slot, txIndex, blockTimeUs, programId, listener) {
@@ -2,14 +2,22 @@
2
2
  * Yellowstone gRPC 客户端实现 - 基于 @triton-one/yellowstone-grpc
3
3
  */
4
4
  import { CommitmentLevel } from "@triton-one/yellowstone-grpc";
5
- import { type ClientConfig, type TransactionFilter, type SubscribeCallbacks } from "./types.js";
5
+ import type { DexEvent } from "../core/dex_event.js";
6
+ import { type AccountFilter, type ClientConfig, type EventTypeFilter, type TransactionFilter, type SubscribeCallbacks } from "./types.js";
6
7
  export { SubscribeCallbacks };
8
+ export interface DexEventSubscription extends AsyncIterable<DexEvent> {
9
+ id: string;
10
+ errors: AsyncIterable<Error>;
11
+ cancel(): void;
12
+ next(): Promise<IteratorResult<DexEvent>>;
13
+ }
7
14
  /** Yellowstone gRPC 客户端包装器 */
8
15
  export declare class YellowstoneGrpc {
9
16
  private client;
10
17
  private config;
11
18
  private connected;
12
19
  private subscribers;
20
+ private dexSubscribers;
13
21
  constructor(endpoint: string, xToken: string, config?: ClientConfig);
14
22
  /** 连接到 gRPC 服务器 */
15
23
  connect(): Promise<void>;
@@ -28,14 +36,31 @@ export declare class YellowstoneGrpc {
28
36
  * 若不回复,公共节点或 LB 可能在超时后 RST_STREAM(HTTP/2 CANCEL)。
29
37
  */
30
38
  private subscribePingPongRequest;
39
+ private static toBytes;
40
+ private static toBigInt;
41
+ private static protobufNumber;
42
+ private static timestampToMicros;
43
+ private parseAccountEvent;
31
44
  private initialSubscribeRequest;
32
45
  /** 订阅交易 */
33
46
  subscribeTransactions(filter: TransactionFilter, callbacks: SubscribeCallbacks): Promise<{
34
47
  id: string;
35
48
  cancel: () => void;
36
49
  }>;
50
+ /**
51
+ * 订阅并直接产出 `DexEvent`。
52
+ *
53
+ * 交易更新走统一交易解析(外层/内层指令 + 日志 + 数据填充);账户更新走 `parseAccountUnified`。
54
+ * 事件队列满时丢弃新事件,避免回调阻塞 gRPC 读循环,保持低延迟。
55
+ */
56
+ subscribeDexEvents(transactionFilters?: TransactionFilter[], accountFilters?: AccountFilter[], eventTypeFilter?: EventTypeFilter, options?: {
57
+ autoReconnect?: boolean;
58
+ }): Promise<DexEventSubscription>;
37
59
  /** 取消订阅 */
38
60
  unsubscribe(subId: string): void;
61
+ /** 动态更新当前 DEX 订阅过滤器(与 Rust update_subscription 语义对齐)。 */
62
+ updateSubscription(transactionFilters: TransactionFilter[], accountFilters: AccountFilter[]): Promise<void>;
63
+ update_subscription(transactionFilters: TransactionFilter[], accountFilters: AccountFilter[]): Promise<void>;
39
64
  /** 获取最新区块哈希 */
40
65
  getLatestBlockhash(commitment?: CommitmentLevel): Promise<{
41
66
  slot: number;
@@ -32,20 +32,72 @@ var __importStar = (this && this.__importStar) || (function () {
32
32
  return result;
33
33
  };
34
34
  })();
35
+ var __importDefault = (this && this.__importDefault) || function (mod) {
36
+ return (mod && mod.__esModule) ? mod : { "default": mod };
37
+ };
35
38
  Object.defineProperty(exports, "__esModule", { value: true });
36
39
  exports.YellowstoneGrpc = void 0;
37
40
  /**
38
41
  * Yellowstone gRPC 客户端实现 - 基于 @triton-one/yellowstone-grpc
39
42
  */
40
43
  const yellowstone_grpc_1 = __importStar(require("@triton-one/yellowstone-grpc"));
44
+ const bs58_1 = __importDefault(require("bs58"));
45
+ const metadata_js_1 = require("../core/metadata.js");
46
+ const mod_js_1 = require("../accounts/mod.js");
41
47
  const geyser_connect_js_1 = require("./geyser_connect.js");
48
+ const order_buffer_js_1 = require("./order_buffer.js");
49
+ const subscribe_builder_js_1 = require("./subscribe_builder.js");
50
+ const yellowstone_parse_js_1 = require("./yellowstone_parse.js");
42
51
  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
+ }
93
+ }
43
94
  /** Yellowstone gRPC 客户端包装器 */
44
95
  class YellowstoneGrpc {
45
96
  client;
46
97
  config;
47
98
  connected = false;
48
99
  subscribers = new Map();
100
+ dexSubscribers = new Map();
49
101
  constructor(endpoint, xToken, config = (0, types_js_1.defaultClientConfig)()) {
50
102
  this.config = config;
51
103
  const gc = (0, geyser_connect_js_1.defaultGeyserConnectConfig)();
@@ -68,6 +120,10 @@ class YellowstoneGrpc {
68
120
  sub.cancel();
69
121
  }
70
122
  this.subscribers.clear();
123
+ for (const [, sub] of this.dexSubscribers) {
124
+ sub.cancel();
125
+ }
126
+ this.dexSubscribers.clear();
71
127
  this.connected = false;
72
128
  }
73
129
  /** 检查是否已连接 */
@@ -96,6 +152,9 @@ class YellowstoneGrpc {
96
152
  const result = {
97
153
  filters: update.filters,
98
154
  };
155
+ const createdAtUs = YellowstoneGrpc.timestampToMicros(update.createdAt ?? update.created_at);
156
+ if (createdAtUs !== undefined)
157
+ result.createdAtUs = createdAtUs;
99
158
  if (update.account) {
100
159
  const acc = update.account;
101
160
  result.account = {
@@ -191,6 +250,58 @@ class YellowstoneGrpc {
191
250
  ping: { id: 1 },
192
251
  };
193
252
  }
253
+ static toBytes(value) {
254
+ if (!value)
255
+ return new Uint8Array();
256
+ return value instanceof Uint8Array ? value : Uint8Array.from(value);
257
+ }
258
+ static toBigInt(value) {
259
+ return typeof value === "bigint" ? value : BigInt(value);
260
+ }
261
+ static protobufNumber(value) {
262
+ if (typeof value === "bigint")
263
+ return Number(value);
264
+ if (typeof value === "number")
265
+ return value;
266
+ if (typeof value === "string")
267
+ return Number(value);
268
+ if (value && typeof value === "object") {
269
+ const longLike = value;
270
+ if (typeof longLike.toNumber === "function")
271
+ return longLike.toNumber();
272
+ if (typeof longLike.toString === "function")
273
+ return Number(longLike.toString());
274
+ }
275
+ return Number.NaN;
276
+ }
277
+ static timestampToMicros(value) {
278
+ if (!value || typeof value !== "object")
279
+ return undefined;
280
+ const ts = value;
281
+ if (ts.seconds === undefined)
282
+ return undefined;
283
+ const seconds = YellowstoneGrpc.protobufNumber(ts.seconds);
284
+ const nanos = Number(ts.nanos ?? 0);
285
+ if (!Number.isFinite(seconds) || !Number.isFinite(nanos))
286
+ return undefined;
287
+ return Math.trunc(seconds * 1_000_000 + nanos / 1_000);
288
+ }
289
+ parseAccountEvent(update, grpcRecvUs, eventTypeFilter, blockTimeUs) {
290
+ const acc = update.account;
291
+ if (!acc)
292
+ return null;
293
+ const signatureBytes = YellowstoneGrpc.toBytes(acc.txnSignature);
294
+ const account = {
295
+ pubkey: bs58_1.default.encode(YellowstoneGrpc.toBytes(acc.pubkey)),
296
+ executable: acc.executable,
297
+ lamports: YellowstoneGrpc.toBigInt(acc.lamports),
298
+ owner: bs58_1.default.encode(YellowstoneGrpc.toBytes(acc.owner)),
299
+ rent_epoch: YellowstoneGrpc.toBigInt(acc.rentEpoch),
300
+ data: YellowstoneGrpc.toBytes(acc.data),
301
+ };
302
+ const metadata = (0, metadata_js_1.makeMetadata)(signatureBytes.length > 0 ? bs58_1.default.encode(signatureBytes) : "", Number(update.slot), 0, blockTimeUs, grpcRecvUs);
303
+ return (0, mod_js_1.parseAccountUnified)(account, metadata, eventTypeFilter);
304
+ }
194
305
  initialSubscribeRequest(filter) {
195
306
  return {
196
307
  transactions: {
@@ -317,6 +428,158 @@ class YellowstoneGrpc {
317
428
  },
318
429
  };
319
430
  }
431
+ /**
432
+ * 订阅并直接产出 `DexEvent`。
433
+ *
434
+ * 交易更新走统一交易解析(外层/内层指令 + 日志 + 数据填充);账户更新走 `parseAccountUnified`。
435
+ * 事件队列满时丢弃新事件,避免回调阻塞 gRPC 读循环,保持低延迟。
436
+ */
437
+ async subscribeDexEvents(transactionFilters = [], accountFilters = [], eventTypeFilter, options) {
438
+ await this.connect();
439
+ 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));
442
+ const order = new order_buffer_js_1.OrderDispatcher(this.config);
443
+ const emitEvent = (event) => events.push(event);
444
+ let flushTimer;
445
+ if (order.needsTimer) {
446
+ const intervalMs = this.config.order_mode === "MicroBatch"
447
+ ? Math.max(1, Math.ceil(this.config.micro_batch_us / 1000))
448
+ : Math.max(1, Math.floor(this.config.order_timeout_ms / 2));
449
+ flushTimer = setInterval(() => order.flushDue(emitEvent), intervalMs);
450
+ }
451
+ let isCancelled = false;
452
+ let currentTransactionFilters = transactionFilters;
453
+ let currentAccountFilters = accountFilters;
454
+ const streamHolder = { current: null };
455
+ const cancel = () => {
456
+ isCancelled = true;
457
+ streamHolder.current?.end();
458
+ streamHolder.current = null;
459
+ if (flushTimer)
460
+ clearInterval(flushTimer);
461
+ events.close();
462
+ errors.close();
463
+ };
464
+ const update = async (nextTxFilters, nextAccFilters) => {
465
+ currentTransactionFilters = nextTxFilters;
466
+ currentAccountFilters = nextAccFilters;
467
+ const stream = streamHolder.current;
468
+ if (!stream)
469
+ return;
470
+ await new Promise((resolve, reject) => {
471
+ stream.write((0, subscribe_builder_js_1.buildSubscribeRequest)(currentTransactionFilters, currentAccountFilters), (err) => {
472
+ if (err)
473
+ reject(err);
474
+ else
475
+ resolve();
476
+ });
477
+ });
478
+ };
479
+ this.dexSubscribers.set(id, { cancel, update });
480
+ (async () => {
481
+ const autoReconnect = options?.autoReconnect !== false;
482
+ const maxBackoffMs = 60_000;
483
+ let backoffMs = this.config.retry_delay_ms;
484
+ try {
485
+ while (!isCancelled) {
486
+ try {
487
+ const stream = await this.client.subscribe();
488
+ streamHolder.current = stream;
489
+ await new Promise((resolve, reject) => {
490
+ stream.write((0, subscribe_builder_js_1.buildSubscribeRequest)(currentTransactionFilters, currentAccountFilters), (err) => {
491
+ if (err)
492
+ reject(err);
493
+ else
494
+ resolve();
495
+ });
496
+ });
497
+ backoffMs = this.config.retry_delay_ms;
498
+ await new Promise((resolve, reject) => {
499
+ const cleanup = () => {
500
+ stream.removeAllListeners();
501
+ if (streamHolder.current === stream) {
502
+ streamHolder.current = null;
503
+ }
504
+ };
505
+ stream.on("data", (update) => {
506
+ if (isCancelled)
507
+ return;
508
+ order.flushDue(emitEvent);
509
+ if (update.ping) {
510
+ stream.write(this.subscribePingPongRequest(), (werr) => {
511
+ if (werr && !isCancelled) {
512
+ errors.push(werr instanceof Error ? werr : new Error(String(werr)));
513
+ }
514
+ });
515
+ return;
516
+ }
517
+ try {
518
+ const grpcRecvUs = Math.floor(Date.now() * 1000);
519
+ const converted = this.convertUpdate(update);
520
+ const tx = converted.transaction?.transaction;
521
+ if (tx) {
522
+ const fallbackSlot = YellowstoneGrpc.protobufNumber(converted.transaction?.slot ?? 0);
523
+ 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 });
525
+ order.pushTransactionEvents(txEvents, fallbackSlot, fallbackTxIndex, emitEvent);
526
+ }
527
+ if (converted.account) {
528
+ const ev = this.parseAccountEvent(converted.account, grpcRecvUs, eventTypeFilter, converted.createdAtUs);
529
+ if (ev)
530
+ emitEvent(ev);
531
+ }
532
+ }
533
+ catch (err) {
534
+ errors.push(err instanceof Error ? err : new Error(String(err)));
535
+ }
536
+ });
537
+ stream.on("error", (err) => {
538
+ cleanup();
539
+ reject(err);
540
+ });
541
+ stream.on("end", () => {
542
+ cleanup();
543
+ resolve();
544
+ });
545
+ });
546
+ }
547
+ catch (err) {
548
+ if (isCancelled)
549
+ break;
550
+ errors.push(err instanceof Error ? err : new Error(String(err)));
551
+ if (!autoReconnect)
552
+ break;
553
+ await new Promise((r) => setTimeout(r, backoffMs));
554
+ backoffMs = Math.min(backoffMs * 2, maxBackoffMs);
555
+ continue;
556
+ }
557
+ if (isCancelled || !autoReconnect)
558
+ break;
559
+ await new Promise((r) => setTimeout(r, backoffMs));
560
+ backoffMs = Math.min(backoffMs * 2, maxBackoffMs);
561
+ }
562
+ }
563
+ finally {
564
+ streamHolder.current?.end();
565
+ streamHolder.current = null;
566
+ if (flushTimer)
567
+ clearInterval(flushTimer);
568
+ order.flushAll(emitEvent);
569
+ events.close();
570
+ errors.close();
571
+ this.dexSubscribers.delete(id);
572
+ }
573
+ })();
574
+ return Object.assign(events, {
575
+ id,
576
+ errors,
577
+ cancel: () => {
578
+ cancel();
579
+ this.dexSubscribers.delete(id);
580
+ },
581
+ });
582
+ }
320
583
  /** 取消订阅 */
321
584
  unsubscribe(subId) {
322
585
  const sub = this.subscribers.get(subId);
@@ -324,6 +587,22 @@ class YellowstoneGrpc {
324
587
  sub.cancel();
325
588
  this.subscribers.delete(subId);
326
589
  }
590
+ const dexSub = this.dexSubscribers.get(subId);
591
+ if (dexSub) {
592
+ dexSub.cancel();
593
+ this.dexSubscribers.delete(subId);
594
+ }
595
+ }
596
+ /** 动态更新当前 DEX 订阅过滤器(与 Rust update_subscription 语义对齐)。 */
597
+ async updateSubscription(transactionFilters, accountFilters) {
598
+ const active = this.dexSubscribers.values().next().value;
599
+ if (!active?.update) {
600
+ throw new Error("No active DEX subscription");
601
+ }
602
+ await active.update(transactionFilters, accountFilters);
603
+ }
604
+ async update_subscription(transactionFilters, accountFilters) {
605
+ await this.updateSubscription(transactionFilters, accountFilters);
327
606
  }
328
607
  /** 获取最新区块哈希 */
329
608
  async getLatestBlockhash(commitment) {
@@ -0,0 +1,2 @@
1
+ import type { DexEvent } from "../core/dex_event.js";
2
+ export declare function dedupeLogInstructionEvents(logEvents: DexEvent[], instructionEvents: DexEvent[]): DexEvent[];