sol-parser-sdk 0.5.14 → 0.5.16
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/NATIVE_ALIGNMENT.md +312 -0
- package/NATIVE_DAMM_FACTORY_ALIGNMENT_20261004.md +16 -0
- package/NATIVE_DAMM_FACTORY_EVIDENCE_20261004.json +55 -0
- package/NATIVE_MIGRATION.md +38 -0
- package/NATIVE_PUMPFUN_NATIVE_DEBIT_ALIGNMENT_20261004.md +18 -0
- package/NATIVE_PUMPFUN_NATIVE_DEBIT_EVIDENCE_20261004.json +57 -0
- package/NATIVE_PUMPFUN_SETTLEMENT_ALIGNMENT_20261004.md +11 -0
- package/NATIVE_PUMPFUN_SETTLEMENT_EVIDENCE_20261004.json +70 -0
- package/README.md +24 -1
- package/README_CN.md +21 -1
- package/dist/accounts/mod.js +7 -0
- package/dist/accounts/pumpfun.js +17 -0
- package/dist/accounts/pumpswap.js +18 -3
- package/dist/core/account_dispatcher_rpc.js +24 -4
- package/dist/core/account_fill_meteora.d.ts +1 -1
- package/dist/core/account_fill_meteora.js +12 -1
- package/dist/core/account_fill_raydium.js +12 -2
- package/dist/core/account_fill_raydium_launchlab.js +54 -0
- package/dist/core/clock.js +1 -1
- package/dist/core/common_filler_rpc.d.ts +2 -2
- package/dist/core/common_filler_rpc.js +66 -0
- package/dist/core/dex_event.d.ts +157 -0
- package/dist/core/metadata.d.ts +9 -5
- package/dist/core/metadata.js +28 -4
- package/dist/core/unified_parser.d.ts +7 -7
- package/dist/grpc/block_meta.d.ts +9 -0
- package/dist/grpc/block_meta.js +26 -0
- package/dist/grpc/client.d.ts +24 -9
- package/dist/grpc/client.js +129 -67
- package/dist/grpc/geyser_connect.d.ts +3 -3
- package/dist/grpc/geyser_connect.js +13 -15
- package/dist/grpc/log_instr_dedup.js +6 -0
- package/dist/grpc/native_client.d.ts +26 -0
- package/dist/grpc/native_client.js +101 -0
- package/dist/grpc/order_buffer.d.ts +1 -1
- package/dist/grpc/order_buffer.js +10 -9
- package/dist/grpc/program_ids.js +2 -0
- package/dist/grpc/protocol/LICENSE +202 -0
- package/dist/grpc/protocol/NOTICE +4 -0
- package/dist/grpc/protocol/geyser.d.ts +342 -0
- package/dist/grpc/protocol/geyser.js +37 -0
- package/dist/grpc/protocol/geyser.proto +326 -0
- package/dist/grpc/protocol/solana-storage.d.ts +139 -0
- package/dist/grpc/protocol/solana-storage.js +14 -0
- package/dist/grpc/protocol/solana-storage.proto +151 -0
- package/dist/grpc/rpc_to_grpc.d.ts +1 -1
- package/dist/grpc/subscribe_builder.d.ts +5 -3
- package/dist/grpc/subscribe_builder.js +11 -3
- package/dist/grpc/transaction_meta.d.ts +1 -1
- package/dist/grpc/types.d.ts +8 -8
- package/dist/grpc/types.js +15 -2
- package/dist/grpc/yellowstone_parse.d.ts +3 -3
- package/dist/grpc/yellowstone_parse.js +6 -8
- package/dist/grpc/yellowstone_transaction_adapter.d.ts +1 -1
- package/dist/index.d.ts +11 -1
- package/dist/index.js +23 -5
- package/dist/instr/inner.d.ts +2 -2
- package/dist/instr/inner.js +1 -1
- package/dist/instr/meteora_damm_ix.d.ts +1 -1
- package/dist/instr/meteora_damm_ix.js +41 -79
- package/dist/instr/meteora_dlmm_ix.d.ts +1 -1
- package/dist/instr/meteora_dlmm_ix.js +10 -0
- package/dist/instr/meteora_pools_ix.d.ts +1 -1
- package/dist/instr/mod.d.ts +1 -1
- package/dist/instr/orca_whirlpool_ix.d.ts +1 -1
- package/dist/instr/pump_fees_ix.d.ts +1 -1
- package/dist/instr/pumpfun_ix.d.ts +1 -1
- package/dist/instr/pumpfun_ix.js +16 -0
- package/dist/instr/pumpswap_ix.d.ts +1 -1
- package/dist/instr/pumpswap_ix.js +13 -5
- package/dist/instr/raydium_amm_v4_ix.d.ts +1 -1
- package/dist/instr/raydium_amm_v4_ix.js +9 -5
- package/dist/instr/raydium_clmm_ix.d.ts +1 -1
- package/dist/instr/raydium_cpmm_ix.d.ts +1 -1
- package/dist/instr/raydium_launchlab_ix.d.ts +1 -1
- package/dist/instr/raydium_launchlab_ix.js +62 -2
- package/dist/instr/utils.d.ts +2 -2
- package/dist/instr/utils.js +2 -7
- package/dist/liquidity_snapshot.d.ts +54 -0
- package/dist/liquidity_snapshot.js +85 -0
- package/dist/logs/meteora_damm.d.ts +17 -1
- package/dist/logs/meteora_damm.js +386 -12
- package/dist/logs/meteora_dlmm.d.ts +1 -1
- package/dist/logs/meteora_dlmm.js +10 -0
- package/dist/logs/optimized_matcher.d.ts +3 -3
- package/dist/logs/optimized_matcher.js +35 -1
- package/dist/logs/program_log_discriminators.d.ts +5 -0
- package/dist/logs/program_log_discriminators.js +5 -0
- package/dist/logs/pump.js +10 -0
- package/dist/logs/pump_amm.js +22 -1
- package/dist/logs/raydium_launchlab.js +19 -1
- package/dist/rpc_transaction.d.ts +4 -3
- package/dist/rpc_transaction.js +3 -2
- package/dist/shredstream/client.js +3 -7
- package/dist/shredstream/entries_decode.js +9 -85
- package/dist/shredstream/instruction_parse.d.ts +3 -3
- package/dist/shredstream/proto_types.d.ts +1 -1
- package/dist/shredstream/wire_to_shred_tx.d.ts +1 -1
- package/dist/shredstream/wire_to_shred_tx.js +9 -44
- package/dist/simulation_route.d.ts +6 -0
- package/dist/simulation_route.js +242 -0
- package/dist/stonkfun_registry.d.ts +30 -0
- package/dist/stonkfun_registry.js +137 -0
- package/dist/transaction_route.d.ts +84 -0
- package/dist/transaction_route.js +477 -0
- package/dist/wire_transaction.d.ts +35 -0
- package/dist/wire_transaction.js +191 -0
- package/package.json +7 -4
package/dist/grpc/client.js
CHANGED
|
@@ -1,47 +1,17 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
-
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
-
if (k2 === undefined) k2 = k;
|
|
4
|
-
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
-
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
-
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
-
}
|
|
8
|
-
Object.defineProperty(o, k2, desc);
|
|
9
|
-
}) : (function(o, m, k, k2) {
|
|
10
|
-
if (k2 === undefined) k2 = k;
|
|
11
|
-
o[k2] = m[k];
|
|
12
|
-
}));
|
|
13
|
-
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
-
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
-
}) : function(o, v) {
|
|
16
|
-
o["default"] = v;
|
|
17
|
-
});
|
|
18
|
-
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
-
var ownKeys = function(o) {
|
|
20
|
-
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
-
var ar = [];
|
|
22
|
-
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
-
return ar;
|
|
24
|
-
};
|
|
25
|
-
return ownKeys(o);
|
|
26
|
-
};
|
|
27
|
-
return function (mod) {
|
|
28
|
-
if (mod && mod.__esModule) return mod;
|
|
29
|
-
var result = {};
|
|
30
|
-
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
-
__setModuleDefault(result, mod);
|
|
32
|
-
return result;
|
|
33
|
-
};
|
|
34
|
-
})();
|
|
35
2
|
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
36
3
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
37
4
|
};
|
|
38
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
39
6
|
exports.YellowstoneGrpc = void 0;
|
|
7
|
+
const block_meta_js_1 = require("./block_meta.js");
|
|
40
8
|
/**
|
|
41
|
-
* Yellowstone gRPC 客户端实现 -
|
|
9
|
+
* Yellowstone gRPC 客户端实现 - 基于纯 JavaScript @grpc/grpc-js
|
|
42
10
|
*/
|
|
43
|
-
const
|
|
11
|
+
const native_client_js_1 = __importDefault(require("./native_client.js"));
|
|
12
|
+
const geyser_js_1 = require("./protocol/geyser.js");
|
|
44
13
|
const bs58_1 = __importDefault(require("bs58"));
|
|
14
|
+
const node_crypto_1 = require("node:crypto");
|
|
45
15
|
const node_buffer_1 = require("node:buffer");
|
|
46
16
|
const dex_event_js_1 = require("../core/dex_event.js");
|
|
47
17
|
const metadata_js_1 = require("../core/metadata.js");
|
|
@@ -58,7 +28,11 @@ function cancelSubscribeStream(stream) {
|
|
|
58
28
|
// Stream destruction can emit an error during the short setup window before
|
|
59
29
|
// the normal lifecycle listeners have been installed.
|
|
60
30
|
stream.once("error", () => { });
|
|
61
|
-
stream
|
|
31
|
+
const cancellable = stream;
|
|
32
|
+
if (typeof cancellable.cancel === "function")
|
|
33
|
+
cancellable.cancel();
|
|
34
|
+
else
|
|
35
|
+
stream.destroy();
|
|
62
36
|
}
|
|
63
37
|
function positiveIntegerOption(value, fallback, name) {
|
|
64
38
|
const normalized = value ?? fallback;
|
|
@@ -74,7 +48,7 @@ function replayUnsupported(error) {
|
|
|
74
48
|
return /from.?slot.*(?:unsupported|disabled|unavailable|invalid)/i.test(`${grpcError.message ?? ""} ${grpcError.details ?? ""}`);
|
|
75
49
|
}
|
|
76
50
|
function updateSlot(update) {
|
|
77
|
-
const value = update.transaction?.slot ?? update.account?.slot;
|
|
51
|
+
const value = update.transaction?.slot ?? update.account?.slot ?? update.blockMeta?.slot;
|
|
78
52
|
if (value === undefined || value === null)
|
|
79
53
|
return undefined;
|
|
80
54
|
try {
|
|
@@ -91,6 +65,8 @@ function updateIdentity(update) {
|
|
|
91
65
|
const transaction = update.transaction?.transaction;
|
|
92
66
|
if (transaction?.signature)
|
|
93
67
|
return `t:${bytesKey(transaction.signature)}`;
|
|
68
|
+
if (update.blockMeta)
|
|
69
|
+
return `b:${String(update.blockMeta.slot)}:${update.blockMeta.blockhash}`;
|
|
94
70
|
const account = update.account?.account;
|
|
95
71
|
if (!account?.pubkey)
|
|
96
72
|
return undefined;
|
|
@@ -104,10 +80,28 @@ class YellowstoneGrpc {
|
|
|
104
80
|
connectPromise;
|
|
105
81
|
subscribers = new Map();
|
|
106
82
|
dexSubscribers = new Map();
|
|
83
|
+
dexLifecycle = Promise.resolve();
|
|
84
|
+
activeDex;
|
|
85
|
+
withDexLifecycle(work) {
|
|
86
|
+
const result = this.dexLifecycle.then(work);
|
|
87
|
+
this.dexLifecycle = result.then(() => undefined, () => undefined);
|
|
88
|
+
return result;
|
|
89
|
+
}
|
|
90
|
+
async stopDexUnlocked() {
|
|
91
|
+
const active = this.activeDex;
|
|
92
|
+
this.activeDex = undefined;
|
|
93
|
+
if (active) {
|
|
94
|
+
active.cancel();
|
|
95
|
+
await active.join();
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
async stop() {
|
|
99
|
+
await this.withDexLifecycle(() => this.stopDexUnlocked());
|
|
100
|
+
}
|
|
107
101
|
constructor(endpoint, xToken, config = (0, types_js_1.defaultClientConfig)()) {
|
|
108
102
|
this.config = config;
|
|
109
103
|
const gc = (0, geyser_connect_js_1.defaultGeyserConnectConfig)();
|
|
110
|
-
this.client = new
|
|
104
|
+
this.client = new native_client_js_1.default(endpoint, xToken || undefined, (0, geyser_connect_js_1.geyserGrpcChannelOptions)({
|
|
111
105
|
...gc,
|
|
112
106
|
keepAliveIntervalMs: config.keep_alive_interval_ms,
|
|
113
107
|
keepAliveTimeoutMs: config.keep_alive_timeout_ms,
|
|
@@ -135,6 +129,7 @@ class YellowstoneGrpc {
|
|
|
135
129
|
}
|
|
136
130
|
/** 断开连接 */
|
|
137
131
|
async disconnect() {
|
|
132
|
+
await this.stop();
|
|
138
133
|
for (const [, sub] of this.subscribers) {
|
|
139
134
|
sub.cancel();
|
|
140
135
|
}
|
|
@@ -143,6 +138,7 @@ class YellowstoneGrpc {
|
|
|
143
138
|
sub.cancel();
|
|
144
139
|
}
|
|
145
140
|
this.dexSubscribers.clear();
|
|
141
|
+
this.client.close?.();
|
|
146
142
|
this.connected = false;
|
|
147
143
|
this.connectPromise = undefined;
|
|
148
144
|
}
|
|
@@ -265,7 +261,7 @@ class YellowstoneGrpc {
|
|
|
265
261
|
entry: {},
|
|
266
262
|
blocks: {},
|
|
267
263
|
blocksMeta: {},
|
|
268
|
-
commitment:
|
|
264
|
+
commitment: geyser_js_1.CommitmentLevel.CONFIRMED,
|
|
269
265
|
accountsDataSlice: [],
|
|
270
266
|
ping: { id: 1 },
|
|
271
267
|
};
|
|
@@ -276,7 +272,7 @@ class YellowstoneGrpc {
|
|
|
276
272
|
return value instanceof Uint8Array ? value : Uint8Array.from(value);
|
|
277
273
|
}
|
|
278
274
|
static toBigInt(value) {
|
|
279
|
-
return
|
|
275
|
+
return (0, metadata_js_1.exactU64)(value);
|
|
280
276
|
}
|
|
281
277
|
static protobufNumber(value) {
|
|
282
278
|
if (typeof value === "bigint")
|
|
@@ -297,18 +293,20 @@ class YellowstoneGrpc {
|
|
|
297
293
|
static timestampToMicros(value) {
|
|
298
294
|
if (value instanceof Date) {
|
|
299
295
|
const milliseconds = value.getTime();
|
|
300
|
-
return Number.isFinite(milliseconds) ?
|
|
296
|
+
return Number.isFinite(milliseconds) ? BigInt(milliseconds) * 1000n : undefined;
|
|
301
297
|
}
|
|
302
298
|
if (!value || typeof value !== "object")
|
|
303
299
|
return undefined;
|
|
304
300
|
const ts = value;
|
|
305
301
|
if (ts.seconds === undefined)
|
|
306
302
|
return undefined;
|
|
307
|
-
const seconds =
|
|
308
|
-
const nanos =
|
|
309
|
-
if (
|
|
310
|
-
|
|
311
|
-
|
|
303
|
+
const seconds = (0, metadata_js_1.exactInteger)(typeof ts.seconds === "object" && ts.seconds !== null ? ts.seconds.toString() : ts.seconds, "timestamp seconds");
|
|
304
|
+
const nanos = (0, metadata_js_1.exactInteger)((ts.nanos ?? 0), "timestamp nanos");
|
|
305
|
+
if (nanos < 0n || nanos >= 1000000000n)
|
|
306
|
+
throw new RangeError("Invalid timestamp nanos");
|
|
307
|
+
const micros = seconds * 1000000n + nanos / 1000n;
|
|
308
|
+
const lo = -(1n << 63n), hi = (1n << 63n) - 1n;
|
|
309
|
+
return micros < lo ? lo : micros > hi ? hi : micros;
|
|
312
310
|
}
|
|
313
311
|
parseAccountEvent(update, grpcRecvUs, eventTypeFilter, blockTimeUs) {
|
|
314
312
|
const acc = update.account;
|
|
@@ -323,10 +321,10 @@ class YellowstoneGrpc {
|
|
|
323
321
|
rent_epoch: YellowstoneGrpc.toBigInt(acc.rentEpoch),
|
|
324
322
|
data: YellowstoneGrpc.toBytes(acc.data),
|
|
325
323
|
};
|
|
326
|
-
const metadata = (0, metadata_js_1.makeMetadata)(signatureBytes.length > 0 ? bs58_1.default.encode(signatureBytes) : "",
|
|
324
|
+
const metadata = (0, metadata_js_1.makeMetadata)(signatureBytes.length > 0 ? bs58_1.default.encode(signatureBytes) : "", update.slot, 0, blockTimeUs, grpcRecvUs);
|
|
327
325
|
return (0, mod_js_1.parseAccountUnified)(account, metadata, eventTypeFilter);
|
|
328
326
|
}
|
|
329
|
-
parseAccountEventRaw(update, grpcRecvUs, eventTypeFilter, blockTimeUs) {
|
|
327
|
+
parseAccountEventRaw(update, grpcRecvUs, eventTypeFilter, blockTimeUs, rawSnapshot = false) {
|
|
330
328
|
const acc = update.account;
|
|
331
329
|
if (!acc)
|
|
332
330
|
return null;
|
|
@@ -339,7 +337,9 @@ class YellowstoneGrpc {
|
|
|
339
337
|
rent_epoch: YellowstoneGrpc.toBigInt(acc.rentEpoch),
|
|
340
338
|
data: YellowstoneGrpc.toBytes(acc.data),
|
|
341
339
|
};
|
|
342
|
-
const metadata = (0, metadata_js_1.makeMetadata)(signatureBytes.length > 0 ? bs58_1.default.encode(signatureBytes) : "",
|
|
340
|
+
const metadata = (0, metadata_js_1.makeMetadata)(signatureBytes.length > 0 ? bs58_1.default.encode(signatureBytes) : "", update.slot, 0, blockTimeUs, grpcRecvUs);
|
|
341
|
+
if (rawSnapshot)
|
|
342
|
+
return { RawAccountSnapshot: { metadata, account: { ...account, data: Uint8Array.from(account.data) }, write_version: YellowstoneGrpc.toBigInt(acc.writeVersion), is_startup: update.isStartup } };
|
|
343
343
|
return (0, mod_js_1.parseAccountUnified)(account, metadata, eventTypeFilter);
|
|
344
344
|
}
|
|
345
345
|
initialSubscribeRequest(filter) {
|
|
@@ -360,7 +360,7 @@ class YellowstoneGrpc {
|
|
|
360
360
|
entry: {},
|
|
361
361
|
blocks: {},
|
|
362
362
|
blocksMeta: {},
|
|
363
|
-
commitment:
|
|
363
|
+
commitment: geyser_js_1.CommitmentLevel.CONFIRMED,
|
|
364
364
|
accountsDataSlice: [],
|
|
365
365
|
ping: undefined,
|
|
366
366
|
};
|
|
@@ -377,7 +377,7 @@ class YellowstoneGrpc {
|
|
|
377
377
|
streamHolder.current = null;
|
|
378
378
|
};
|
|
379
379
|
this.subscribers.set(id, { filter, callbacks, cancel });
|
|
380
|
-
(async () => {
|
|
380
|
+
const streamTask = (async () => {
|
|
381
381
|
const autoReconnect = callbacks.autoReconnect !== false;
|
|
382
382
|
const maxBackoffMs = 60_000;
|
|
383
383
|
let backoffMs = this.config.retry_delay_ms;
|
|
@@ -482,6 +482,12 @@ class YellowstoneGrpc {
|
|
|
482
482
|
* 事件队列满时按配置丢弃事件,避免回调阻塞 gRPC 读循环并让丢弃可观测。
|
|
483
483
|
*/
|
|
484
484
|
async subscribeDexEvents(transactionFilters = [], accountFilters = [], eventTypeFilter, options) {
|
|
485
|
+
return this.withDexLifecycle(async () => {
|
|
486
|
+
await this.stopDexUnlocked();
|
|
487
|
+
return this.startDexEvents(transactionFilters, accountFilters, eventTypeFilter, options);
|
|
488
|
+
});
|
|
489
|
+
}
|
|
490
|
+
async startDexEvents(transactionFilters = [], accountFilters = [], eventTypeFilter, options) {
|
|
485
491
|
await this.connect();
|
|
486
492
|
const id = `dex_${Date.now()}_${Math.random().toString(36).slice(2)}`;
|
|
487
493
|
const events = new async_event_queue_js_1.AsyncEventQueue(Math.max(1, this.config.buffer_size), options?.queueOverflowStrategy);
|
|
@@ -504,11 +510,23 @@ class YellowstoneGrpc {
|
|
|
504
510
|
let highestRememberedSlot = 0n;
|
|
505
511
|
let seenHead = 0;
|
|
506
512
|
const seenUpdates = new Map();
|
|
513
|
+
const accountFingerprints = new Map();
|
|
507
514
|
const seenOrder = [];
|
|
515
|
+
const states = new async_event_queue_js_1.AsyncEventQueue(Math.max(1, this.config.buffer_size));
|
|
516
|
+
let streamStatus = Object.freeze({ state: "connecting", continuityBroken: false, reconnects: 0, dropped: 0 });
|
|
517
|
+
states.push(streamStatus);
|
|
518
|
+
const transition = (state, broken = false) => {
|
|
519
|
+
const next = Object.freeze({ state, continuityBroken: streamStatus.continuityBroken || broken, reconnects: reconnectCount, dropped: eventDropped() + ingress.dropped() });
|
|
520
|
+
if (next.state === streamStatus.state && next.continuityBroken === streamStatus.continuityBroken && next.reconnects === streamStatus.reconnects && next.dropped === streamStatus.dropped)
|
|
521
|
+
return;
|
|
522
|
+
streamStatus = next;
|
|
523
|
+
states.push(next);
|
|
524
|
+
};
|
|
508
525
|
const reportError = (error) => errors.push(error);
|
|
509
526
|
const emitEvent = (event) => {
|
|
510
527
|
if (events.push(event))
|
|
511
528
|
return;
|
|
529
|
+
transition(streamStatus.state, true);
|
|
512
530
|
const now = Date.now();
|
|
513
531
|
if (now < nextEventDropWarningAt)
|
|
514
532
|
return;
|
|
@@ -552,12 +570,14 @@ class YellowstoneGrpc {
|
|
|
552
570
|
}
|
|
553
571
|
}
|
|
554
572
|
};
|
|
555
|
-
const rememberParsedUpdate = (identity, slot) => {
|
|
573
|
+
const rememberParsedUpdate = (identity, slot, fingerprint) => {
|
|
556
574
|
if (slot !== undefined && (lastParsedSlot === undefined || slot > lastParsedSlot)) {
|
|
557
575
|
lastParsedSlot = slot;
|
|
558
576
|
}
|
|
559
|
-
if (
|
|
577
|
+
if (identity === undefined || slot === undefined)
|
|
560
578
|
return;
|
|
579
|
+
if (fingerprint !== undefined)
|
|
580
|
+
accountFingerprints.set(identity, fingerprint);
|
|
561
581
|
seenUpdates.set(identity, slot);
|
|
562
582
|
seenOrder.push({ identity, slot });
|
|
563
583
|
if (slot > highestRememberedSlot)
|
|
@@ -567,8 +587,10 @@ class YellowstoneGrpc {
|
|
|
567
587
|
: 0n;
|
|
568
588
|
while (seenHead < seenOrder.length && seenOrder[seenHead].slot < retainFrom) {
|
|
569
589
|
const stale = seenOrder[seenHead++];
|
|
570
|
-
if (seenUpdates.get(stale.identity) === stale.slot)
|
|
590
|
+
if (seenUpdates.get(stale.identity) === stale.slot) {
|
|
571
591
|
seenUpdates.delete(stale.identity);
|
|
592
|
+
accountFingerprints.delete(stale.identity);
|
|
593
|
+
}
|
|
572
594
|
}
|
|
573
595
|
if (seenHead >= 4096 && seenHead * 2 >= seenOrder.length) {
|
|
574
596
|
seenOrder.splice(0, seenHead);
|
|
@@ -578,6 +600,12 @@ class YellowstoneGrpc {
|
|
|
578
600
|
const processUpdate = ({ update, grpcRecvUs, grpcRecvNs, blockTimeUs, sourceToGrpcLatencyUs, }) => {
|
|
579
601
|
const identity = updateIdentity(update);
|
|
580
602
|
const slotForReplay = updateSlot(update);
|
|
603
|
+
const raw = update.account?.account;
|
|
604
|
+
const fingerprint = raw ? (0, node_crypto_1.createHash)("sha256").update(raw.owner).update(raw.data).update(`${raw.lamports}:${raw.executable}:${raw.rentEpoch}`).digest("hex") : undefined;
|
|
605
|
+
if (identity && fingerprint && accountFingerprints.has(identity) && accountFingerprints.get(identity) !== fingerprint) {
|
|
606
|
+
transition(streamStatus.state, true);
|
|
607
|
+
throw Error("Conflicting account version; select and revalidate a fork explicitly");
|
|
608
|
+
}
|
|
581
609
|
if (replayOnReconnect && identity !== undefined && seenUpdates.has(identity)) {
|
|
582
610
|
replayedUpdateCount++;
|
|
583
611
|
rememberParsedUpdate(undefined, slotForReplay);
|
|
@@ -590,8 +618,8 @@ class YellowstoneGrpc {
|
|
|
590
618
|
if (tx) {
|
|
591
619
|
const parseStartedNs = grpcRecvNs === undefined ? undefined : process.hrtime.bigint();
|
|
592
620
|
const slot = txUpdate?.slot ?? 0n;
|
|
593
|
-
const fallbackSlot = YellowstoneGrpc.
|
|
594
|
-
const fallbackTxIndex = YellowstoneGrpc.
|
|
621
|
+
const fallbackSlot = YellowstoneGrpc.toBigInt(slot);
|
|
622
|
+
const fallbackTxIndex = YellowstoneGrpc.toBigInt(tx.index ?? 0);
|
|
595
623
|
const txInfo = {
|
|
596
624
|
signature: tx.signature,
|
|
597
625
|
isVote: tx.isVote,
|
|
@@ -608,9 +636,23 @@ class YellowstoneGrpc {
|
|
|
608
636
|
annotateLocalMetrics(txEvents, grpcRecvNs, parseStartedNs, parseFinishedNs, sourceToGrpcLatencyUs);
|
|
609
637
|
order.pushTransactionEvents(txEvents, fallbackSlot, fallbackTxIndex, emitEvent);
|
|
610
638
|
}
|
|
639
|
+
if (update.blockMeta) {
|
|
640
|
+
const event = (0, block_meta_js_1.parseBlockMetaUpdate)(update.blockMeta, grpcRecvUs, blockTimeUs);
|
|
641
|
+
if (!eventTypeFilter || eventTypeFilter.shouldInclude("BlockMeta"))
|
|
642
|
+
emitEvent(event);
|
|
643
|
+
}
|
|
611
644
|
if (update.account) {
|
|
645
|
+
const rawOnly = !!eventTypeFilter?.include_only?.length &&
|
|
646
|
+
eventTypeFilter.include_only.every(type => type === "AccountRawSnapshot");
|
|
647
|
+
if (eventTypeFilter?.include_only?.includes('AccountRawSnapshot')) {
|
|
648
|
+
const snapshot = this.parseAccountEventRaw(update.account, grpcRecvUs, eventTypeFilter, blockTimeUs, true);
|
|
649
|
+
if (snapshot)
|
|
650
|
+
emitEvent(snapshot);
|
|
651
|
+
}
|
|
612
652
|
const parseStartedNs = grpcRecvNs === undefined ? undefined : process.hrtime.bigint();
|
|
613
|
-
|
|
653
|
+
// Raw-only cache subscriptions do not need a second normalization,
|
|
654
|
+
// base58 conversion and protocol decode for the same account bytes.
|
|
655
|
+
const ev = rawOnly ? null : this.parseAccountEventRaw(update.account, grpcRecvUs, eventTypeFilter, blockTimeUs);
|
|
614
656
|
const parseFinishedNs = grpcRecvNs === undefined ? undefined : process.hrtime.bigint();
|
|
615
657
|
if (ev) {
|
|
616
658
|
annotateLocalMetrics([ev], grpcRecvNs, parseStartedNs, parseFinishedNs, sourceToGrpcLatencyUs);
|
|
@@ -618,7 +660,7 @@ class YellowstoneGrpc {
|
|
|
618
660
|
if (ev)
|
|
619
661
|
emitEvent(ev);
|
|
620
662
|
}
|
|
621
|
-
rememberParsedUpdate(identity, slotForReplay);
|
|
663
|
+
rememberParsedUpdate(identity, slotForReplay, fingerprint);
|
|
622
664
|
};
|
|
623
665
|
const scheduleIngressDrain = () => {
|
|
624
666
|
if (isCancelled || ingressDrainScheduled || ingress.len() === 0)
|
|
@@ -640,6 +682,7 @@ class YellowstoneGrpc {
|
|
|
640
682
|
processUpdate(pending);
|
|
641
683
|
}
|
|
642
684
|
catch (err) {
|
|
685
|
+
transition(streamStatus.state, true);
|
|
643
686
|
reportError(err instanceof Error ? err : new Error(String(err)));
|
|
644
687
|
}
|
|
645
688
|
processed++;
|
|
@@ -677,6 +720,8 @@ class YellowstoneGrpc {
|
|
|
677
720
|
});
|
|
678
721
|
const cancel = () => {
|
|
679
722
|
isCancelled = true;
|
|
723
|
+
transition("stopped", true);
|
|
724
|
+
states.close();
|
|
680
725
|
cancelReconnectDelay?.();
|
|
681
726
|
cancelSubscribeStream(streamHolder.current);
|
|
682
727
|
streamHolder.current = null;
|
|
@@ -699,7 +744,7 @@ class YellowstoneGrpc {
|
|
|
699
744
|
if (!stream)
|
|
700
745
|
return;
|
|
701
746
|
await new Promise((resolve, reject) => {
|
|
702
|
-
stream.write((0, subscribe_builder_js_1.
|
|
747
|
+
stream.write((0, subscribe_builder_js_1.buildSubscribeRequestWithEventFilter)(currentTransactionFilters, currentAccountFilters, eventTypeFilter), (err) => {
|
|
703
748
|
if (err)
|
|
704
749
|
reject(err);
|
|
705
750
|
else
|
|
@@ -708,7 +753,7 @@ class YellowstoneGrpc {
|
|
|
708
753
|
});
|
|
709
754
|
};
|
|
710
755
|
this.dexSubscribers.set(id, { cancel, update });
|
|
711
|
-
(async () => {
|
|
756
|
+
const streamTask = (async () => {
|
|
712
757
|
const autoReconnect = options?.autoReconnect !== false;
|
|
713
758
|
const maxBackoffMs = 60_000;
|
|
714
759
|
let backoffMs = this.config.retry_delay_ms;
|
|
@@ -722,13 +767,14 @@ class YellowstoneGrpc {
|
|
|
722
767
|
attempt++;
|
|
723
768
|
if (attempt > 1)
|
|
724
769
|
reconnectCount++;
|
|
770
|
+
transition(attempt > 1 ? "reconnecting" : "connecting");
|
|
725
771
|
const stream = await this.client.subscribe();
|
|
726
772
|
if (isCancelled) {
|
|
727
773
|
cancelSubscribeStream(stream);
|
|
728
774
|
break;
|
|
729
775
|
}
|
|
730
776
|
streamHolder.current = stream;
|
|
731
|
-
const request = (0, subscribe_builder_js_1.
|
|
777
|
+
const request = (0, subscribe_builder_js_1.buildSubscribeRequestWithEventFilter)(currentTransactionFilters, currentAccountFilters, eventTypeFilter);
|
|
732
778
|
if (attempt > 1 && replayAllowed && lastParsedSlot !== undefined) {
|
|
733
779
|
request.fromSlot = lastParsedSlot.toString();
|
|
734
780
|
attemptedReplay = true;
|
|
@@ -742,6 +788,7 @@ class YellowstoneGrpc {
|
|
|
742
788
|
});
|
|
743
789
|
});
|
|
744
790
|
streamConnected = true;
|
|
791
|
+
transition("connected");
|
|
745
792
|
await new Promise((resolve, reject) => {
|
|
746
793
|
const cleanup = () => {
|
|
747
794
|
streamConnected = false;
|
|
@@ -773,9 +820,10 @@ class YellowstoneGrpc {
|
|
|
773
820
|
grpcRecvUs,
|
|
774
821
|
grpcRecvNs,
|
|
775
822
|
blockTimeUs: createdAtUs,
|
|
776
|
-
sourceToGrpcLatencyUs: createdAtUs === undefined ? undefined : Math.max(0, grpcRecvUs - createdAtUs),
|
|
823
|
+
sourceToGrpcLatencyUs: createdAtUs === undefined ? undefined : Math.max(0, Number((0, metadata_js_1.exactI64)(grpcRecvUs) - createdAtUs)),
|
|
777
824
|
});
|
|
778
825
|
if (!accepted) {
|
|
826
|
+
transition(streamStatus.state, true);
|
|
779
827
|
const now = Date.now();
|
|
780
828
|
if (now >= nextIngressDropWarningAt) {
|
|
781
829
|
nextIngressDropWarningAt = now + 10_000;
|
|
@@ -788,11 +836,15 @@ class YellowstoneGrpc {
|
|
|
788
836
|
});
|
|
789
837
|
stream.on("error", (err) => {
|
|
790
838
|
streamDisconnectCount++;
|
|
839
|
+
if (!isCancelled)
|
|
840
|
+
transition("reconnecting", true);
|
|
791
841
|
cleanup();
|
|
792
842
|
reject(err);
|
|
793
843
|
});
|
|
794
844
|
stream.on("end", () => {
|
|
795
845
|
streamDisconnectCount++;
|
|
846
|
+
if (!isCancelled)
|
|
847
|
+
transition("reconnecting", true);
|
|
796
848
|
cleanup();
|
|
797
849
|
if (!isCancelled)
|
|
798
850
|
reportError(new Error("Yellowstone stream ended; reconnecting"));
|
|
@@ -806,6 +858,7 @@ class YellowstoneGrpc {
|
|
|
806
858
|
cancelSubscribeStream(streamHolder.current);
|
|
807
859
|
streamHolder.current = null;
|
|
808
860
|
streamConnected = false;
|
|
861
|
+
transition("reconnecting", true);
|
|
809
862
|
reportError(err instanceof Error ? err : new Error(String(err)));
|
|
810
863
|
await waitForIngressIdle();
|
|
811
864
|
if (!autoReconnect) {
|
|
@@ -854,12 +907,18 @@ class YellowstoneGrpc {
|
|
|
854
907
|
ingress.close();
|
|
855
908
|
events.close();
|
|
856
909
|
errors.close();
|
|
910
|
+
transition("stopped", true);
|
|
911
|
+
states.close();
|
|
857
912
|
this.dexSubscribers.delete(id);
|
|
858
913
|
}
|
|
859
914
|
})();
|
|
915
|
+
this.activeDex = { cancel, join: () => streamTask };
|
|
860
916
|
return Object.assign(events, {
|
|
861
917
|
id,
|
|
918
|
+
join: () => streamTask,
|
|
862
919
|
errors,
|
|
920
|
+
states,
|
|
921
|
+
status: () => streamStatus,
|
|
863
922
|
dropped: () => eventDropped() + ingress.dropped(),
|
|
864
923
|
eventDropped,
|
|
865
924
|
ingressLen: () => ingress.len(),
|
|
@@ -890,6 +949,9 @@ class YellowstoneGrpc {
|
|
|
890
949
|
}
|
|
891
950
|
/** 动态更新当前 DEX 订阅过滤器(与 Rust update_subscription 语义对齐)。 */
|
|
892
951
|
async updateSubscription(transactionFilters, accountFilters) {
|
|
952
|
+
return this.withDexLifecycle(() => this.updateDexUnlocked(transactionFilters, accountFilters));
|
|
953
|
+
}
|
|
954
|
+
async updateDexUnlocked(transactionFilters, accountFilters) {
|
|
893
955
|
const active = this.dexSubscribers.values().next().value;
|
|
894
956
|
if (!active?.update) {
|
|
895
957
|
throw new Error("No active DEX subscription");
|
|
@@ -904,22 +966,22 @@ class YellowstoneGrpc {
|
|
|
904
966
|
await this.connect();
|
|
905
967
|
const resp = await this.client.getLatestBlockhash(commitment);
|
|
906
968
|
return {
|
|
907
|
-
slot:
|
|
969
|
+
slot: (0, metadata_js_1.exactU64)(resp.slot),
|
|
908
970
|
blockhash: resp.blockhash,
|
|
909
|
-
lastValidBlockHeight:
|
|
971
|
+
lastValidBlockHeight: (0, metadata_js_1.exactU64)(resp.lastValidBlockHeight),
|
|
910
972
|
};
|
|
911
973
|
}
|
|
912
974
|
/** 获取区块高度 */
|
|
913
975
|
async getBlockHeight(commitment) {
|
|
914
976
|
await this.connect();
|
|
915
977
|
const resp = await this.client.getBlockHeight(commitment);
|
|
916
|
-
return
|
|
978
|
+
return (0, metadata_js_1.exactU64)(resp.blockHeight);
|
|
917
979
|
}
|
|
918
980
|
/** 获取当前 Slot */
|
|
919
981
|
async getSlot(commitment) {
|
|
920
982
|
await this.connect();
|
|
921
983
|
const resp = await this.client.getSlot(commitment);
|
|
922
|
-
return
|
|
984
|
+
return (0, metadata_js_1.exactU64)(resp.slot);
|
|
923
985
|
}
|
|
924
986
|
/** 获取服务器版本 */
|
|
925
987
|
async getVersion() {
|
|
@@ -932,7 +994,7 @@ class YellowstoneGrpc {
|
|
|
932
994
|
await this.connect();
|
|
933
995
|
const resp = await this.client.isBlockhashValid(blockhash, commitment);
|
|
934
996
|
return {
|
|
935
|
-
slot:
|
|
997
|
+
slot: (0, metadata_js_1.exactU64)(resp.slot),
|
|
936
998
|
valid: resp.valid,
|
|
937
999
|
};
|
|
938
1000
|
}
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* 与 Rust `sol-parser-sdk/src/grpc/geyser_connect.rs` 对齐:Yellowstone Geyser gRPC 连接选项。
|
|
3
3
|
*
|
|
4
|
-
* `@
|
|
5
|
-
* `connectTimeoutMs`
|
|
4
|
+
* 纯 JavaScript `@grpc/grpc-js` 传输,不加载 Rust N-API;
|
|
5
|
+
* `connectTimeoutMs` 用于 waitForReady 与 unary deadline。
|
|
6
6
|
*/
|
|
7
|
-
import Client, { type ChannelOptions } from "
|
|
7
|
+
import Client, { type NativeChannelOptions as ChannelOptions } from "./native_client.js";
|
|
8
8
|
/** 与 Rust `GeyserConnectConfig` 字段对应(毫秒 / 字节) */
|
|
9
9
|
export interface GeyserConnectConfig {
|
|
10
10
|
/** 对应 Rust `connect_timeout`(毫秒) */
|
|
@@ -9,10 +9,10 @@ exports.connectYellowstoneGeyser = connectYellowstoneGeyser;
|
|
|
9
9
|
/**
|
|
10
10
|
* 与 Rust `sol-parser-sdk/src/grpc/geyser_connect.rs` 对齐:Yellowstone Geyser gRPC 连接选项。
|
|
11
11
|
*
|
|
12
|
-
* `@
|
|
13
|
-
* `connectTimeoutMs`
|
|
12
|
+
* 纯 JavaScript `@grpc/grpc-js` 传输,不加载 Rust N-API;
|
|
13
|
+
* `connectTimeoutMs` 用于 waitForReady 与 unary deadline。
|
|
14
14
|
*/
|
|
15
|
-
const
|
|
15
|
+
const native_client_js_1 = __importDefault(require("./native_client.js"));
|
|
16
16
|
/** 公共字段与 Rust `GeyserConnectConfig::default` 一致,并包含 Node gRPC 通道默认值。 */
|
|
17
17
|
function defaultGeyserConnectConfig() {
|
|
18
18
|
return {
|
|
@@ -32,17 +32,15 @@ function geyserGrpcChannelOptions(config = defaultGeyserConnectConfig()) {
|
|
|
32
32
|
const interval = config.keepAliveIntervalMs ?? 30_000;
|
|
33
33
|
const timeout = config.keepAliveTimeoutMs ?? 5000;
|
|
34
34
|
return {
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
grpcKeepAliveWhileIdle: true,
|
|
45
|
-
grpcTcpNodelay: true,
|
|
35
|
+
connectTimeoutMs: config.connectTimeoutMs,
|
|
36
|
+
"grpc.max_receive_message_length": max,
|
|
37
|
+
"grpc.max_send_message_length": max,
|
|
38
|
+
"grpc.keepalive_time_ms": interval,
|
|
39
|
+
"grpc.keepalive_timeout_ms": timeout,
|
|
40
|
+
"grpc.keepalive_permit_without_calls": 1,
|
|
41
|
+
"grpc.initial_reconnect_backoff_ms": config.initialReconnectBackoffMs ?? 1000,
|
|
42
|
+
"grpc.max_reconnect_backoff_ms": config.maxReconnectBackoffMs ?? 60000,
|
|
43
|
+
"grpc-node.flow_control_window": config.flowControlWindowBytes ?? 1024 * 1024,
|
|
46
44
|
};
|
|
47
45
|
}
|
|
48
46
|
/**
|
|
@@ -50,7 +48,7 @@ function geyserGrpcChannelOptions(config = defaultGeyserConnectConfig()) {
|
|
|
50
48
|
* Rust 为 async;此处构造同步完成,返回 `Promise` 以保持 `await connectYellowstoneGeyser(...)` 写法。
|
|
51
49
|
*/
|
|
52
50
|
async function connectYellowstoneGeyser(endpoint, config = defaultGeyserConnectConfig()) {
|
|
53
|
-
const client = new
|
|
51
|
+
const client = new native_client_js_1.default(endpoint, config.xToken, geyserGrpcChannelOptions(config));
|
|
54
52
|
await client.connect();
|
|
55
53
|
return client;
|
|
56
54
|
}
|
|
@@ -168,6 +168,8 @@ function mergePumpfunTrade(log, ix) {
|
|
|
168
168
|
"quote_amount",
|
|
169
169
|
"virtual_quote_reserves",
|
|
170
170
|
"real_quote_reserves",
|
|
171
|
+
"holder_rewards_bps",
|
|
172
|
+
"holder_rewards",
|
|
171
173
|
]) {
|
|
172
174
|
fillNonZero(log, key, ix);
|
|
173
175
|
}
|
|
@@ -197,6 +199,7 @@ function mergePumpfunCreate(log, ix) {
|
|
|
197
199
|
"real_token_reserves",
|
|
198
200
|
"token_total_supply",
|
|
199
201
|
"virtual_quote_reserves",
|
|
202
|
+
"creator_fee_bps",
|
|
200
203
|
]) {
|
|
201
204
|
fillNonZero(log, key, ix);
|
|
202
205
|
}
|
|
@@ -204,6 +207,7 @@ function mergePumpfunCreate(log, ix) {
|
|
|
204
207
|
log.is_mayhem_mode = Boolean(log.is_mayhem_mode) || Boolean(ix.is_mayhem_mode);
|
|
205
208
|
log.is_cashback_enabled =
|
|
206
209
|
Boolean(log.is_cashback_enabled) || Boolean(ix.is_cashback_enabled);
|
|
210
|
+
log.is_holder_reward = Boolean(log.is_holder_reward) || Boolean(ix.is_holder_reward);
|
|
207
211
|
}
|
|
208
212
|
function mergePumpfunCreateV2(log, ix) {
|
|
209
213
|
mergePumpfunCreate(log, ix);
|
|
@@ -224,6 +228,8 @@ function mergePumpfunCreateV2(log, ix) {
|
|
|
224
228
|
]) {
|
|
225
229
|
fillString(log, key, ix);
|
|
226
230
|
}
|
|
231
|
+
fillNonZero(log, "creator_fee_bps", ix);
|
|
232
|
+
log.is_holder_reward = Boolean(log.is_holder_reward) || Boolean(ix.is_holder_reward);
|
|
227
233
|
}
|
|
228
234
|
function mergePumpfunMigrate(log, ix) {
|
|
229
235
|
for (const key of ["bonding_curve", "pool", "user"]) {
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/** Pure JavaScript Yellowstone transport. Exact protobuf integers are decimal strings. */
|
|
2
|
+
import * as grpc from "@grpc/grpc-js";
|
|
3
|
+
import type { CommitmentLevel, SubscribeRequest, SubscribeUpdate } from "./protocol/geyser.js";
|
|
4
|
+
export interface NativeChannelOptions extends grpc.ChannelOptions {
|
|
5
|
+
connectTimeoutMs?: number;
|
|
6
|
+
}
|
|
7
|
+
export declare const geyserService: grpc.ServiceDefinition<grpc.UntypedServiceImplementation>;
|
|
8
|
+
export default class NativeYellowstoneClient {
|
|
9
|
+
private readonly options;
|
|
10
|
+
private raw?;
|
|
11
|
+
private readonly address;
|
|
12
|
+
private readonly credentials;
|
|
13
|
+
private readonly metadata;
|
|
14
|
+
constructor(endpoint: string, token?: string, options?: NativeChannelOptions);
|
|
15
|
+
private client;
|
|
16
|
+
connect(): Promise<void>;
|
|
17
|
+
close(): void;
|
|
18
|
+
subscribe(): Promise<grpc.ClientDuplexStream<SubscribeRequest, SubscribeUpdate>>;
|
|
19
|
+
private unary;
|
|
20
|
+
getLatestBlockhash(commitment?: CommitmentLevel): Promise<any>;
|
|
21
|
+
getBlockHeight(commitment?: CommitmentLevel): Promise<any>;
|
|
22
|
+
getSlot(commitment?: CommitmentLevel): Promise<any>;
|
|
23
|
+
getVersion(): Promise<any>;
|
|
24
|
+
isBlockhashValid(blockhash: string, commitment?: CommitmentLevel): Promise<any>;
|
|
25
|
+
ping(count: number): Promise<number>;
|
|
26
|
+
}
|