sol-parser-sdk 0.5.13 → 0.5.14

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.
package/README.md CHANGED
@@ -38,12 +38,20 @@
38
38
  | Parser inputs | Yellowstone gRPC, ShredStream, RPC transactions, encoded transactions, protocol account data |
39
39
  | DEX protocols | PumpFun, PumpSwap, Pump Fees, Raydium LaunchLab, Raydium CPMM, Raydium CLMM, Raydium AMM V4, Meteora DAMM v2, Meteora DLMM, Meteora DBC, Orca Whirlpool |
40
40
  | Use cases | Real-time DEX event parsing, token launch monitoring, copy trading, sniper bots, account filling, JSON event pipelines |
41
- | Runtime | Node.js 18+, TypeScript, npm/yarn/pnpm projects |
41
+ | Runtime | Node.js 20.18+, TypeScript, npm/yarn/pnpm projects |
42
42
 
43
43
  ---
44
44
 
45
45
  ## Release notes
46
46
 
47
+ ### v0.5.14
48
+
49
+ - Upgrades the Yellowstone client to `@triton-one/yellowstone-grpc` 7.0.0 with its native Rust N-API transport.
50
+ - Adds `source_to_grpc_latency_us` to distinguish provider or transport backlog from exact local processing latency.
51
+ - Fixes Yellowstone `createdAt` timestamps represented as JavaScript `Date` objects.
52
+ - Updates native connection, keepalive, flow-control, stream lifecycle, and unary RPC compatibility.
53
+ - Requires Node.js 20.18 or newer.
54
+
47
55
  ### v0.5.8
48
56
 
49
57
  - Syncs parser parity with the Rust SDK event model and program routing.
@@ -75,7 +83,7 @@
75
83
  **From npm**
76
84
 
77
85
  ```bash
78
- npm install sol-parser-sdk@0.5.13
86
+ npm install sol-parser-sdk@0.5.14
79
87
  ```
80
88
 
81
89
  **From source** (folder may be named `sol-parser-sdk-ts` in a monorepo)
package/README_CN.md CHANGED
@@ -38,12 +38,20 @@
38
38
  | 解析输入 | Yellowstone gRPC、ShredStream、RPC 交易、编码交易、协议账户数据 |
39
39
  | DEX 协议 | PumpFun、PumpSwap、Pump Fees、Raydium LaunchLab、Raydium CPMM、Raydium CLMM、Raydium AMM V4、Meteora DAMM v2、Meteora DLMM、Meteora DBC、Orca Whirlpool |
40
40
  | 使用场景 | 实时 DEX 事件解析、代币发射监控、跟单交易、狙击机器人、账户填充、JSON 事件管道 |
41
- | 运行环境 | Node.js 18+、TypeScript、npm/yarn/pnpm 项目 |
41
+ | 运行环境 | Node.js 20.18+、TypeScript、npm/yarn/pnpm 项目 |
42
42
 
43
43
  ---
44
44
 
45
45
  ## 发布说明
46
46
 
47
+ ### v0.5.14
48
+
49
+ - 将 Yellowstone 客户端升级到 `@triton-one/yellowstone-grpc` 7.0.0,使用其原生 Rust N-API 传输层。
50
+ - 新增 `source_to_grpc_latency_us`,用于区分服务商或传输层积压与精确的本地处理延迟。
51
+ - 修复 Yellowstone `createdAt` 使用 JavaScript `Date` 对象表示时的时间戳解析。
52
+ - 更新原生连接、keepalive、流量控制、流生命周期和 unary RPC 兼容处理。
53
+ - 要求 Node.js 20.18 或更高版本。
54
+
47
55
  ### v0.5.8
48
56
 
49
57
  - 与 Rust SDK 的事件模型和 program 路由保持解析对齐。
@@ -75,7 +83,7 @@
75
83
  **npm**
76
84
 
77
85
  ```bash
78
- npm install sol-parser-sdk@0.5.13
86
+ npm install sol-parser-sdk@0.5.14
79
87
  ```
80
88
 
81
89
  **源码**(monorepo 里目录可能是 `sol-parser-sdk-ts`)
@@ -12,6 +12,8 @@ export interface EventMetadata {
12
12
  parse_duration_us?: number;
13
13
  /** Start of the local gRPC data callback through parser completion. No RPC calls are included. */
14
14
  local_processing_latency_us?: number;
15
+ /** Yellowstone update creation through the local gRPC data callback; includes provider and transport delay. */
16
+ source_to_grpc_latency_us?: number;
15
17
  recent_blockhash?: string;
16
18
  }
17
19
  export declare function makeMetadata(signature: string, slot: number, txIndex: number, blockTimeUs: number | undefined, grpcRecvUs: number, recentBlockhash?: string): EventMetadata;
@@ -46,6 +46,7 @@ export declare class YellowstoneGrpc {
46
46
  private client;
47
47
  private config;
48
48
  private connected;
49
+ private connectPromise?;
49
50
  private subscribers;
50
51
  private dexSubscribers;
51
52
  constructor(endpoint: string, xToken: string, config?: ClientConfig);
@@ -55,10 +55,10 @@ const types_js_1 = require("./types.js");
55
55
  function cancelSubscribeStream(stream) {
56
56
  if (!stream)
57
57
  return;
58
- // grpc-js emits CANCELLED through `error`; guard the short setup window before
58
+ // Stream destruction can emit an error during the short setup window before
59
59
  // the normal lifecycle listeners have been installed.
60
60
  stream.once("error", () => { });
61
- stream.cancel();
61
+ stream.destroy();
62
62
  }
63
63
  function positiveIntegerOption(value, fallback, name) {
64
64
  const normalized = value ?? fallback;
@@ -101,6 +101,7 @@ class YellowstoneGrpc {
101
101
  client;
102
102
  config;
103
103
  connected = false;
104
+ connectPromise;
104
105
  subscribers = new Map();
105
106
  dexSubscribers = new Map();
106
107
  constructor(endpoint, xToken, config = (0, types_js_1.defaultClientConfig)()) {
@@ -117,10 +118,20 @@ class YellowstoneGrpc {
117
118
  }
118
119
  /** 连接到 gRPC 服务器 */
119
120
  async connect() {
120
- if (this.connected) {
121
+ if (this.connected)
121
122
  return;
123
+ if (!this.connectPromise) {
124
+ this.connectPromise = this.client.connect().then(() => {
125
+ this.connected = true;
126
+ });
127
+ }
128
+ try {
129
+ await this.connectPromise;
130
+ }
131
+ catch (error) {
132
+ this.connectPromise = undefined;
133
+ throw error;
122
134
  }
123
- this.connected = true;
124
135
  }
125
136
  /** 断开连接 */
126
137
  async disconnect() {
@@ -133,6 +144,7 @@ class YellowstoneGrpc {
133
144
  }
134
145
  this.dexSubscribers.clear();
135
146
  this.connected = false;
147
+ this.connectPromise = undefined;
136
148
  }
137
149
  /** 检查是否已连接 */
138
150
  isConnected() {
@@ -283,6 +295,10 @@ class YellowstoneGrpc {
283
295
  return Number.NaN;
284
296
  }
285
297
  static timestampToMicros(value) {
298
+ if (value instanceof Date) {
299
+ const milliseconds = value.getTime();
300
+ return Number.isFinite(milliseconds) ? Math.trunc(milliseconds * 1_000) : undefined;
301
+ }
286
302
  if (!value || typeof value !== "object")
287
303
  return undefined;
288
304
  const ts = value;
@@ -351,6 +367,7 @@ class YellowstoneGrpc {
351
367
  }
352
368
  /** 订阅交易 */
353
369
  async subscribeTransactions(filter, callbacks) {
370
+ await this.connect();
354
371
  const id = `sub_${Date.now()}_${Math.random().toString(36).slice(2)}`;
355
372
  let isCancelled = false;
356
373
  const streamHolder = { current: null };
@@ -516,7 +533,7 @@ class YellowstoneGrpc {
516
533
  for (const resolve of ingressIdleWaiters.splice(0))
517
534
  resolve();
518
535
  };
519
- const annotateLocalMetrics = (events, grpcRecvNs, parseStartedNs, parseFinishedNs) => {
536
+ const annotateLocalMetrics = (events, grpcRecvNs, parseStartedNs, parseFinishedNs, sourceToGrpcLatencyUs) => {
520
537
  if (grpcRecvNs === undefined || parseStartedNs === undefined || parseFinishedNs === undefined) {
521
538
  return;
522
539
  }
@@ -530,6 +547,9 @@ class YellowstoneGrpc {
530
547
  metadata.local_queue_latency_us = queueUs;
531
548
  metadata.parse_duration_us = parseUs;
532
549
  metadata.local_processing_latency_us = processingUs;
550
+ if (sourceToGrpcLatencyUs !== undefined) {
551
+ metadata.source_to_grpc_latency_us = sourceToGrpcLatencyUs;
552
+ }
533
553
  }
534
554
  };
535
555
  const rememberParsedUpdate = (identity, slot) => {
@@ -555,7 +575,7 @@ class YellowstoneGrpc {
555
575
  seenHead = 0;
556
576
  }
557
577
  };
558
- const processUpdate = ({ update, grpcRecvUs, grpcRecvNs, blockTimeUs }) => {
578
+ const processUpdate = ({ update, grpcRecvUs, grpcRecvNs, blockTimeUs, sourceToGrpcLatencyUs, }) => {
559
579
  const identity = updateIdentity(update);
560
580
  const slotForReplay = updateSlot(update);
561
581
  if (replayOnReconnect && identity !== undefined && seenUpdates.has(identity)) {
@@ -585,15 +605,16 @@ class YellowstoneGrpc {
585
605
  eventTypeFilter,
586
606
  });
587
607
  const parseFinishedNs = grpcRecvNs === undefined ? undefined : process.hrtime.bigint();
588
- annotateLocalMetrics(txEvents, grpcRecvNs, parseStartedNs, parseFinishedNs);
608
+ annotateLocalMetrics(txEvents, grpcRecvNs, parseStartedNs, parseFinishedNs, sourceToGrpcLatencyUs);
589
609
  order.pushTransactionEvents(txEvents, fallbackSlot, fallbackTxIndex, emitEvent);
590
610
  }
591
611
  if (update.account) {
592
612
  const parseStartedNs = grpcRecvNs === undefined ? undefined : process.hrtime.bigint();
593
613
  const ev = this.parseAccountEventRaw(update.account, grpcRecvUs, eventTypeFilter, blockTimeUs);
594
614
  const parseFinishedNs = grpcRecvNs === undefined ? undefined : process.hrtime.bigint();
595
- if (ev)
596
- annotateLocalMetrics([ev], grpcRecvNs, parseStartedNs, parseFinishedNs);
615
+ if (ev) {
616
+ annotateLocalMetrics([ev], grpcRecvNs, parseStartedNs, parseFinishedNs, sourceToGrpcLatencyUs);
617
+ }
597
618
  if (ev)
598
619
  emitEvent(ev);
599
620
  }
@@ -746,11 +767,13 @@ class YellowstoneGrpc {
746
767
  });
747
768
  return;
748
769
  }
770
+ const createdAtUs = YellowstoneGrpc.timestampToMicros(update.createdAt ?? update.created_at);
749
771
  const accepted = ingress.push({
750
772
  update,
751
773
  grpcRecvUs,
752
774
  grpcRecvNs,
753
- blockTimeUs: YellowstoneGrpc.timestampToMicros(update.createdAt ?? update.created_at),
775
+ blockTimeUs: createdAtUs,
776
+ sourceToGrpcLatencyUs: createdAtUs === undefined ? undefined : Math.max(0, grpcRecvUs - createdAtUs),
754
777
  });
755
778
  if (!accepted) {
756
779
  const now = Date.now();
@@ -878,6 +901,7 @@ class YellowstoneGrpc {
878
901
  }
879
902
  /** 获取最新区块哈希 */
880
903
  async getLatestBlockhash(commitment) {
904
+ await this.connect();
881
905
  const resp = await this.client.getLatestBlockhash(commitment);
882
906
  return {
883
907
  slot: Number(resp.slot),
@@ -887,21 +911,25 @@ class YellowstoneGrpc {
887
911
  }
888
912
  /** 获取区块高度 */
889
913
  async getBlockHeight(commitment) {
914
+ await this.connect();
890
915
  const resp = await this.client.getBlockHeight(commitment);
891
- return Number(resp);
916
+ return Number(resp.blockHeight);
892
917
  }
893
918
  /** 获取当前 Slot */
894
919
  async getSlot(commitment) {
920
+ await this.connect();
895
921
  const resp = await this.client.getSlot(commitment);
896
- return Number(resp);
922
+ return Number(resp.slot);
897
923
  }
898
924
  /** 获取服务器版本 */
899
925
  async getVersion() {
926
+ await this.connect();
900
927
  const resp = await this.client.getVersion();
901
- return String(resp);
928
+ return resp.version;
902
929
  }
903
930
  /** 验证区块哈希是否有效 */
904
931
  async isBlockhashValid(blockhash, commitment) {
932
+ await this.connect();
905
933
  const resp = await this.client.isBlockhashValid(blockhash, commitment);
906
934
  return {
907
935
  slot: Number(resp.slot),
@@ -4,8 +4,7 @@
4
4
  * `@triton-one/yellowstone-grpc` 的 `Client` 通过构造函数第三参传入 `ChannelOptions`;
5
5
  * `connectTimeoutMs` 无与 tonic 完全一一对应的项,仅作文档与默认值对齐(可配合环境网络调优)。
6
6
  */
7
- import type { ChannelOptions } from "@grpc/grpc-js";
8
- import Client from "@triton-one/yellowstone-grpc";
7
+ import Client, { type ChannelOptions } from "@triton-one/yellowstone-grpc";
9
8
  /** 与 Rust `GeyserConnectConfig` 字段对应(毫秒 / 字节) */
10
9
  export interface GeyserConnectConfig {
11
10
  /** 对应 Rust `connect_timeout`(毫秒) */
@@ -6,6 +6,12 @@ Object.defineProperty(exports, "__esModule", { value: true });
6
6
  exports.defaultGeyserConnectConfig = defaultGeyserConnectConfig;
7
7
  exports.geyserGrpcChannelOptions = geyserGrpcChannelOptions;
8
8
  exports.connectYellowstoneGeyser = connectYellowstoneGeyser;
9
+ /**
10
+ * 与 Rust `sol-parser-sdk/src/grpc/geyser_connect.rs` 对齐:Yellowstone Geyser gRPC 连接选项。
11
+ *
12
+ * `@triton-one/yellowstone-grpc` 的 `Client` 通过构造函数第三参传入 `ChannelOptions`;
13
+ * `connectTimeoutMs` 无与 tonic 完全一一对应的项,仅作文档与默认值对齐(可配合环境网络调优)。
14
+ */
9
15
  const yellowstone_grpc_1 = __importDefault(require("@triton-one/yellowstone-grpc"));
10
16
  /** 公共字段与 Rust `GeyserConnectConfig::default` 一致,并包含 Node gRPC 通道默认值。 */
11
17
  function defaultGeyserConnectConfig() {
@@ -26,15 +32,17 @@ function geyserGrpcChannelOptions(config = defaultGeyserConnectConfig()) {
26
32
  const interval = config.keepAliveIntervalMs ?? 30_000;
27
33
  const timeout = config.keepAliveTimeoutMs ?? 5000;
28
34
  return {
29
- "grpc.max_receive_message_length": max,
30
- "grpc.max_send_message_length": max,
31
- "grpc.keepalive_time_ms": interval,
32
- "grpc.keepalive_timeout_ms": timeout,
33
- /** 无活跃 RPC 时仍发 keepalive,避免长时间仅订阅时被中间设备掐断 */
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,
35
+ grpcConnectTimeout: config.connectTimeoutMs,
36
+ grpcMaxDecodingMessageSize: max,
37
+ grpcMaxEncodingMessageSize: max,
38
+ grpcHttp2KeepAliveInterval: interval,
39
+ grpcKeepAliveTimeout: timeout,
40
+ grpcTcpKeepalive: interval,
41
+ grpcInitialConnectionWindowSize: config.flowControlWindowBytes,
42
+ grpcInitialStreamWindowSize: config.flowControlWindowBytes,
43
+ grpcHttp2AdaptiveWindow: true,
44
+ grpcKeepAliveWhileIdle: true,
45
+ grpcTcpNodelay: true,
38
46
  };
39
47
  }
40
48
  /**
@@ -42,5 +50,7 @@ function geyserGrpcChannelOptions(config = defaultGeyserConnectConfig()) {
42
50
  * Rust 为 async;此处构造同步完成,返回 `Promise` 以保持 `await connectYellowstoneGeyser(...)` 写法。
43
51
  */
44
52
  async function connectYellowstoneGeyser(endpoint, config = defaultGeyserConnectConfig()) {
45
- return new yellowstone_grpc_1.default(endpoint, config.xToken, geyserGrpcChannelOptions(config));
53
+ const client = new yellowstone_grpc_1.default(endpoint, config.xToken, geyserGrpcChannelOptions(config));
54
+ await client.connect();
55
+ return client;
46
56
  }
@@ -1,6 +1,8 @@
1
- import type { Transaction as YTransaction, TransactionStatusMeta } from "@triton-one/yellowstone-grpc/dist/grpc/solana-storage.js";
1
+ import type { SubscribeUpdateTransactionInfo } from "@triton-one/yellowstone-grpc";
2
2
  import { type VersionedTransactionResponse } from "@solana/web3.js";
3
3
  import type { ParseError } from "../core/error.js";
4
+ type YTransaction = NonNullable<SubscribeUpdateTransactionInfo["transaction"]>;
5
+ type TransactionStatusMeta = NonNullable<SubscribeUpdateTransactionInfo["meta"]>;
4
6
  export type ConvertRpcToGrpcOk = {
5
7
  ok: true;
6
8
  meta: TransactionStatusMeta;
@@ -16,3 +18,4 @@ export type ConvertRpcToGrpcErr = {
16
18
  export declare function convertRpcToGrpc(tx: VersionedTransactionResponse): ConvertRpcToGrpcOk | ConvertRpcToGrpcErr;
17
19
  /** Rust 蛇形命名 */
18
20
  export declare const convert_rpc_to_grpc: typeof convertRpcToGrpc;
21
+ export {};
@@ -1,4 +1,7 @@
1
- import type { TokenBalance, Transaction, TransactionStatusMeta } from "@triton-one/yellowstone-grpc/dist/grpc/solana-storage.js";
1
+ import type { SubscribeUpdateTransactionInfo } from "@triton-one/yellowstone-grpc";
2
+ type Transaction = NonNullable<SubscribeUpdateTransactionInfo["transaction"]>;
3
+ type TransactionStatusMeta = NonNullable<SubscribeUpdateTransactionInfo["meta"]>;
4
+ type TokenBalance = TransactionStatusMeta["preTokenBalances"][number];
2
5
  /** 32 字节公钥 → Base58(无效长度返回 `null`) */
3
6
  export declare function pubkeyBytesToBs58(bytes: Uint8Array): string | null;
4
7
  /**
@@ -27,3 +30,4 @@ export declare function splTokenCounterpartyByOwner(meta: TransactionStatusMeta,
27
30
  export declare function collectWatchTransferCounterpartyPairs(tx: Transaction, meta: TransactionStatusMeta, watchedBs58: string[], minNativeOutflowLamports: bigint, splMinWatchDecreaseRaw: bigint): [string, string][] | null;
28
31
  /** Yellowstone 交易签名原始字节(64)→ Base58 签名字符串;长度非 64 返回 `null` */
29
32
  export declare function tryYellowstoneSignature(sig: Uint8Array): string | null;
33
+ export {};
@@ -1,4 +1,7 @@
1
1
  import { type VersionedTransactionResponse } from "@solana/web3.js";
2
- import type { Transaction as YellowstoneTransaction, TransactionStatusMeta as YellowstoneMeta } from "@triton-one/yellowstone-grpc/dist/grpc/solana-storage.js";
2
+ import type { SubscribeUpdateTransactionInfo } from "@triton-one/yellowstone-grpc";
3
+ type YellowstoneTransaction = NonNullable<SubscribeUpdateTransactionInfo["transaction"]>;
4
+ type YellowstoneMeta = NonNullable<SubscribeUpdateTransactionInfo["meta"]>;
3
5
  /** Convert Yellowstone protobuf objects directly, without a WASM/Base58 serialization round trip. */
4
6
  export declare function yellowstoneTransactionToWeb3(transaction: YellowstoneTransaction, meta: YellowstoneMeta, slot: number, blockTime: number | null): VersionedTransactionResponse;
7
+ export {};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "sol-parser-sdk",
3
- "version": "0.5.13",
3
+ "version": "0.5.14",
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",
@@ -20,7 +20,7 @@
20
20
  },
21
21
  "homepage": "https://github.com/0xfnzero/sol-parser-sdk-nodejs#readme",
22
22
  "engines": {
23
- "node": ">=18"
23
+ "node": ">=20.18.0"
24
24
  },
25
25
  "scripts": {
26
26
  "build": "tsc && mkdir -p dist/shredstream && cp src/shredstream/shredstream.proto dist/shredstream/",
@@ -58,7 +58,7 @@
58
58
  "@grpc/grpc-js": "^1.12.4",
59
59
  "@grpc/proto-loader": "^0.7.13",
60
60
  "@solana/web3.js": "^1.95.4",
61
- "@triton-one/yellowstone-grpc": "^1.0.0",
61
+ "@triton-one/yellowstone-grpc": "7.0.0",
62
62
  "bs58": "^6.0.0"
63
63
  }
64
64
  }