sol-parser-sdk-nodejs 0.3.0 → 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (93) hide show
  1. package/.env.example +24 -0
  2. package/README.md +94 -352
  3. package/README_CN.md +101 -253
  4. package/dist/accounts/mod.d.ts +2 -0
  5. package/dist/accounts/mod.js +5 -1
  6. package/dist/accounts/rpc_wallet.d.ts +5 -0
  7. package/dist/accounts/rpc_wallet.js +18 -0
  8. package/dist/accounts/rust_aliases.d.ts +9 -0
  9. package/dist/accounts/rust_aliases.js +19 -0
  10. package/dist/accounts/wallet_resolve.d.ts +1 -0
  11. package/dist/accounts/wallet_resolve.js +28 -0
  12. package/dist/common/constants.d.ts +10 -0
  13. package/dist/common/constants.js +13 -0
  14. package/dist/core/account_dispatcher_rpc.js +26 -5
  15. package/dist/core/account_fill_meteora.d.ts +4 -2
  16. package/dist/core/account_fill_meteora.js +5 -2
  17. package/dist/core/account_pubkey_cache.d.ts +12 -0
  18. package/dist/core/account_pubkey_cache.js +26 -0
  19. package/dist/core/clock.d.ts +6 -0
  20. package/dist/core/clock.js +13 -0
  21. package/dist/core/dex_event.d.ts +25 -44
  22. package/dist/core/metadata.d.ts +1 -0
  23. package/dist/core/unified_parser.d.ts +2 -2
  24. package/dist/core/unified_parser.js +6 -6
  25. package/dist/grpc/client.d.ts +6 -0
  26. package/dist/grpc/client.js +121 -64
  27. package/dist/grpc/event_parser.d.ts +6 -0
  28. package/dist/grpc/event_parser.js +15 -0
  29. package/dist/grpc/geyser_connect.d.ts +30 -0
  30. package/dist/grpc/geyser_connect.js +40 -0
  31. package/dist/grpc/program_ids.d.ts +25 -0
  32. package/dist/grpc/program_ids.js +54 -0
  33. package/dist/grpc/rpc_to_grpc.d.ts +18 -0
  34. package/dist/grpc/rpc_to_grpc.js +127 -0
  35. package/dist/grpc/subscribe_builder.d.ts +13 -0
  36. package/dist/grpc/subscribe_builder.js +66 -0
  37. package/dist/grpc/transaction_meta.d.ts +29 -0
  38. package/dist/grpc/transaction_meta.js +208 -0
  39. package/dist/grpc/types.d.ts +53 -2
  40. package/dist/grpc/types.js +98 -7
  41. package/dist/grpc/yellowstone_parse.d.ts +5 -0
  42. package/dist/grpc/yellowstone_parse.js +15 -2
  43. package/dist/index.d.ts +36 -6
  44. package/dist/index.js +172 -2
  45. package/dist/instr/bonk_ix.d.ts +4 -1
  46. package/dist/instr/bonk_ix.js +106 -27
  47. package/dist/instr/meteora_damm_ix.d.ts +4 -2
  48. package/dist/instr/meteora_damm_ix.js +248 -13
  49. package/dist/instr/mod.js +7 -2
  50. package/dist/instr/orca_whirlpool_ix.d.ts +4 -1
  51. package/dist/instr/orca_whirlpool_ix.js +45 -16
  52. package/dist/instr/program_ids.d.ts +7 -13
  53. package/dist/instr/program_ids.js +19 -15
  54. package/dist/instr/pumpswap_ix.d.ts +1 -1
  55. package/dist/instr/pumpswap_ix.js +78 -57
  56. package/dist/instr/raydium_amm_v4_ix.d.ts +1 -1
  57. package/dist/instr/raydium_amm_v4_ix.js +94 -28
  58. package/dist/instr/raydium_clmm_ix.d.ts +1 -1
  59. package/dist/instr/raydium_clmm_ix.js +59 -26
  60. package/dist/instr/raydium_cpmm_ix.d.ts +1 -1
  61. package/dist/instr/raydium_cpmm_ix.js +46 -12
  62. package/dist/instr/rust_aliases.d.ts +7 -0
  63. package/dist/instr/rust_aliases.js +14 -0
  64. package/dist/instr/utils.d.ts +1 -1
  65. package/dist/instr/utils.js +2 -1
  66. package/dist/logs/discriminator_lut.d.ts +19 -0
  67. package/dist/logs/discriminator_lut.js +60 -0
  68. package/dist/logs/meteora_damm.d.ts +3 -4
  69. package/dist/logs/meteora_damm.js +3 -369
  70. package/dist/logs/optimized_matcher.d.ts +2 -2
  71. package/dist/logs/optimized_matcher.js +3 -3
  72. package/dist/logs/rust_aliases.d.ts +6 -0
  73. package/dist/logs/rust_aliases.js +13 -0
  74. package/dist/rpc_parser.d.ts +1 -0
  75. package/dist/rpc_parser.js +4 -1
  76. package/dist/shredstream/alt_lookup.d.ts +9 -0
  77. package/dist/shredstream/alt_lookup.js +70 -0
  78. package/dist/shredstream/client.d.ts +62 -0
  79. package/dist/shredstream/client.js +399 -0
  80. package/dist/shredstream/config.d.ts +30 -0
  81. package/dist/shredstream/config.js +34 -0
  82. package/dist/shredstream/entries_decode.d.ts +28 -0
  83. package/dist/shredstream/entries_decode.js +251 -0
  84. package/dist/shredstream/index.d.ts +17 -0
  85. package/dist/shredstream/index.js +33 -0
  86. package/dist/shredstream/instruction_parse.d.ts +34 -0
  87. package/dist/shredstream/instruction_parse.js +47 -0
  88. package/dist/shredstream/proto_types.d.ts +9 -0
  89. package/dist/shredstream/proto_types.js +2 -0
  90. package/dist/shredstream/shredstream.proto +15 -0
  91. package/dist/shredstream/wire_to_shred_tx.d.ts +2 -0
  92. package/dist/shredstream/wire_to_shred_tx.js +59 -0
  93. package/package.json +28 -11
@@ -18,6 +18,24 @@ function tryFill(programId, programInvokes, message, meta, resolver, fn) {
18
18
  return;
19
19
  fn(get);
20
20
  }
21
+ const BONK_PROGRAM_FILL_ORDER = [
22
+ program_ids_js_1.BONK_PROGRAM_ID,
23
+ program_ids_js_1.BONK_LAUNCHPAD_PROGRAM_ID,
24
+ program_ids_js_1.BONK_PROGRAM_ID_LEGACY,
25
+ ];
26
+ /** Bonk 多程序 ID:按 Rust 主网 ID → Launchpad → 旧 TS 兼容顺序尝试 CPI 账户填充 */
27
+ function tryFillBonk(programInvokes, message, meta, resolver, fn) {
28
+ for (const programId of BONK_PROGRAM_FILL_ORDER) {
29
+ const invoke = (0, rpc_invoke_map_js_1.findMaxAccountsInvoke)(programId, programInvokes, message, meta);
30
+ if (!invoke)
31
+ continue;
32
+ const get = (0, rpc_invoke_map_js_1.makeInvokeAccountGetter)(resolver, invoke, message, meta);
33
+ if (!get)
34
+ continue;
35
+ fn(get);
36
+ return;
37
+ }
38
+ }
21
39
  /** 就地修改事件体内字段 */
22
40
  function fillAccountsFromTransactionDataRpc(ev, message, meta, programInvokes, resolver) {
23
41
  if ("PumpFunTrade" in ev) {
@@ -116,18 +134,21 @@ function fillAccountsFromTransactionDataRpc(ev, message, meta, programInvokes, r
116
134
  else if ("MeteoraDammV2CreatePosition" in ev) {
117
135
  tryFill(program_ids_js_1.METEORA_DAMM_V2_PROGRAM_ID, programInvokes, message, meta, resolver, (g) => (0, account_fill_meteora_js_1.fillMeteoraDammV2CreatePositionAccounts)(ev.MeteoraDammV2CreatePosition, g));
118
136
  }
137
+ else if ("MeteoraDammV2InitializePool" in ev) {
138
+ tryFill(program_ids_js_1.METEORA_DAMM_V2_PROGRAM_ID, programInvokes, message, meta, resolver, (g) => (0, account_fill_meteora_js_1.fillMeteoraDammV2InitializePoolAccounts)(ev.MeteoraDammV2InitializePool, g));
139
+ }
119
140
  else if ("MeteoraDammV2ClosePosition" in ev) {
120
141
  tryFill(program_ids_js_1.METEORA_DAMM_V2_PROGRAM_ID, programInvokes, message, meta, resolver, (g) => (0, account_fill_meteora_js_1.fillMeteoraDammV2ClosePositionAccounts)(ev.MeteoraDammV2ClosePosition, g));
121
142
  }
122
143
  else if ("MeteoraDammV2AddLiquidity" in ev) {
123
144
  tryFill(program_ids_js_1.METEORA_DAMM_V2_PROGRAM_ID, programInvokes, message, meta, resolver, (g) => (0, account_fill_meteora_js_1.fillMeteoraDammV2AddLiquidityAccounts)(ev.MeteoraDammV2AddLiquidity, g));
124
145
  }
146
+ else if ("MeteoraDammV2RemoveAllLiquidity" in ev) {
147
+ tryFill(program_ids_js_1.METEORA_DAMM_V2_PROGRAM_ID, programInvokes, message, meta, resolver, (g) => (0, account_fill_meteora_js_1.fillMeteoraDammV2RemoveAllLiquidityAccounts)(ev.MeteoraDammV2RemoveAllLiquidity, g));
148
+ }
125
149
  else if ("MeteoraDammV2RemoveLiquidity" in ev) {
126
150
  tryFill(program_ids_js_1.METEORA_DAMM_V2_PROGRAM_ID, programInvokes, message, meta, resolver, (g) => (0, account_fill_meteora_js_1.fillMeteoraDammV2RemoveLiquidityAccounts)(ev.MeteoraDammV2RemoveLiquidity, g));
127
151
  }
128
- else if ("MeteoraDammV2InitializePool" in ev) {
129
- tryFill(program_ids_js_1.METEORA_DAMM_V2_PROGRAM_ID, programInvokes, message, meta, resolver, (g) => (0, account_fill_meteora_js_1.fillMeteoraDammV2InitializePoolAccounts)(ev.MeteoraDammV2InitializePool, g));
130
- }
131
152
  else if ("MeteoraPoolsSwap" in ev) {
132
153
  tryFill(program_ids_js_1.METEORA_POOLS_PROGRAM_ID, programInvokes, message, meta, resolver, (g) => (0, account_fill_meteora_js_1.fillMeteoraPoolsSwapAccounts)(ev.MeteoraPoolsSwap, g));
133
154
  }
@@ -147,9 +168,9 @@ function fillAccountsFromTransactionDataRpc(ev, message, meta, programInvokes, r
147
168
  tryFill(program_ids_js_1.METEORA_DLMM_PROGRAM_ID, programInvokes, message, meta, resolver, (g) => (0, account_fill_meteora_js_1.fillMeteoraDlmmRemoveLiquidityAccounts)(ev.MeteoraDlmmRemoveLiquidity, g));
148
169
  }
149
170
  else if ("BonkTrade" in ev) {
150
- tryFill(program_ids_js_1.BONK_PROGRAM_ID, programInvokes, message, meta, resolver, (g) => (0, account_fill_bonk_js_1.fillBonkTradeAccounts)(ev.BonkTrade, g));
171
+ tryFillBonk(programInvokes, message, meta, resolver, (g) => (0, account_fill_bonk_js_1.fillBonkTradeAccounts)(ev.BonkTrade, g));
151
172
  }
152
173
  else if ("BonkPoolCreate" in ev) {
153
- tryFill(program_ids_js_1.BONK_PROGRAM_ID, programInvokes, message, meta, resolver, (g) => (0, account_fill_bonk_js_1.fillBonkPoolCreateAccounts)(ev.BonkPoolCreate, g));
174
+ tryFillBonk(programInvokes, message, meta, resolver, (g) => (0, account_fill_bonk_js_1.fillBonkPoolCreateAccounts)(ev.BonkPoolCreate, g));
154
175
  }
155
176
  }
@@ -1,11 +1,13 @@
1
1
  /** Meteora DAMM V2 / Pools / DLMM 账户填充(部分占位) */
2
- import type { MeteoraDammV2AddLiquidityEvent, MeteoraDammV2ClosePositionEvent, MeteoraDammV2CreatePositionEvent, MeteoraDammV2InitializePoolEvent, MeteoraDammV2RemoveLiquidityEvent, MeteoraDammV2SwapEvent, MeteoraDlmmAddLiquidityEvent, MeteoraDlmmRemoveLiquidityEvent, MeteoraDlmmSwapEvent, MeteoraPoolsAddLiquidityEvent, MeteoraPoolsRemoveLiquidityEvent, MeteoraPoolsSwapEvent } from "./dex_event.js";
2
+ import type { MeteoraDammV2AddLiquidityEvent, MeteoraDammV2ClosePositionEvent, MeteoraDammV2CreatePositionEvent, MeteoraDammV2InitializePoolEvent, MeteoraDammV2RemoveAllLiquidityEvent, MeteoraDammV2RemoveLiquidityEvent, MeteoraDammV2SwapEvent, MeteoraDlmmAddLiquidityEvent, MeteoraDlmmRemoveLiquidityEvent, MeteoraDlmmSwapEvent, MeteoraPoolsAddLiquidityEvent, MeteoraPoolsRemoveLiquidityEvent, MeteoraPoolsSwapEvent } from "./dex_event.js";
3
+ /** 预留:gRPC+meta 全量路径若需从 account_keys 补 vault,应对齐 cp_amm `swap` 账户顺序(IDL 中 vault/mint/token program 下标) */
3
4
  export declare function fillMeteoraDammV2SwapAccounts(_e: MeteoraDammV2SwapEvent, _get: (i: number) => string): void;
4
5
  export declare function fillMeteoraDammV2CreatePositionAccounts(_e: MeteoraDammV2CreatePositionEvent, _get: (i: number) => string): void;
6
+ export declare function fillMeteoraDammV2InitializePoolAccounts(_e: MeteoraDammV2InitializePoolEvent, _get: (i: number) => string): void;
5
7
  export declare function fillMeteoraDammV2ClosePositionAccounts(_e: MeteoraDammV2ClosePositionEvent, _get: (i: number) => string): void;
6
8
  export declare function fillMeteoraDammV2AddLiquidityAccounts(_e: MeteoraDammV2AddLiquidityEvent, _get: (i: number) => string): void;
7
9
  export declare function fillMeteoraDammV2RemoveLiquidityAccounts(_e: MeteoraDammV2RemoveLiquidityEvent, _get: (i: number) => string): void;
8
- export declare function fillMeteoraDammV2InitializePoolAccounts(_e: MeteoraDammV2InitializePoolEvent, _get: (i: number) => string): void;
10
+ export declare function fillMeteoraDammV2RemoveAllLiquidityAccounts(_e: MeteoraDammV2RemoveAllLiquidityEvent, _get: (i: number) => string): void;
9
11
  export declare function fillMeteoraPoolsSwapAccounts(_e: MeteoraPoolsSwapEvent, _get: (i: number) => string): void;
10
12
  export declare function fillMeteoraPoolsAddLiquidityAccounts(_e: MeteoraPoolsAddLiquidityEvent, _get: (i: number) => string): void;
11
13
  export declare function fillMeteoraPoolsRemoveLiquidityAccounts(_e: MeteoraPoolsRemoveLiquidityEvent, _get: (i: number) => string): void;
@@ -2,22 +2,25 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.fillMeteoraDammV2SwapAccounts = fillMeteoraDammV2SwapAccounts;
4
4
  exports.fillMeteoraDammV2CreatePositionAccounts = fillMeteoraDammV2CreatePositionAccounts;
5
+ exports.fillMeteoraDammV2InitializePoolAccounts = fillMeteoraDammV2InitializePoolAccounts;
5
6
  exports.fillMeteoraDammV2ClosePositionAccounts = fillMeteoraDammV2ClosePositionAccounts;
6
7
  exports.fillMeteoraDammV2AddLiquidityAccounts = fillMeteoraDammV2AddLiquidityAccounts;
7
8
  exports.fillMeteoraDammV2RemoveLiquidityAccounts = fillMeteoraDammV2RemoveLiquidityAccounts;
8
- exports.fillMeteoraDammV2InitializePoolAccounts = fillMeteoraDammV2InitializePoolAccounts;
9
+ exports.fillMeteoraDammV2RemoveAllLiquidityAccounts = fillMeteoraDammV2RemoveAllLiquidityAccounts;
9
10
  exports.fillMeteoraPoolsSwapAccounts = fillMeteoraPoolsSwapAccounts;
10
11
  exports.fillMeteoraPoolsAddLiquidityAccounts = fillMeteoraPoolsAddLiquidityAccounts;
11
12
  exports.fillMeteoraPoolsRemoveLiquidityAccounts = fillMeteoraPoolsRemoveLiquidityAccounts;
12
13
  exports.fillMeteoraDlmmSwapAccounts = fillMeteoraDlmmSwapAccounts;
13
14
  exports.fillMeteoraDlmmAddLiquidityAccounts = fillMeteoraDlmmAddLiquidityAccounts;
14
15
  exports.fillMeteoraDlmmRemoveLiquidityAccounts = fillMeteoraDlmmRemoveLiquidityAccounts;
16
+ /** 预留:gRPC+meta 全量路径若需从 account_keys 补 vault,应对齐 cp_amm `swap` 账户顺序(IDL 中 vault/mint/token program 下标) */
15
17
  function fillMeteoraDammV2SwapAccounts(_e, _get) { }
16
18
  function fillMeteoraDammV2CreatePositionAccounts(_e, _get) { }
19
+ function fillMeteoraDammV2InitializePoolAccounts(_e, _get) { }
17
20
  function fillMeteoraDammV2ClosePositionAccounts(_e, _get) { }
18
21
  function fillMeteoraDammV2AddLiquidityAccounts(_e, _get) { }
19
22
  function fillMeteoraDammV2RemoveLiquidityAccounts(_e, _get) { }
20
- function fillMeteoraDammV2InitializePoolAccounts(_e, _get) { }
23
+ function fillMeteoraDammV2RemoveAllLiquidityAccounts(_e, _get) { }
21
24
  function fillMeteoraPoolsSwapAccounts(_e, _get) { }
22
25
  function fillMeteoraPoolsAddLiquidityAccounts(_e, _get) { }
23
26
  function fillMeteoraPoolsRemoveLiquidityAccounts(_e, _get) { }
@@ -0,0 +1,12 @@
1
+ /**
2
+ * 与 Rust `sol-parser-sdk/src/core/cache.rs` 对齐:账户公钥索引解析缓存。
3
+ * JS 主线程单线程,使用模块级单例代替 Rust `thread_local`。
4
+ */
5
+ import { PublicKey } from "@solana/web3.js";
6
+ export declare class AccountPubkeyCache {
7
+ private readonly cache;
8
+ /** 与 Rust `AccountPubkeyCache::build_account_pubkeys` 一致 */
9
+ buildAccountPubkeys(instructionAccounts: Uint8Array | readonly number[], allAccounts: readonly PublicKey[]): PublicKey[];
10
+ }
11
+ /** 与 Rust `build_account_pubkeys_with_cache` 一致(返回 `Vec` 拷贝,与 Rust `.to_vec()` 相同) */
12
+ export declare function buildAccountPubkeysWithCache(instructionAccounts: Uint8Array | readonly number[], allAccounts: readonly PublicKey[]): PublicKey[];
@@ -0,0 +1,26 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.AccountPubkeyCache = void 0;
4
+ exports.buildAccountPubkeysWithCache = buildAccountPubkeysWithCache;
5
+ class AccountPubkeyCache {
6
+ cache = [];
7
+ /** 与 Rust `AccountPubkeyCache::build_account_pubkeys` 一致 */
8
+ buildAccountPubkeys(instructionAccounts, allAccounts) {
9
+ this.cache.length = 0;
10
+ for (let i = 0; i < instructionAccounts.length; i++) {
11
+ const idx = instructionAccounts[i];
12
+ if (idx < allAccounts.length) {
13
+ this.cache.push(allAccounts[idx]);
14
+ }
15
+ }
16
+ return this.cache;
17
+ }
18
+ }
19
+ exports.AccountPubkeyCache = AccountPubkeyCache;
20
+ let threadLocalLike;
21
+ /** 与 Rust `build_account_pubkeys_with_cache` 一致(返回 `Vec` 拷贝,与 Rust `.to_vec()` 相同) */
22
+ function buildAccountPubkeysWithCache(instructionAccounts, allAccounts) {
23
+ if (!threadLocalLike)
24
+ threadLocalLike = new AccountPubkeyCache();
25
+ return [...threadLocalLike.buildAccountPubkeys(instructionAccounts, allAccounts)];
26
+ }
@@ -1,2 +1,8 @@
1
1
  /** 当前时间微秒戳(用于 `grpc_recv_us`) */
2
2
  export declare function nowUs(): number;
3
+ /** 与 Rust `now_micros` 一致(别名) */
4
+ export declare const nowMicros: typeof nowUs;
5
+ /** 与 Rust `now_nanos` 同语义:墙钟纳秒(`Date` 精度为毫秒,纳秒位为推算值) */
6
+ export declare function nowNanos(): bigint;
7
+ /** 与 Rust `elapsed_micros_since` 一致 */
8
+ export declare function elapsedMicrosSince(startUs: number): number;
@@ -1,7 +1,20 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.nowMicros = void 0;
3
4
  exports.nowUs = nowUs;
5
+ exports.nowNanos = nowNanos;
6
+ exports.elapsedMicrosSince = elapsedMicrosSince;
4
7
  /** 当前时间微秒戳(用于 `grpc_recv_us`) */
5
8
  function nowUs() {
6
9
  return Math.floor(Date.now() * 1000);
7
10
  }
11
+ /** 与 Rust `now_micros` 一致(别名) */
12
+ exports.nowMicros = nowUs;
13
+ /** 与 Rust `now_nanos` 同语义:墙钟纳秒(`Date` 精度为毫秒,纳秒位为推算值) */
14
+ function nowNanos() {
15
+ return BigInt(Math.floor(Date.now() * 1_000_000));
16
+ }
17
+ /** 与 Rust `elapsed_micros_since` 一致 */
18
+ function elapsedMicrosSince(startUs) {
19
+ return nowUs() - startUs;
20
+ }
@@ -87,11 +87,13 @@ export type DexEvent = {
87
87
  } | {
88
88
  MeteoraDammV2RemoveLiquidity: MeteoraDammV2RemoveLiquidityEvent;
89
89
  } | {
90
- MeteoraDammV2CreatePosition: MeteoraDammV2CreatePositionEvent;
90
+ MeteoraDammV2RemoveAllLiquidity: MeteoraDammV2RemoveAllLiquidityEvent;
91
91
  } | {
92
- MeteoraDammV2ClosePosition: MeteoraDammV2ClosePositionEvent;
92
+ MeteoraDammV2CreatePosition: MeteoraDammV2CreatePositionEvent;
93
93
  } | {
94
94
  MeteoraDammV2InitializePool: MeteoraDammV2InitializePoolEvent;
95
+ } | {
96
+ MeteoraDammV2ClosePosition: MeteoraDammV2ClosePositionEvent;
95
97
  } | {
96
98
  MeteoraDlmmSwap: MeteoraDlmmSwapEvent;
97
99
  } | {
@@ -761,67 +763,46 @@ export interface MeteoraDammV2RemoveLiquidityEvent {
761
763
  token_a_amount_threshold: bigint;
762
764
  token_b_amount_threshold: bigint;
763
765
  }
764
- /** `MeteoraDammV2CreatePositionEvent` 对齐 */
765
- export interface MeteoraDammV2CreatePositionEvent {
766
+ /** 外层 `remove_all_liquidity`;指令仅含两 u64 阈值,无 `liquidity_delta`。 */
767
+ export interface MeteoraDammV2RemoveAllLiquidityEvent {
766
768
  metadata: EventMetadata;
767
769
  pool: string;
768
- owner: string;
769
770
  position: string;
770
- position_nft_mint: string;
771
+ owner: string;
772
+ token_a_amount_threshold: bigint;
773
+ token_b_amount_threshold: bigint;
771
774
  }
772
- /** 与 `MeteoraDammV2ClosePositionEvent` 对齐 */
773
- export interface MeteoraDammV2ClosePositionEvent {
775
+ /** 与 `MeteoraDammV2CreatePositionEvent` 对齐 */
776
+ export interface MeteoraDammV2CreatePositionEvent {
774
777
  metadata: EventMetadata;
775
778
  pool: string;
776
779
  owner: string;
777
780
  position: string;
778
781
  position_nft_mint: string;
779
782
  }
780
- /** Meteora `cp-amm` `DynamicFeeParameters`(Anchor 序列化) */
781
- export interface MeteoraDammV2DynamicFeeParameters {
782
- bin_step: number;
783
- bin_step_u128: bigint;
784
- filter_period: number;
785
- decay_period: number;
786
- reduction_factor: number;
787
- max_volatility_accumulator: number;
788
- variable_fee_control: number;
789
- }
790
- /** Meteora `cp-amm` `PoolFeeParameters`(Anchor 序列化解析) */
791
- export interface MeteoraDammV2PoolFeeParameters {
792
- /** `BaseFeeParameters.data`,27 字节十六进制 */
793
- base_fee_data: string;
794
- compounding_fee_bps: number;
795
- padding: number;
796
- dynamic_fee: MeteoraDammV2DynamicFeeParameters | null;
797
- }
798
783
  /**
799
- * Meteora `cp-amm` `EvtInitializePool` 对齐(Program data 载荷)。
800
- * Go/Python 中 `sqrt_min_price`、`liquidity`、`pool_fees.dynamic_fee.bin_step_u128` u128 语义字段为十进制字符串。
784
+ * 外层 `initialize_pool`(`InitializePoolParameters`);无日志时仅含指令内字段与账户索引映射。
785
+ * Go/Python 中 `liquidity` / `sqrt_price` 为十进制字符串;`activation_point` 为 `None` 或十进制字符串。
801
786
  */
802
787
  export interface MeteoraDammV2InitializePoolEvent {
803
788
  metadata: EventMetadata;
789
+ creator: string;
790
+ position_nft_mint: string;
804
791
  pool: string;
792
+ position: string;
805
793
  token_a_mint: string;
806
794
  token_b_mint: string;
807
- creator: string;
808
- payer: string;
809
- alpha_vault: string;
810
- pool_fees: MeteoraDammV2PoolFeeParameters;
811
- sqrt_min_price: bigint;
812
- sqrt_max_price: bigint;
813
- activation_type: number;
814
- collect_fee_mode: number;
815
795
  liquidity: bigint;
816
796
  sqrt_price: bigint;
817
- activation_point: bigint;
818
- token_a_flag: number;
819
- token_b_flag: number;
820
- token_a_amount: bigint;
821
- token_b_amount: bigint;
822
- total_amount_a: bigint;
823
- total_amount_b: bigint;
824
- pool_type: number;
797
+ activation_point: bigint | null;
798
+ }
799
+ /** 与 `MeteoraDammV2ClosePositionEvent` 对齐 */
800
+ export interface MeteoraDammV2ClosePositionEvent {
801
+ metadata: EventMetadata;
802
+ pool: string;
803
+ owner: string;
804
+ position: string;
805
+ position_nft_mint: string;
825
806
  }
826
807
  /** Meteora DLMM Swap:`fee_bps` 在 Go/Python 为十进制字符串。 */
827
808
  export interface MeteoraDlmmSwapEvent {
@@ -2,6 +2,7 @@
2
2
  export interface EventMetadata {
3
3
  signature: string;
4
4
  slot: number;
5
+ /** gRPC:与 Yellowstone `SubscribeUpdateTransactionInfo.index` 一致(Rust `info.index`)。Shred 等其它路径含义见各实现说明。 */
5
6
  tx_index: number;
6
7
  block_time_us: number;
7
8
  grpc_recv_us: number;
@@ -9,10 +9,10 @@ export type StreamingEventListener = {
9
9
  };
10
10
  /** 从交易解析事件;当前主路径为日志解析 */
11
11
  export declare function parseTransactionEvents(_instructionData: Uint8Array, _accounts: string[], logs: string[], signature: string, slot: number, _txIndex: number, blockTimeUs: number | undefined, _programId: string): DexEvent[];
12
- export declare function parseLogsOnly(logs: string[], signature: string, slot: number, blockTimeUs: number | undefined): DexEvent[];
12
+ export declare function parseLogsOnly(logs: string[], signature: string, slot: number, blockTimeUs: number | undefined, txIndex?: number): DexEvent[];
13
13
  export declare function parseTransactionWithListener(instructionData: Uint8Array, accounts: string[], logs: string[], signature: string, slot: number, txIndex: number, blockTimeUs: number | undefined, programId: string, listener: EventListener): void;
14
14
  export declare function parseTransactionEventsStreaming(_instructionData: Uint8Array, _accounts: string[], logs: string[], signature: string, slot: number, _txIndex: number, blockTimeUs: number | undefined, _programId: string, callback: (event: DexEvent) => void): void;
15
- export declare function parseLogsStreaming(logs: string[], signature: string, slot: number, blockTimeUs: number | undefined, callback: (event: DexEvent) => void): void;
15
+ export declare function parseLogsStreaming(logs: string[], signature: string, slot: number, blockTimeUs: number | undefined, callback: (event: DexEvent) => void, txIndex?: number): void;
16
16
  export declare function parseTransactionWithStreamingListener(instructionData: Uint8Array, accounts: string[], logs: string[], signature: string, slot: number, txIndex: number, blockTimeUs: number | undefined, programId: string, listener: StreamingEventListener): void;
17
17
  /** 带完整 gRPC 元数据字段的日志解析 */
18
18
  export declare function parseLog(log: string, signature: string, slot: number, txIndex: number, blockTimeUs: number | undefined, grpcRecvUs: number, eventTypeFilter: EventTypeFilter | undefined, isCreatedBuy: boolean, recentBlockhash?: Uint8Array): DexEvent | null;
@@ -13,12 +13,12 @@ const clock_js_1 = require("./clock.js");
13
13
  Object.defineProperty(exports, "nowUs", { enumerable: true, get: function () { return clock_js_1.nowUs; } });
14
14
  /** 从交易解析事件;当前主路径为日志解析 */
15
15
  function parseTransactionEvents(_instructionData, _accounts, logs, signature, slot, _txIndex, blockTimeUs, _programId) {
16
- return parseLogsOnly(logs, signature, slot, blockTimeUs);
16
+ return parseLogsOnly(logs, signature, slot, blockTimeUs, _txIndex);
17
17
  }
18
- function parseLogsOnly(logs, signature, slot, blockTimeUs) {
18
+ function parseLogsOnly(logs, signature, slot, blockTimeUs, txIndex = 0) {
19
19
  const out = [];
20
20
  for (const log of logs) {
21
- const e = (0, optimized_matcher_js_1.parseLogUnified)(log, signature, slot, blockTimeUs);
21
+ const e = (0, optimized_matcher_js_1.parseLogUnified)(log, signature, slot, blockTimeUs, txIndex);
22
22
  if (e)
23
23
  out.push(e);
24
24
  }
@@ -30,11 +30,11 @@ function parseTransactionWithListener(instructionData, accounts, logs, signature
30
30
  }
31
31
  }
32
32
  function parseTransactionEventsStreaming(_instructionData, _accounts, logs, signature, slot, _txIndex, blockTimeUs, _programId, callback) {
33
- parseLogsStreaming(logs, signature, slot, blockTimeUs, callback);
33
+ parseLogsStreaming(logs, signature, slot, blockTimeUs, callback, _txIndex);
34
34
  }
35
- function parseLogsStreaming(logs, signature, slot, blockTimeUs, callback) {
35
+ function parseLogsStreaming(logs, signature, slot, blockTimeUs, callback, txIndex = 0) {
36
36
  for (const log of logs) {
37
- const e = (0, optimized_matcher_js_1.parseLogUnified)(log, signature, slot, blockTimeUs);
37
+ const e = (0, optimized_matcher_js_1.parseLogUnified)(log, signature, slot, blockTimeUs, txIndex);
38
38
  if (e)
39
39
  callback(e);
40
40
  }
@@ -23,6 +23,12 @@ export declare class YellowstoneGrpc {
23
23
  private convertSlotStatus;
24
24
  /** 转换订阅更新 */
25
25
  private convertUpdate;
26
+ /**
27
+ * 应答 Geyser 在 SubscribeUpdate 中下发的 ping(与 `solana-streamer` / Rust 侧一致)。
28
+ * 若不回复,公共节点或 LB 可能在超时后 RST_STREAM(HTTP/2 CANCEL)。
29
+ */
30
+ private subscribePingPongRequest;
31
+ private initialSubscribeRequest;
26
32
  /** 订阅交易 */
27
33
  subscribeTransactions(filter: TransactionFilter, callbacks: SubscribeCallbacks): Promise<{
28
34
  id: string;
@@ -38,6 +38,7 @@ exports.YellowstoneGrpc = void 0;
38
38
  * Yellowstone gRPC 客户端实现 - 基于 @triton-one/yellowstone-grpc
39
39
  */
40
40
  const yellowstone_grpc_1 = __importStar(require("@triton-one/yellowstone-grpc"));
41
+ const geyser_connect_js_1 = require("./geyser_connect.js");
41
42
  const types_js_1 = require("./types.js");
42
43
  /** Yellowstone gRPC 客户端包装器 */
43
44
  class YellowstoneGrpc {
@@ -47,7 +48,12 @@ class YellowstoneGrpc {
47
48
  subscribers = new Map();
48
49
  constructor(endpoint, xToken, config = (0, types_js_1.defaultClientConfig)()) {
49
50
  this.config = config;
50
- this.client = new yellowstone_grpc_1.default(endpoint, xToken || undefined, undefined);
51
+ const gc = (0, geyser_connect_js_1.defaultGeyserConnectConfig)();
52
+ this.client = new yellowstone_grpc_1.default(endpoint, xToken || undefined, (0, geyser_connect_js_1.geyserGrpcChannelOptions)({
53
+ ...gc,
54
+ keepAliveIntervalMs: config.keep_alive_interval_ms,
55
+ keepAliveTimeoutMs: config.keep_alive_timeout_ms,
56
+ }));
51
57
  }
52
58
  /** 连接到 gRPC 服务器 */
53
59
  async connect() {
@@ -167,89 +173,140 @@ class YellowstoneGrpc {
167
173
  }
168
174
  return result;
169
175
  }
176
+ /**
177
+ * 应答 Geyser 在 SubscribeUpdate 中下发的 ping(与 `solana-streamer` / Rust 侧一致)。
178
+ * 若不回复,公共节点或 LB 可能在超时后 RST_STREAM(HTTP/2 CANCEL)。
179
+ */
180
+ subscribePingPongRequest() {
181
+ return {
182
+ accounts: {},
183
+ slots: {},
184
+ transactions: {},
185
+ transactionsStatus: {},
186
+ entry: {},
187
+ blocks: {},
188
+ blocksMeta: {},
189
+ commitment: yellowstone_grpc_1.CommitmentLevel.CONFIRMED,
190
+ accountsDataSlice: [],
191
+ ping: { id: 1 },
192
+ };
193
+ }
194
+ initialSubscribeRequest(filter) {
195
+ return {
196
+ transactions: {
197
+ client: {
198
+ accountInclude: filter.account_include,
199
+ accountExclude: filter.account_exclude,
200
+ accountRequired: filter.account_required,
201
+ vote: filter.vote,
202
+ failed: filter.failed,
203
+ signature: filter.signature,
204
+ },
205
+ },
206
+ accounts: {},
207
+ slots: {},
208
+ transactionsStatus: {},
209
+ entry: {},
210
+ blocks: {},
211
+ blocksMeta: {},
212
+ commitment: yellowstone_grpc_1.CommitmentLevel.CONFIRMED,
213
+ accountsDataSlice: [],
214
+ ping: undefined,
215
+ };
216
+ }
170
217
  /** 订阅交易 */
171
218
  async subscribeTransactions(filter, callbacks) {
172
219
  const id = `sub_${Date.now()}_${Math.random().toString(36).slice(2)}`;
173
220
  let isCancelled = false;
221
+ const streamHolder = { current: null };
174
222
  const cancel = () => {
175
223
  isCancelled = true;
224
+ streamHolder.current?.end();
225
+ streamHolder.current = null;
176
226
  };
177
227
  this.subscribers.set(id, { filter, callbacks, cancel });
178
228
  (async () => {
229
+ const autoReconnect = callbacks.autoReconnect !== false;
230
+ const maxBackoffMs = 60_000;
231
+ let backoffMs = this.config.retry_delay_ms;
179
232
  try {
180
- const stream = await this.client.subscribe();
181
- await new Promise((resolve, reject) => {
182
- stream.write({
183
- transactions: {
184
- client: {
185
- accountInclude: filter.account_include,
186
- accountExclude: filter.account_exclude,
187
- accountRequired: filter.account_required,
188
- vote: filter.vote,
189
- failed: filter.failed,
190
- signature: filter.signature,
191
- },
192
- },
193
- accounts: {},
194
- slots: {},
195
- transactionsStatus: {},
196
- entry: {},
197
- blocks: {},
198
- blocksMeta: {},
199
- commitment: yellowstone_grpc_1.CommitmentLevel.CONFIRMED,
200
- accountsDataSlice: [],
201
- ping: undefined,
202
- }, (err) => {
203
- if (err)
204
- reject(err);
205
- else
206
- resolve();
207
- });
208
- });
209
- stream.on("data", (update) => {
210
- if (isCancelled)
211
- return;
212
- if (!callbacks.onUpdate)
213
- return;
233
+ while (!isCancelled) {
214
234
  try {
215
- const converted = this.convertUpdate(update);
216
- callbacks.onUpdate(converted);
235
+ const stream = await this.client.subscribe();
236
+ streamHolder.current = stream;
237
+ await new Promise((resolve, reject) => {
238
+ stream.write(this.initialSubscribeRequest(filter), (err) => {
239
+ if (err)
240
+ reject(err);
241
+ else
242
+ resolve();
243
+ });
244
+ });
245
+ backoffMs = this.config.retry_delay_ms;
246
+ await new Promise((resolve, reject) => {
247
+ const cleanup = () => {
248
+ stream.removeAllListeners();
249
+ if (streamHolder.current === stream) {
250
+ streamHolder.current = null;
251
+ }
252
+ };
253
+ stream.on("data", (update) => {
254
+ if (isCancelled)
255
+ return;
256
+ if (update.ping) {
257
+ stream.write(this.subscribePingPongRequest(), (werr) => {
258
+ if (werr && callbacks.onError && !isCancelled) {
259
+ callbacks.onError(werr instanceof Error ? werr : new Error(String(werr)));
260
+ }
261
+ });
262
+ return;
263
+ }
264
+ if (!callbacks.onUpdate)
265
+ return;
266
+ try {
267
+ const converted = this.convertUpdate(update);
268
+ callbacks.onUpdate(converted);
269
+ }
270
+ catch (err) {
271
+ const e = err instanceof Error ? err : new Error(String(err));
272
+ callbacks.onError?.(e);
273
+ }
274
+ });
275
+ stream.on("error", (err) => {
276
+ cleanup();
277
+ reject(err);
278
+ });
279
+ stream.on("end", () => {
280
+ cleanup();
281
+ resolve();
282
+ });
283
+ });
217
284
  }
218
285
  catch (err) {
219
- // 用户 onUpdate 内抛错(如同步 IO/画图)若未捕获,会破坏 gRPC duplex 流并触发 RST_STREAM。
286
+ if (isCancelled)
287
+ break;
220
288
  const e = err instanceof Error ? err : new Error(String(err));
221
- if (callbacks.onError) {
222
- callbacks.onError(e);
223
- }
224
- }
225
- });
226
- stream.on("error", (err) => {
227
- if (isCancelled)
228
- return;
229
- if (callbacks.onError) {
230
- callbacks.onError(err);
289
+ callbacks.onError?.(e);
290
+ if (!autoReconnect)
291
+ break;
292
+ await new Promise((r) => setTimeout(r, backoffMs));
293
+ backoffMs = Math.min(backoffMs * 2, maxBackoffMs);
294
+ continue;
231
295
  }
232
- });
233
- stream.on("end", () => {
234
296
  if (isCancelled)
235
- return;
236
- this.subscribers.delete(id);
237
- if (callbacks.onEnd) {
238
- callbacks.onEnd();
297
+ break;
298
+ if (!autoReconnect) {
299
+ callbacks.onEnd?.();
300
+ break;
239
301
  }
240
- });
241
- while (!isCancelled) {
242
- await new Promise((resolve) => setTimeout(resolve, 100));
302
+ await new Promise((r) => setTimeout(r, backoffMs));
303
+ backoffMs = Math.min(backoffMs * 2, maxBackoffMs);
243
304
  }
244
- stream.end();
245
305
  }
246
- catch (err) {
247
- if (isCancelled)
248
- return;
306
+ finally {
307
+ streamHolder.current?.end();
308
+ streamHolder.current = null;
249
309
  this.subscribers.delete(id);
250
- if (callbacks.onError) {
251
- callbacks.onError(err);
252
- }
253
310
  }
254
311
  })();
255
312
  return {
@@ -0,0 +1,6 @@
1
+ /**
2
+ * 与 Rust `grpc::event_parser`(`pub use crate::core as event_parser`)对应:核心解析子集再导出。
3
+ */
4
+ export { parseTransactionEvents, parseLogsOnly, parseTransactionWithListener, parseTransactionEventsStreaming, parseLogsStreaming, parseTransactionWithStreamingListener, parseLog, nowUs, type EventListener, type StreamingEventListener, } from "../core/unified_parser.js";
5
+ export type { DexEvent } from "../core/dex_event.js";
6
+ export type { EventMetadata } from "../core/metadata.js";
@@ -0,0 +1,15 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.nowUs = exports.parseLog = exports.parseTransactionWithStreamingListener = exports.parseLogsStreaming = exports.parseTransactionEventsStreaming = exports.parseTransactionWithListener = exports.parseLogsOnly = exports.parseTransactionEvents = void 0;
4
+ /**
5
+ * 与 Rust `grpc::event_parser`(`pub use crate::core as event_parser`)对应:核心解析子集再导出。
6
+ */
7
+ var unified_parser_js_1 = require("../core/unified_parser.js");
8
+ Object.defineProperty(exports, "parseTransactionEvents", { enumerable: true, get: function () { return unified_parser_js_1.parseTransactionEvents; } });
9
+ Object.defineProperty(exports, "parseLogsOnly", { enumerable: true, get: function () { return unified_parser_js_1.parseLogsOnly; } });
10
+ Object.defineProperty(exports, "parseTransactionWithListener", { enumerable: true, get: function () { return unified_parser_js_1.parseTransactionWithListener; } });
11
+ Object.defineProperty(exports, "parseTransactionEventsStreaming", { enumerable: true, get: function () { return unified_parser_js_1.parseTransactionEventsStreaming; } });
12
+ Object.defineProperty(exports, "parseLogsStreaming", { enumerable: true, get: function () { return unified_parser_js_1.parseLogsStreaming; } });
13
+ Object.defineProperty(exports, "parseTransactionWithStreamingListener", { enumerable: true, get: function () { return unified_parser_js_1.parseTransactionWithStreamingListener; } });
14
+ Object.defineProperty(exports, "parseLog", { enumerable: true, get: function () { return unified_parser_js_1.parseLog; } });
15
+ Object.defineProperty(exports, "nowUs", { enumerable: true, get: function () { return unified_parser_js_1.nowUs; } });
@@ -0,0 +1,30 @@
1
+ /**
2
+ * 与 Rust `sol-parser-sdk/src/grpc/geyser_connect.rs` 对齐:Yellowstone Geyser gRPC 连接选项。
3
+ *
4
+ * `@triton-one/yellowstone-grpc` 的 `Client` 通过构造函数第三参传入 `ChannelOptions`;
5
+ * `connectTimeoutMs` 无与 tonic 完全一一对应的项,仅作文档与默认值对齐(可配合环境网络调优)。
6
+ */
7
+ import type { ChannelOptions } from "@grpc/grpc-js";
8
+ import Client from "@triton-one/yellowstone-grpc";
9
+ /** 与 Rust `GeyserConnectConfig` 字段对应(毫秒 / 字节) */
10
+ export interface GeyserConnectConfig {
11
+ /** 对应 Rust `connect_timeout`(毫秒) */
12
+ connectTimeoutMs: number;
13
+ /** 对应 Rust `max_decoding_message_size` */
14
+ maxDecodingMessageSize: number;
15
+ /** 对应 Rust `x_token` */
16
+ xToken?: string;
17
+ /** gRPC 通道 HTTP/2 keepalive 间隔(毫秒),与 Rust `ClientConfig.keep_alive_interval_ms` 默认对齐 */
18
+ keepAliveIntervalMs?: number;
19
+ /** gRPC keepalive 应答超时(毫秒) */
20
+ keepAliveTimeoutMs?: number;
21
+ }
22
+ /** 与 Rust `GeyserConnectConfig::default` 一致 */
23
+ export declare function defaultGeyserConnectConfig(): GeyserConnectConfig;
24
+ /** 与 `ClientConfig` / `grpc.keepalive_*` 对齐,用于长连接抗 NAT/负载均衡 idle 断开 */
25
+ export declare function geyserGrpcChannelOptions(config?: GeyserConnectConfig): ChannelOptions;
26
+ /**
27
+ * 建立 Yellowstone Geyser 客户端(与 Rust `connect_yellowstone_geyser` 一致)。
28
+ * Rust 为 async;此处构造同步完成,返回 `Promise` 以保持 `await connectYellowstoneGeyser(...)` 写法。
29
+ */
30
+ export declare function connectYellowstoneGeyser(endpoint: string, config?: GeyserConnectConfig): Promise<Client>;