sol-parser-sdk-nodejs 0.3.0 → 0.4.4
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/.env.example +24 -0
- package/README.md +100 -352
- package/README_CN.md +107 -253
- package/dist/accounts/mod.d.ts +3 -0
- package/dist/accounts/mod.js +18 -1
- package/dist/accounts/pumpfun.d.ts +6 -0
- package/dist/accounts/pumpfun.js +157 -0
- package/dist/accounts/rpc_wallet.d.ts +5 -0
- package/dist/accounts/rpc_wallet.js +18 -0
- package/dist/accounts/rust_aliases.d.ts +10 -0
- package/dist/accounts/rust_aliases.js +21 -0
- package/dist/accounts/wallet_resolve.d.ts +1 -0
- package/dist/accounts/wallet_resolve.js +28 -0
- package/dist/common/constants.d.ts +10 -0
- package/dist/common/constants.js +13 -0
- package/dist/core/account_dispatcher_rpc.js +26 -5
- package/dist/core/account_fill_meteora.d.ts +4 -2
- package/dist/core/account_fill_meteora.js +5 -2
- package/dist/core/account_fill_pumpfun.js +17 -0
- package/dist/core/account_pubkey_cache.d.ts +12 -0
- package/dist/core/account_pubkey_cache.js +26 -0
- package/dist/core/clock.d.ts +6 -0
- package/dist/core/clock.js +13 -0
- package/dist/core/dex_event.d.ts +172 -44
- package/dist/core/metadata.d.ts +1 -0
- package/dist/core/pumpfun_fee_enrich.d.ts +4 -0
- package/dist/core/pumpfun_fee_enrich.js +80 -0
- package/dist/core/unified_parser.d.ts +2 -2
- package/dist/core/unified_parser.js +8 -6
- package/dist/grpc/client.d.ts +32 -1
- package/dist/grpc/client.js +400 -64
- package/dist/grpc/event_parser.d.ts +6 -0
- package/dist/grpc/event_parser.js +15 -0
- package/dist/grpc/geyser_connect.d.ts +30 -0
- package/dist/grpc/geyser_connect.js +40 -0
- package/dist/grpc/log_instr_dedup.d.ts +2 -0
- package/dist/grpc/log_instr_dedup.js +330 -0
- package/dist/grpc/order_buffer.d.ts +27 -0
- package/dist/grpc/order_buffer.js +166 -0
- package/dist/grpc/program_ids.d.ts +26 -0
- package/dist/grpc/program_ids.js +55 -0
- package/dist/grpc/rpc_to_grpc.d.ts +18 -0
- package/dist/grpc/rpc_to_grpc.js +127 -0
- package/dist/grpc/subscribe_builder.d.ts +13 -0
- package/dist/grpc/subscribe_builder.js +66 -0
- package/dist/grpc/transaction_meta.d.ts +29 -0
- package/dist/grpc/transaction_meta.js +208 -0
- package/dist/grpc/types.d.ts +57 -2
- package/dist/grpc/types.js +199 -7
- package/dist/grpc/yellowstone_parse.d.ts +8 -1
- package/dist/grpc/yellowstone_parse.js +27 -11
- package/dist/index.d.ts +39 -8
- package/dist/index.js +184 -2
- package/dist/instr/bonk_ix.d.ts +4 -1
- package/dist/instr/bonk_ix.js +106 -27
- package/dist/instr/meteora_damm_ix.d.ts +4 -2
- package/dist/instr/meteora_damm_ix.js +248 -13
- package/dist/instr/mod.d.ts +1 -0
- package/dist/instr/mod.js +16 -3
- package/dist/instr/orca_whirlpool_ix.d.ts +4 -1
- package/dist/instr/orca_whirlpool_ix.js +45 -16
- package/dist/instr/program_ids.d.ts +7 -13
- package/dist/instr/program_ids.js +20 -15
- package/dist/instr/pump_fees_ix.d.ts +2 -0
- package/dist/instr/pump_fees_ix.js +166 -0
- package/dist/instr/pumpfun_ix.js +57 -0
- package/dist/instr/pumpswap_ix.d.ts +1 -1
- package/dist/instr/pumpswap_ix.js +78 -57
- package/dist/instr/raydium_amm_v4_ix.d.ts +1 -1
- package/dist/instr/raydium_amm_v4_ix.js +94 -28
- package/dist/instr/raydium_clmm_ix.d.ts +1 -1
- package/dist/instr/raydium_clmm_ix.js +84 -30
- package/dist/instr/raydium_cpmm_ix.d.ts +1 -1
- package/dist/instr/raydium_cpmm_ix.js +46 -12
- package/dist/instr/rust_aliases.d.ts +8 -0
- package/dist/instr/rust_aliases.js +16 -0
- package/dist/instr/utils.d.ts +1 -1
- package/dist/instr/utils.js +2 -1
- package/dist/logs/discriminator_lut.d.ts +19 -0
- package/dist/logs/discriminator_lut.js +62 -0
- package/dist/logs/meteora_damm.d.ts +3 -4
- package/dist/logs/meteora_damm.js +3 -369
- package/dist/logs/optimized_matcher.d.ts +2 -2
- package/dist/logs/optimized_matcher.js +123 -23
- package/dist/logs/program_log_discriminators.d.ts +10 -0
- package/dist/logs/program_log_discriminators.js +10 -0
- package/dist/logs/pump.d.ts +2 -0
- package/dist/logs/pump.js +51 -4
- package/dist/logs/pump_fees.d.ts +23 -0
- package/dist/logs/pump_fees.js +364 -0
- package/dist/logs/rust_aliases.d.ts +6 -0
- package/dist/logs/rust_aliases.js +13 -0
- package/dist/rpc_parser.d.ts +1 -0
- package/dist/rpc_parser.js +4 -1
- package/dist/rpc_transaction.d.ts +2 -0
- package/dist/rpc_transaction.js +14 -6
- package/dist/shredstream/alt_lookup.d.ts +9 -0
- package/dist/shredstream/alt_lookup.js +70 -0
- package/dist/shredstream/client.d.ts +62 -0
- package/dist/shredstream/client.js +399 -0
- package/dist/shredstream/config.d.ts +30 -0
- package/dist/shredstream/config.js +34 -0
- package/dist/shredstream/entries_decode.d.ts +28 -0
- package/dist/shredstream/entries_decode.js +251 -0
- package/dist/shredstream/index.d.ts +17 -0
- package/dist/shredstream/index.js +33 -0
- package/dist/shredstream/instruction_parse.d.ts +34 -0
- package/dist/shredstream/instruction_parse.js +49 -0
- package/dist/shredstream/proto_types.d.ts +9 -0
- package/dist/shredstream/proto_types.js +2 -0
- package/dist/shredstream/shredstream.proto +15 -0
- package/dist/shredstream/wire_to_shred_tx.d.ts +2 -0
- package/dist/shredstream/wire_to_shred_tx.js +59 -0
- package/package.json +28 -11
|
@@ -0,0 +1,251 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.bincodeVecEntryCount = bincodeVecEntryCount;
|
|
4
|
+
exports.decodeCompactU16 = decodeCompactU16;
|
|
5
|
+
exports.parseTransaction = parseTransaction;
|
|
6
|
+
exports.decodeEntriesBincodeFlat = decodeEntriesBincodeFlat;
|
|
7
|
+
exports.decodeEntriesBincodeNested = decodeEntriesBincodeNested;
|
|
8
|
+
exports.decodeShredstreamEntriesBincode = decodeShredstreamEntriesBincode;
|
|
9
|
+
const wire_to_shred_tx_js_1 = require("./wire_to_shred_tx.js");
|
|
10
|
+
const MAX_VEC_ENTRIES = 100_000;
|
|
11
|
+
function readU64LE(buf, offset) {
|
|
12
|
+
const v = new DataView(buf.buffer, buf.byteOffset + offset, 8);
|
|
13
|
+
return v.getBigUint64(0, true);
|
|
14
|
+
}
|
|
15
|
+
/** 与 Go `BincodeVecEntryCount` 一致 */
|
|
16
|
+
function bincodeVecEntryCount(entriesBytes) {
|
|
17
|
+
if (entriesBytes.length < 8) {
|
|
18
|
+
throw new Error("shredstream: entries payload too short for vec length");
|
|
19
|
+
}
|
|
20
|
+
const n = readU64LE(entriesBytes, 0);
|
|
21
|
+
if (n > BigInt(MAX_VEC_ENTRIES)) {
|
|
22
|
+
throw new Error(`shredstream: corrupt entry_count ${n} exceeds limit`);
|
|
23
|
+
}
|
|
24
|
+
return n;
|
|
25
|
+
}
|
|
26
|
+
/** compact-u16,与 Go `decodeCompactU16` 一致 */
|
|
27
|
+
function decodeCompactU16(buf, pos) {
|
|
28
|
+
if (pos >= buf.length)
|
|
29
|
+
return null;
|
|
30
|
+
const b0 = buf[pos];
|
|
31
|
+
if (b0 < 0x80)
|
|
32
|
+
return { value: b0, bytes: 1 };
|
|
33
|
+
if (pos + 1 >= buf.length)
|
|
34
|
+
return null;
|
|
35
|
+
const b1 = buf[pos + 1];
|
|
36
|
+
if (b1 < 0x80) {
|
|
37
|
+
return { value: (b0 & 0x7f) | (b1 << 7), bytes: 2 };
|
|
38
|
+
}
|
|
39
|
+
if (pos + 2 >= buf.length)
|
|
40
|
+
return null;
|
|
41
|
+
const b2 = buf[pos + 2];
|
|
42
|
+
return { value: (b0 & 0x7f) | ((b1 & 0x7f) << 7) | (b2 << 14), bytes: 3 };
|
|
43
|
+
}
|
|
44
|
+
/** 返回 (txWireLength, signatures);失败返回 null */
|
|
45
|
+
function parseTransaction(buf, pos) {
|
|
46
|
+
const start = pos;
|
|
47
|
+
if (pos >= buf.length)
|
|
48
|
+
return null;
|
|
49
|
+
const sigCountEnc = decodeCompactU16(buf, pos);
|
|
50
|
+
if (!sigCountEnc)
|
|
51
|
+
return null;
|
|
52
|
+
let p = pos + sigCountEnc.bytes;
|
|
53
|
+
const sigCount = sigCountEnc.value;
|
|
54
|
+
const sigsEnd = p + sigCount * 64;
|
|
55
|
+
if (sigsEnd > buf.length)
|
|
56
|
+
return null;
|
|
57
|
+
const sigs = [];
|
|
58
|
+
for (let i = 0; i < sigCount; i++) {
|
|
59
|
+
sigs.push(buf.subarray(p, p + 64));
|
|
60
|
+
p += 64;
|
|
61
|
+
}
|
|
62
|
+
if (p >= buf.length)
|
|
63
|
+
return null;
|
|
64
|
+
const msgFirst = buf[p];
|
|
65
|
+
const isV0 = msgFirst >= 0x80;
|
|
66
|
+
if (isV0)
|
|
67
|
+
p += 1;
|
|
68
|
+
p += 3;
|
|
69
|
+
if (p > buf.length)
|
|
70
|
+
return null;
|
|
71
|
+
const acctEnc = decodeCompactU16(buf, p);
|
|
72
|
+
if (!acctEnc)
|
|
73
|
+
return null;
|
|
74
|
+
p += acctEnc.bytes;
|
|
75
|
+
p += acctEnc.value * 32;
|
|
76
|
+
if (p > buf.length)
|
|
77
|
+
return null;
|
|
78
|
+
p += 32;
|
|
79
|
+
if (p > buf.length)
|
|
80
|
+
return null;
|
|
81
|
+
const ixCountEnc = decodeCompactU16(buf, p);
|
|
82
|
+
if (!ixCountEnc)
|
|
83
|
+
return null;
|
|
84
|
+
p += ixCountEnc.bytes;
|
|
85
|
+
const ixCount = ixCountEnc.value;
|
|
86
|
+
for (let ix = 0; ix < ixCount; ix++) {
|
|
87
|
+
p += 1;
|
|
88
|
+
if (p > buf.length)
|
|
89
|
+
return null;
|
|
90
|
+
const acctLenEnc = decodeCompactU16(buf, p);
|
|
91
|
+
if (!acctLenEnc)
|
|
92
|
+
return null;
|
|
93
|
+
p += acctLenEnc.bytes;
|
|
94
|
+
p += acctLenEnc.value;
|
|
95
|
+
if (p > buf.length)
|
|
96
|
+
return null;
|
|
97
|
+
const dataLenEnc = decodeCompactU16(buf, p);
|
|
98
|
+
if (!dataLenEnc)
|
|
99
|
+
return null;
|
|
100
|
+
p += dataLenEnc.bytes;
|
|
101
|
+
p += dataLenEnc.value;
|
|
102
|
+
if (p > buf.length)
|
|
103
|
+
return null;
|
|
104
|
+
}
|
|
105
|
+
if (isV0) {
|
|
106
|
+
if (p >= buf.length)
|
|
107
|
+
return null;
|
|
108
|
+
const atlCountEnc = decodeCompactU16(buf, p);
|
|
109
|
+
if (!atlCountEnc)
|
|
110
|
+
return null;
|
|
111
|
+
p += atlCountEnc.bytes;
|
|
112
|
+
const atlCount = atlCountEnc.value;
|
|
113
|
+
for (let atl = 0; atl < atlCount; atl++) {
|
|
114
|
+
p += 32;
|
|
115
|
+
if (p > buf.length)
|
|
116
|
+
return null;
|
|
117
|
+
const wLenEnc = decodeCompactU16(buf, p);
|
|
118
|
+
if (!wLenEnc)
|
|
119
|
+
return null;
|
|
120
|
+
p += wLenEnc.bytes;
|
|
121
|
+
p += wLenEnc.value;
|
|
122
|
+
if (p > buf.length)
|
|
123
|
+
return null;
|
|
124
|
+
const rLenEnc = decodeCompactU16(buf, p);
|
|
125
|
+
if (!rLenEnc)
|
|
126
|
+
return null;
|
|
127
|
+
p += rLenEnc.bytes;
|
|
128
|
+
p += rLenEnc.value;
|
|
129
|
+
if (p > buf.length)
|
|
130
|
+
return null;
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
return { txLen: p - start, sigs };
|
|
134
|
+
}
|
|
135
|
+
class BatchDecoder {
|
|
136
|
+
buffer = new Uint8Array(0);
|
|
137
|
+
expectedEntryCount = 0;
|
|
138
|
+
entriesYielded = 0;
|
|
139
|
+
cursor = 0;
|
|
140
|
+
append(payload) {
|
|
141
|
+
const merged = new Uint8Array(this.buffer.length + payload.length);
|
|
142
|
+
merged.set(this.buffer);
|
|
143
|
+
merged.set(payload, this.buffer.length);
|
|
144
|
+
this.buffer = merged;
|
|
145
|
+
}
|
|
146
|
+
tryDecodeEntry() {
|
|
147
|
+
const pos = this.cursor;
|
|
148
|
+
const buf = this.buffer;
|
|
149
|
+
if (pos + 48 > buf.length)
|
|
150
|
+
return null;
|
|
151
|
+
let p = pos + 8 + 32;
|
|
152
|
+
const txCount = Number(readU64LE(buf, p));
|
|
153
|
+
p += 8;
|
|
154
|
+
const txs = [];
|
|
155
|
+
for (let i = 0; i < txCount; i++) {
|
|
156
|
+
const txStart = p;
|
|
157
|
+
const parsed = parseTransaction(buf, p);
|
|
158
|
+
if (!parsed) {
|
|
159
|
+
throw new Error(`shredstream: truncated transaction ${i} in entry`);
|
|
160
|
+
}
|
|
161
|
+
const { txLen, sigs } = parsed;
|
|
162
|
+
const raw = buf.subarray(txStart, txStart + txLen);
|
|
163
|
+
txs.push({
|
|
164
|
+
raw: Uint8Array.from(raw),
|
|
165
|
+
signatures: sigs.map((s) => Uint8Array.from(s)),
|
|
166
|
+
});
|
|
167
|
+
p = txStart + txLen;
|
|
168
|
+
}
|
|
169
|
+
this.cursor = p;
|
|
170
|
+
return txs;
|
|
171
|
+
}
|
|
172
|
+
/** 扁平列表,对齐 Go `DecodeEntriesBincode` */
|
|
173
|
+
pushFlat(payload) {
|
|
174
|
+
this.append(payload);
|
|
175
|
+
if (this.expectedEntryCount === 0 && this.entriesYielded === 0 && this.cursor === 0) {
|
|
176
|
+
if (this.buffer.length < 8) {
|
|
177
|
+
throw new Error("shredstream: entries payload too short for vec length");
|
|
178
|
+
}
|
|
179
|
+
const count = readU64LE(this.buffer, 0);
|
|
180
|
+
if (count > BigInt(MAX_VEC_ENTRIES)) {
|
|
181
|
+
throw new Error(`shredstream: corrupt entry_count ${count} exceeds limit`);
|
|
182
|
+
}
|
|
183
|
+
this.expectedEntryCount = Number(count);
|
|
184
|
+
this.cursor = 8;
|
|
185
|
+
}
|
|
186
|
+
const out = [];
|
|
187
|
+
while (this.entriesYielded < this.expectedEntryCount) {
|
|
188
|
+
const entryTxs = this.tryDecodeEntry();
|
|
189
|
+
if (entryTxs === null) {
|
|
190
|
+
throw new Error(`shredstream: incomplete entry at offset ${this.cursor}`);
|
|
191
|
+
}
|
|
192
|
+
out.push(...entryTxs);
|
|
193
|
+
this.entriesYielded++;
|
|
194
|
+
}
|
|
195
|
+
return out;
|
|
196
|
+
}
|
|
197
|
+
/** 按 Solana `Entry` 分层,供 ShredStream 客户端(Entry 内 tx_index) */
|
|
198
|
+
pushNested(payload) {
|
|
199
|
+
this.append(payload);
|
|
200
|
+
if (this.expectedEntryCount === 0 && this.entriesYielded === 0 && this.cursor === 0) {
|
|
201
|
+
if (this.buffer.length < 8) {
|
|
202
|
+
throw new Error("shredstream: entries payload too short for vec length");
|
|
203
|
+
}
|
|
204
|
+
const count = readU64LE(this.buffer, 0);
|
|
205
|
+
if (count > BigInt(MAX_VEC_ENTRIES)) {
|
|
206
|
+
throw new Error(`shredstream: corrupt entry_count ${count} exceeds limit`);
|
|
207
|
+
}
|
|
208
|
+
this.expectedEntryCount = Number(count);
|
|
209
|
+
this.cursor = 8;
|
|
210
|
+
}
|
|
211
|
+
const nested = [];
|
|
212
|
+
while (this.entriesYielded < this.expectedEntryCount) {
|
|
213
|
+
const entryTxs = this.tryDecodeEntry();
|
|
214
|
+
if (entryTxs === null) {
|
|
215
|
+
throw new Error(`shredstream: incomplete entry at offset ${this.cursor}`);
|
|
216
|
+
}
|
|
217
|
+
nested.push(entryTxs);
|
|
218
|
+
this.entriesYielded++;
|
|
219
|
+
}
|
|
220
|
+
return nested;
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
function decodeEntriesBincodeFlat(entriesBytes) {
|
|
224
|
+
if (entriesBytes.length === 0)
|
|
225
|
+
return [];
|
|
226
|
+
const dec = new BatchDecoder();
|
|
227
|
+
return dec.pushFlat(entriesBytes);
|
|
228
|
+
}
|
|
229
|
+
function decodeEntriesBincodeNested(entriesBytes) {
|
|
230
|
+
if (entriesBytes.length === 0)
|
|
231
|
+
return [];
|
|
232
|
+
const dec = new BatchDecoder();
|
|
233
|
+
return dec.pushNested(entriesBytes);
|
|
234
|
+
}
|
|
235
|
+
/**
|
|
236
|
+
* 解码 gRPC `entries` 字节并展开为与旧 WASM 相同的嵌套 `ShredWasmTx`(跳过无法反序列化的单笔交易)。
|
|
237
|
+
*/
|
|
238
|
+
function decodeShredstreamEntriesBincode(bytes) {
|
|
239
|
+
const nested = decodeEntriesBincodeNested(bytes);
|
|
240
|
+
const out = [];
|
|
241
|
+
for (const entryTxs of nested) {
|
|
242
|
+
const row = [];
|
|
243
|
+
for (const w of entryTxs) {
|
|
244
|
+
const tx = (0, wire_to_shred_tx_js_1.wireBytesToShredWasmTx)(w.raw);
|
|
245
|
+
if (tx)
|
|
246
|
+
row.push(tx);
|
|
247
|
+
}
|
|
248
|
+
out.push(row);
|
|
249
|
+
}
|
|
250
|
+
return out;
|
|
251
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Jito ShredStream 超低延迟订阅(与 Rust `sol-parser-sdk::shredstream` 对齐)。
|
|
3
|
+
*
|
|
4
|
+
* 限制(与 Rust `shredstream/mod.rs` 文档一致):
|
|
5
|
+
* - 仅静态账户键;使用 ALT 的交易账户列表不完整
|
|
6
|
+
* - 无 inner instructions,无法 CPI 解析
|
|
7
|
+
* - 无 block_time
|
|
8
|
+
* - 无交易日志(program logs);客户端对每条交易走 **外层 `parseInstructionUnified`**(与 gRPC 指令解析同源),可产出 `DexEvent`(V0+ALT 若账户下标超出静态表则跳过该指令)
|
|
9
|
+
* - `metadata.tx_index` 为**单条 gRPC `Entry` 消息内**跨所有 Solana `Entry` 分组的连续下标(与 golang `shredstream_entries` 扁平 `ti` 对齐),非 slot 级全局序号
|
|
10
|
+
*/
|
|
11
|
+
export { type ShredStreamConfig, defaultShredStreamConfig, lowLatencyShredStreamConfig, highThroughputShredStreamConfig, } from "./config.js";
|
|
12
|
+
export { ShredStreamClient, ShredEventQueue, type ShredStreamReceiveStats, } from "./client.js";
|
|
13
|
+
export { dexEventsFromShredWasmTx, dexEventsFromShredWasmTxWithFullKeys, type ShredWasmTx, type ShredWasmCompiledIx, } from "./instruction_parse.js";
|
|
14
|
+
export { fullAccountKeyStringsFromShredTx, loadAddressLookupTableAccounts } from "./alt_lookup.js";
|
|
15
|
+
export type { SubscribeEntriesRequest, ShredstreamEntryMessage } from "./proto_types.js";
|
|
16
|
+
export { bincodeVecEntryCount, decodeEntriesBincodeFlat, decodeEntriesBincodeNested, decodeShredstreamEntriesBincode, type DecodedWireTransaction, } from "./entries_decode.js";
|
|
17
|
+
export { wireBytesToShredWasmTx } from "./wire_to_shred_tx.js";
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.wireBytesToShredWasmTx = exports.decodeShredstreamEntriesBincode = exports.decodeEntriesBincodeNested = exports.decodeEntriesBincodeFlat = exports.bincodeVecEntryCount = exports.loadAddressLookupTableAccounts = exports.fullAccountKeyStringsFromShredTx = exports.dexEventsFromShredWasmTxWithFullKeys = exports.dexEventsFromShredWasmTx = exports.ShredEventQueue = exports.ShredStreamClient = exports.highThroughputShredStreamConfig = exports.lowLatencyShredStreamConfig = exports.defaultShredStreamConfig = void 0;
|
|
4
|
+
/**
|
|
5
|
+
* Jito ShredStream 超低延迟订阅(与 Rust `sol-parser-sdk::shredstream` 对齐)。
|
|
6
|
+
*
|
|
7
|
+
* 限制(与 Rust `shredstream/mod.rs` 文档一致):
|
|
8
|
+
* - 仅静态账户键;使用 ALT 的交易账户列表不完整
|
|
9
|
+
* - 无 inner instructions,无法 CPI 解析
|
|
10
|
+
* - 无 block_time
|
|
11
|
+
* - 无交易日志(program logs);客户端对每条交易走 **外层 `parseInstructionUnified`**(与 gRPC 指令解析同源),可产出 `DexEvent`(V0+ALT 若账户下标超出静态表则跳过该指令)
|
|
12
|
+
* - `metadata.tx_index` 为**单条 gRPC `Entry` 消息内**跨所有 Solana `Entry` 分组的连续下标(与 golang `shredstream_entries` 扁平 `ti` 对齐),非 slot 级全局序号
|
|
13
|
+
*/
|
|
14
|
+
var config_js_1 = require("./config.js");
|
|
15
|
+
Object.defineProperty(exports, "defaultShredStreamConfig", { enumerable: true, get: function () { return config_js_1.defaultShredStreamConfig; } });
|
|
16
|
+
Object.defineProperty(exports, "lowLatencyShredStreamConfig", { enumerable: true, get: function () { return config_js_1.lowLatencyShredStreamConfig; } });
|
|
17
|
+
Object.defineProperty(exports, "highThroughputShredStreamConfig", { enumerable: true, get: function () { return config_js_1.highThroughputShredStreamConfig; } });
|
|
18
|
+
var client_js_1 = require("./client.js");
|
|
19
|
+
Object.defineProperty(exports, "ShredStreamClient", { enumerable: true, get: function () { return client_js_1.ShredStreamClient; } });
|
|
20
|
+
Object.defineProperty(exports, "ShredEventQueue", { enumerable: true, get: function () { return client_js_1.ShredEventQueue; } });
|
|
21
|
+
var instruction_parse_js_1 = require("./instruction_parse.js");
|
|
22
|
+
Object.defineProperty(exports, "dexEventsFromShredWasmTx", { enumerable: true, get: function () { return instruction_parse_js_1.dexEventsFromShredWasmTx; } });
|
|
23
|
+
Object.defineProperty(exports, "dexEventsFromShredWasmTxWithFullKeys", { enumerable: true, get: function () { return instruction_parse_js_1.dexEventsFromShredWasmTxWithFullKeys; } });
|
|
24
|
+
var alt_lookup_js_1 = require("./alt_lookup.js");
|
|
25
|
+
Object.defineProperty(exports, "fullAccountKeyStringsFromShredTx", { enumerable: true, get: function () { return alt_lookup_js_1.fullAccountKeyStringsFromShredTx; } });
|
|
26
|
+
Object.defineProperty(exports, "loadAddressLookupTableAccounts", { enumerable: true, get: function () { return alt_lookup_js_1.loadAddressLookupTableAccounts; } });
|
|
27
|
+
var entries_decode_js_1 = require("./entries_decode.js");
|
|
28
|
+
Object.defineProperty(exports, "bincodeVecEntryCount", { enumerable: true, get: function () { return entries_decode_js_1.bincodeVecEntryCount; } });
|
|
29
|
+
Object.defineProperty(exports, "decodeEntriesBincodeFlat", { enumerable: true, get: function () { return entries_decode_js_1.decodeEntriesBincodeFlat; } });
|
|
30
|
+
Object.defineProperty(exports, "decodeEntriesBincodeNested", { enumerable: true, get: function () { return entries_decode_js_1.decodeEntriesBincodeNested; } });
|
|
31
|
+
Object.defineProperty(exports, "decodeShredstreamEntriesBincode", { enumerable: true, get: function () { return entries_decode_js_1.decodeShredstreamEntriesBincode; } });
|
|
32
|
+
var wire_to_shred_tx_js_1 = require("./wire_to_shred_tx.js");
|
|
33
|
+
Object.defineProperty(exports, "wireBytesToShredWasmTx", { enumerable: true, get: function () { return wire_to_shred_tx_js_1.wireBytesToShredWasmTx; } });
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ShredStream:外层编译指令 → `parseInstructionUnified`(与 gRPC 指令解析同源)。
|
|
3
|
+
* `ShredWasmTx` 来自线格式交易经 `@solana/web3.js` 反序列化(见 `wire_to_shred_tx.ts`),非 WASM。
|
|
4
|
+
* 完整账户列表需含 V0 ALT 展开,见 `alt_lookup.ts` + `ShredStreamConfig.connection`。
|
|
5
|
+
*/
|
|
6
|
+
import type { MessageHeader } from "@solana/web3.js";
|
|
7
|
+
import type { DexEvent } from "../core/dex_event.js";
|
|
8
|
+
import type { EventTypeFilter } from "../grpc/types.js";
|
|
9
|
+
export type ShredWasmCompiledIx = {
|
|
10
|
+
programIdIndex: number;
|
|
11
|
+
accounts: Uint8Array;
|
|
12
|
+
data: Uint8Array;
|
|
13
|
+
};
|
|
14
|
+
export type ShredAddressTableLookup = {
|
|
15
|
+
accountKey: string;
|
|
16
|
+
writableIndexes: Uint8Array;
|
|
17
|
+
readonlyIndexes: Uint8Array;
|
|
18
|
+
};
|
|
19
|
+
export type ShredWasmTx = {
|
|
20
|
+
signature: string;
|
|
21
|
+
accounts: string[];
|
|
22
|
+
instructions?: ShredWasmCompiledIx[];
|
|
23
|
+
/** 缺省则按仅静态账户处理 */
|
|
24
|
+
messageVersion?: "legacy" | "v0";
|
|
25
|
+
header?: MessageHeader;
|
|
26
|
+
recentBlockhash?: Uint8Array;
|
|
27
|
+
addressTableLookups?: ShredAddressTableLookup[];
|
|
28
|
+
};
|
|
29
|
+
/**
|
|
30
|
+
* 使用已解析的完整账户表(静态+ALT)解析外层指令。
|
|
31
|
+
*/
|
|
32
|
+
export declare function dexEventsFromShredWasmTxWithFullKeys(tx: ShredWasmTx, fullAccountKeys: string[], slot: number, txIndex: number, grpcRecvUs: number, eventTypeFilter?: EventTypeFilter): DexEvent[];
|
|
33
|
+
/** 仅静态账户表(无 RPC 时;V0+ALT 交易多数指令无法解析) */
|
|
34
|
+
export declare function dexEventsFromShredWasmTx(tx: ShredWasmTx, slot: number, txIndex: number, grpcRecvUs: number, eventTypeFilter?: EventTypeFilter): DexEvent[];
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.dexEventsFromShredWasmTxWithFullKeys = dexEventsFromShredWasmTxWithFullKeys;
|
|
4
|
+
exports.dexEventsFromShredWasmTx = dexEventsFromShredWasmTx;
|
|
5
|
+
const pumpfun_fee_enrich_js_1 = require("../core/pumpfun_fee_enrich.js");
|
|
6
|
+
const mod_js_1 = require("../instr/mod.js");
|
|
7
|
+
function asU8(b) {
|
|
8
|
+
if (b instanceof Uint8Array)
|
|
9
|
+
return b;
|
|
10
|
+
return Uint8Array.from(b);
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* 使用已解析的完整账户表(静态+ALT)解析外层指令。
|
|
14
|
+
*/
|
|
15
|
+
function dexEventsFromShredWasmTxWithFullKeys(tx, fullAccountKeys, slot, txIndex, grpcRecvUs, eventTypeFilter) {
|
|
16
|
+
const ixs = tx.instructions;
|
|
17
|
+
if (!ixs?.length || fullAccountKeys.length === 0)
|
|
18
|
+
return [];
|
|
19
|
+
const out = [];
|
|
20
|
+
for (const rawIx of ixs) {
|
|
21
|
+
const pidIdx = rawIx.programIdIndex;
|
|
22
|
+
if (!Number.isFinite(pidIdx) || pidIdx < 0 || pidIdx >= fullAccountKeys.length)
|
|
23
|
+
continue;
|
|
24
|
+
const programId = fullAccountKeys[pidIdx];
|
|
25
|
+
const accBytes = asU8(rawIx.accounts);
|
|
26
|
+
const data = asU8(rawIx.data);
|
|
27
|
+
const accountStrs = [];
|
|
28
|
+
let oob = false;
|
|
29
|
+
for (let i = 0; i < accBytes.length; i++) {
|
|
30
|
+
const ai = accBytes[i];
|
|
31
|
+
if (ai >= fullAccountKeys.length) {
|
|
32
|
+
oob = true;
|
|
33
|
+
break;
|
|
34
|
+
}
|
|
35
|
+
accountStrs.push(fullAccountKeys[ai]);
|
|
36
|
+
}
|
|
37
|
+
if (oob)
|
|
38
|
+
continue;
|
|
39
|
+
const ev = (0, mod_js_1.parseInstructionUnified)(data, accountStrs, tx.signature, slot, txIndex, undefined, grpcRecvUs, eventTypeFilter, programId);
|
|
40
|
+
if (ev)
|
|
41
|
+
out.push(ev);
|
|
42
|
+
}
|
|
43
|
+
(0, pumpfun_fee_enrich_js_1.enrichPumpfunSameTxPostMerge)(out);
|
|
44
|
+
return out;
|
|
45
|
+
}
|
|
46
|
+
/** 仅静态账户表(无 RPC 时;V0+ALT 交易多数指令无法解析) */
|
|
47
|
+
function dexEventsFromShredWasmTx(tx, slot, txIndex, grpcRecvUs, eventTypeFilter) {
|
|
48
|
+
return dexEventsFromShredWasmTxWithFullKeys(tx, tx.accounts, slot, txIndex, grpcRecvUs, eventTypeFilter);
|
|
49
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ShredStream gRPC 消息形状(与 Rust `shredstream::proto` 中 prost 定义一致)。
|
|
3
|
+
* 实际线路仍由 `shredstream.proto` + `@grpc/proto-loader` 解析。
|
|
4
|
+
*/
|
|
5
|
+
export type SubscribeEntriesRequest = Record<string, never>;
|
|
6
|
+
export interface ShredstreamEntryMessage {
|
|
7
|
+
slot: number | string | bigint;
|
|
8
|
+
entries: Uint8Array | Buffer;
|
|
9
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
syntax = "proto3";
|
|
2
|
+
|
|
3
|
+
package shredstream;
|
|
4
|
+
|
|
5
|
+
// 与 Rust `shredstream/proto/mod.rs` 内嵌定义一致;路径 /shredstream.ShredstreamProxy/SubscribeEntries
|
|
6
|
+
service ShredstreamProxy {
|
|
7
|
+
rpc SubscribeEntries(SubscribeEntriesRequest) returns (stream Entry);
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
message SubscribeEntriesRequest {}
|
|
11
|
+
|
|
12
|
+
message Entry {
|
|
13
|
+
uint64 slot = 1;
|
|
14
|
+
bytes entries = 2;
|
|
15
|
+
}
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.wireBytesToShredWasmTx = wireBytesToShredWasmTx;
|
|
7
|
+
/**
|
|
8
|
+
* 线格式交易字节 → `ShredWasmTx`(与原 wasm-pack 展开字段对齐,供 `parseInstructionUnified`)。
|
|
9
|
+
*/
|
|
10
|
+
const web3_js_1 = require("@solana/web3.js");
|
|
11
|
+
const bs58_1 = __importDefault(require("bs58"));
|
|
12
|
+
function wireBytesToShredWasmTx(raw) {
|
|
13
|
+
try {
|
|
14
|
+
const vt = web3_js_1.VersionedTransaction.deserialize(Buffer.from(raw));
|
|
15
|
+
const sig0 = vt.signatures[0];
|
|
16
|
+
if (!sig0)
|
|
17
|
+
return null;
|
|
18
|
+
const signature = bs58_1.default.encode(sig0);
|
|
19
|
+
const msg = vt.message;
|
|
20
|
+
if (msg instanceof web3_js_1.MessageV0) {
|
|
21
|
+
const lookups = msg.addressTableLookups.map((l) => ({
|
|
22
|
+
accountKey: l.accountKey.toBase58(),
|
|
23
|
+
writableIndexes: new Uint8Array(l.writableIndexes),
|
|
24
|
+
readonlyIndexes: new Uint8Array(l.readonlyIndexes),
|
|
25
|
+
}));
|
|
26
|
+
const instructions = msg.compiledInstructions.map((ix) => ({
|
|
27
|
+
programIdIndex: ix.programIdIndex,
|
|
28
|
+
accounts: new Uint8Array(ix.accountKeyIndexes),
|
|
29
|
+
data: new Uint8Array(ix.data),
|
|
30
|
+
}));
|
|
31
|
+
return {
|
|
32
|
+
signature,
|
|
33
|
+
accounts: msg.staticAccountKeys.map((k) => k.toBase58()),
|
|
34
|
+
instructions,
|
|
35
|
+
messageVersion: "v0",
|
|
36
|
+
header: msg.header,
|
|
37
|
+
recentBlockhash: Uint8Array.from(bs58_1.default.decode(msg.recentBlockhash)),
|
|
38
|
+
addressTableLookups: lookups,
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
const m = msg;
|
|
42
|
+
const instructions = m.compiledInstructions.map((ix) => ({
|
|
43
|
+
programIdIndex: ix.programIdIndex,
|
|
44
|
+
accounts: new Uint8Array(ix.accountKeyIndexes),
|
|
45
|
+
data: new Uint8Array(ix.data),
|
|
46
|
+
}));
|
|
47
|
+
return {
|
|
48
|
+
signature,
|
|
49
|
+
accounts: m.staticAccountKeys.map((k) => k.toBase58()),
|
|
50
|
+
instructions,
|
|
51
|
+
messageVersion: "legacy",
|
|
52
|
+
header: m.header,
|
|
53
|
+
recentBlockhash: Uint8Array.from(bs58_1.default.decode(m.recentBlockhash)),
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
catch {
|
|
57
|
+
return null;
|
|
58
|
+
}
|
|
59
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "sol-parser-sdk-nodejs",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.4.4",
|
|
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",
|
|
@@ -8,7 +8,8 @@
|
|
|
8
8
|
"dist",
|
|
9
9
|
"README.md",
|
|
10
10
|
"README_CN.md",
|
|
11
|
-
"LICENSE"
|
|
11
|
+
"LICENSE",
|
|
12
|
+
".env.example"
|
|
12
13
|
],
|
|
13
14
|
"repository": {
|
|
14
15
|
"type": "git",
|
|
@@ -22,21 +23,37 @@
|
|
|
22
23
|
"node": ">=18"
|
|
23
24
|
},
|
|
24
25
|
"scripts": {
|
|
25
|
-
"build": "tsc",
|
|
26
|
+
"build": "tsc && mkdir -p dist/shredstream && cp src/shredstream/shredstream.proto dist/shredstream/",
|
|
26
27
|
"prepublishOnly": "npm run build",
|
|
27
|
-
"
|
|
28
|
-
"
|
|
29
|
-
"check:
|
|
30
|
-
"
|
|
31
|
-
"
|
|
32
|
-
"check:migration": "npm run
|
|
28
|
+
"check:dex-events": "tsx scripts/check-dex-event-parity.ts",
|
|
29
|
+
"verify:discriminators": "tsx scripts/verify-program-log-discriminators.ts",
|
|
30
|
+
"check:json-utils": "tsx scripts/check-json-utils.ts",
|
|
31
|
+
"check:discriminator-lut": "tsx scripts/check-discriminator-lut.ts",
|
|
32
|
+
"test": "vitest run",
|
|
33
|
+
"check:migration": "npm run check:dex-events && npm run verify:discriminators && npm run check:discriminator-lut && npm run check:json-utils && npm run build",
|
|
34
|
+
"example:shredstream:subscribe": "tsx examples/shredstream_example.ts",
|
|
35
|
+
"example:shredstream:pumpfun-json": "tsx examples/shredstream_pumpfun_json.ts",
|
|
36
|
+
"example:shredstream": "tsx examples/shredstream_example.ts",
|
|
37
|
+
"example:shredstream:pumpfun": "tsx examples/shredstream_pumpfun_json.ts"
|
|
33
38
|
},
|
|
34
|
-
"keywords": [
|
|
39
|
+
"keywords": [
|
|
40
|
+
"solana",
|
|
41
|
+
"dex",
|
|
42
|
+
"parser",
|
|
43
|
+
"pumpfun",
|
|
44
|
+
"raydium"
|
|
45
|
+
],
|
|
35
46
|
"license": "MIT",
|
|
36
47
|
"devDependencies": {
|
|
37
|
-
"
|
|
48
|
+
"@types/node": "^22.19.17",
|
|
49
|
+
"dotenv": "^17.4.1",
|
|
50
|
+
"tsx": "^4.21.0",
|
|
51
|
+
"typescript": "^5.6.3",
|
|
52
|
+
"vitest": "^2.1.9"
|
|
38
53
|
},
|
|
39
54
|
"dependencies": {
|
|
55
|
+
"@grpc/grpc-js": "^1.12.4",
|
|
56
|
+
"@grpc/proto-loader": "^0.7.13",
|
|
40
57
|
"@solana/web3.js": "^1.95.4",
|
|
41
58
|
"@triton-one/yellowstone-grpc": "^1.0.0",
|
|
42
59
|
"bs58": "^6.0.0"
|