sol-parser-sdk 0.5.17 → 0.5.18
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 +24 -15
- package/README_CN.md +23 -14
- package/dist/accounts/pumpfun.js +15 -0
- package/dist/accounts/pumpswap.js +2 -0
- package/dist/core/account_dispatcher_rpc.d.ts +2 -1
- package/dist/core/account_dispatcher_rpc.js +94 -9
- package/dist/core/account_fill_pumpfun.js +20 -0
- package/dist/core/account_fill_pumpswap.d.ts +2 -0
- package/dist/core/account_fill_pumpswap.js +55 -6
- package/dist/core/account_fill_raydium.js +4 -0
- package/dist/core/dex_event.d.ts +149 -0
- package/dist/grpc/client.js +2 -0
- package/dist/grpc/log_instr_dedup.js +75 -7
- package/dist/grpc/order_buffer.d.ts +5 -0
- package/dist/grpc/order_buffer.js +53 -17
- package/dist/grpc/types.d.ts +4 -1
- package/dist/grpc/types.js +23 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -0
- package/dist/instr/inner.js +28 -1
- package/dist/instr/meteora_damm_ix.js +26 -0
- package/dist/instr/meteora_pools_ix.js +17 -9
- package/dist/instr/orca_whirlpool_ix.js +13 -9
- package/dist/instr/pumpfun_ix.js +84 -0
- package/dist/instr/pumpswap_ix.js +85 -10
- package/dist/instr/raydium_clmm_ix.js +5 -3
- package/dist/instr/raydium_cpmm_ix.js +11 -8
- package/dist/logs/meteora_damm.d.ts +7 -0
- package/dist/logs/meteora_damm.js +211 -0
- package/dist/logs/meteora_dbc.d.ts +3 -0
- package/dist/logs/meteora_dbc.js +23 -0
- package/dist/logs/meteora_dlmm.js +16 -4
- package/dist/logs/optimized_matcher.js +93 -10
- package/dist/logs/program_log_discriminators.d.ts +11 -0
- package/dist/logs/program_log_discriminators.js +11 -0
- package/dist/logs/pump.js +6 -3
- package/dist/logs/pump_amm.js +8 -2
- package/dist/logs/pump_upgrade.d.ts +68 -0
- package/dist/logs/pump_upgrade.js +128 -0
- package/dist/logs/raydium_cpmm.js +7 -3
- package/dist/rpc_transaction.js +84 -7
- package/dist/shredstream/alt_cache.d.ts +26 -0
- package/dist/shredstream/alt_cache.js +138 -0
- package/dist/shredstream/client.d.ts +11 -3
- package/dist/shredstream/client.js +76 -72
- package/dist/shredstream/config.d.ts +7 -3
- package/dist/shredstream/index.d.ts +3 -2
- package/dist/shredstream/index.js +5 -3
- package/dist/shredstream/instruction_parse.d.ts +1 -1
- package/dist/shredstream/instruction_parse.js +1 -0
- package/dist/simulation_route.d.ts +1 -1
- package/dist/simulation_route.js +17 -4
- package/dist/transaction_route.js +20 -1
- package/dist/wire_transaction.js +9 -10
- package/docs/USAGE.md +161 -0
- package/package.json +2 -5
- package/NATIVE_ALIGNMENT.md +0 -312
- package/NATIVE_DAMM_FACTORY_ALIGNMENT_20261004.md +0 -16
- package/NATIVE_DAMM_FACTORY_EVIDENCE_20261004.json +0 -55
- package/NATIVE_MIGRATION.md +0 -38
- package/NATIVE_PUMPFUN_NATIVE_DEBIT_ALIGNMENT_20261004.md +0 -18
- package/NATIVE_PUMPFUN_NATIVE_DEBIT_EVIDENCE_20261004.json +0 -57
- package/NATIVE_PUMPFUN_SETTLEMENT_ALIGNMENT_20261004.md +0 -11
- package/NATIVE_PUMPFUN_SETTLEMENT_EVIDENCE_20261004.json +0 -70
|
@@ -43,7 +43,7 @@ const protoLoader = __importStar(require("@grpc/proto-loader"));
|
|
|
43
43
|
const node_path_1 = require("node:path");
|
|
44
44
|
const unified_parser_js_1 = require("../core/unified_parser.js");
|
|
45
45
|
const instruction_parse_js_1 = require("./instruction_parse.js");
|
|
46
|
-
const
|
|
46
|
+
const alt_cache_js_1 = require("./alt_cache.js");
|
|
47
47
|
const config_js_1 = require("./config.js");
|
|
48
48
|
const entries_decode_js_1 = require("./entries_decode.js");
|
|
49
49
|
const PROTO_PATH = (0, node_path_1.join)(__dirname, "shredstream.proto");
|
|
@@ -79,8 +79,18 @@ function grpcCredentialsForEndpoint(endpoint) {
|
|
|
79
79
|
}
|
|
80
80
|
return grpc.credentials.createInsecure();
|
|
81
81
|
}
|
|
82
|
-
function sleepMs(ms) {
|
|
83
|
-
|
|
82
|
+
function sleepMs(ms, signal) {
|
|
83
|
+
if (signal.aborted)
|
|
84
|
+
return Promise.resolve();
|
|
85
|
+
return new Promise((resolve) => {
|
|
86
|
+
const finish = () => {
|
|
87
|
+
clearTimeout(timer);
|
|
88
|
+
signal.removeEventListener("abort", finish);
|
|
89
|
+
resolve();
|
|
90
|
+
};
|
|
91
|
+
const timer = setTimeout(finish, ms);
|
|
92
|
+
signal.addEventListener("abort", finish, { once: true });
|
|
93
|
+
});
|
|
84
94
|
}
|
|
85
95
|
function shredDebugEnabled() {
|
|
86
96
|
const v = process.env.SHREDSTREAM_DEBUG;
|
|
@@ -127,18 +137,22 @@ class ShredStreamClient {
|
|
|
127
137
|
endpoint;
|
|
128
138
|
config;
|
|
129
139
|
ServiceClient;
|
|
140
|
+
lifecycleTail = Promise.resolve();
|
|
130
141
|
loopAbort = null;
|
|
131
142
|
loopPromise = null;
|
|
132
143
|
activeCall = null;
|
|
144
|
+
altCache;
|
|
133
145
|
receiveStats = {
|
|
134
146
|
entryMessagesReceived: 0,
|
|
135
147
|
entryDecodeFailures: 0,
|
|
136
148
|
transactionsDecoded: 0,
|
|
137
149
|
dexEventsQueued: 0,
|
|
150
|
+
altCacheMissTransactions: 0,
|
|
138
151
|
};
|
|
139
152
|
constructor(endpoint, config) {
|
|
140
153
|
this.endpoint = endpoint;
|
|
141
154
|
this.config = config;
|
|
155
|
+
this.altCache = new alt_cache_js_1.AddressLookupTableCache(config.connection, config.address_lookup_tables, config.alt_refresh_interval_ms);
|
|
142
156
|
const root = loadProtoDefinition();
|
|
143
157
|
const shred = root.shredstream;
|
|
144
158
|
this.ServiceClient = shred.ShredstreamProxy;
|
|
@@ -167,6 +181,12 @@ class ShredStreamClient {
|
|
|
167
181
|
getReceiveStats() {
|
|
168
182
|
return { ...this.receiveStats };
|
|
169
183
|
}
|
|
184
|
+
/** Prewarm required ALTs before subscribe(); this explicit call may perform RPC. */
|
|
185
|
+
async preloadAddressLookupTables(addresses) {
|
|
186
|
+
await this.altCache.preload(addresses);
|
|
187
|
+
}
|
|
188
|
+
/** Background refresh failures; cache misses are reported separately in receive stats. */
|
|
189
|
+
getAltRefreshFailures() { return this.altCache.refreshFailures; }
|
|
170
190
|
/**
|
|
171
191
|
* 与 Rust `new_with_config` 中 `ShredstreamProxyClient::connect_with_config(endpoint, &config)` 一致:
|
|
172
192
|
* `connection_timeout_ms` / `request_timeout_ms` / `max_decoding_message_size` 见 `shredstream/proto/mod.rs`。
|
|
@@ -195,19 +215,27 @@ class ShredStreamClient {
|
|
|
195
215
|
* 重连循环使用订阅时刻的配置快照(与 Rust 在 `tokio::spawn` 前 `config.clone()` 一致)。
|
|
196
216
|
*/
|
|
197
217
|
async subscribe(eventTypeFilter) {
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
218
|
+
return this.serializeLifecycle(async () => {
|
|
219
|
+
await this.stopUnlocked();
|
|
220
|
+
this.receiveStats = {
|
|
221
|
+
entryMessagesReceived: 0,
|
|
222
|
+
entryDecodeFailures: 0,
|
|
223
|
+
transactionsDecoded: 0,
|
|
224
|
+
dexEventsQueued: 0,
|
|
225
|
+
altCacheMissTransactions: 0,
|
|
226
|
+
};
|
|
227
|
+
const queue = new ShredEventQueue();
|
|
228
|
+
const ac = new AbortController();
|
|
229
|
+
this.loopAbort = ac;
|
|
230
|
+
const configSnapshot = { ...this.config };
|
|
231
|
+
this.altCache.start();
|
|
232
|
+
this.loopPromise = this.runReconnectLoop(queue, ac.signal, configSnapshot, eventTypeFilter)
|
|
233
|
+
.finally(() => {
|
|
234
|
+
if (this.loopAbort === ac)
|
|
235
|
+
this.altCache.stop();
|
|
236
|
+
});
|
|
237
|
+
return queue;
|
|
238
|
+
});
|
|
211
239
|
}
|
|
212
240
|
/** 与 Rust `subscribe_with_filter` 对齐:在解析热路径按事件类型预过滤。 */
|
|
213
241
|
async subscribeWithFilter(eventTypeFilter) {
|
|
@@ -215,6 +243,15 @@ class ShredStreamClient {
|
|
|
215
243
|
}
|
|
216
244
|
/** 停止订阅并中止当前流 */
|
|
217
245
|
async stop() {
|
|
246
|
+
await this.serializeLifecycle(() => this.stopUnlocked());
|
|
247
|
+
}
|
|
248
|
+
serializeLifecycle(operation) {
|
|
249
|
+
const result = this.lifecycleTail.then(operation);
|
|
250
|
+
this.lifecycleTail = result.then(() => undefined, () => undefined);
|
|
251
|
+
return result;
|
|
252
|
+
}
|
|
253
|
+
async stopUnlocked() {
|
|
254
|
+
this.altCache.stop();
|
|
218
255
|
this.loopAbort?.abort();
|
|
219
256
|
this.loopAbort = null;
|
|
220
257
|
this.activeCall?.cancel();
|
|
@@ -244,7 +281,7 @@ class ShredStreamClient {
|
|
|
244
281
|
break;
|
|
245
282
|
const msg = e instanceof Error ? e.message : String(e);
|
|
246
283
|
console.error(`ShredStream error: ${msg} - retry in ${delay}ms`);
|
|
247
|
-
await sleepMs(delay);
|
|
284
|
+
await sleepMs(delay, signal);
|
|
248
285
|
if (signal.aborted)
|
|
249
286
|
break;
|
|
250
287
|
delay = Math.min(delay * 2, 60_000);
|
|
@@ -268,23 +305,31 @@ class ShredStreamClient {
|
|
|
268
305
|
if (settled)
|
|
269
306
|
return;
|
|
270
307
|
settled = true;
|
|
308
|
+
signal.removeEventListener("abort", abort);
|
|
309
|
+
if (this.activeCall === call)
|
|
310
|
+
this.activeCall = null;
|
|
311
|
+
client.close();
|
|
271
312
|
fn();
|
|
272
313
|
};
|
|
314
|
+
const abort = () => {
|
|
315
|
+
call.cancel();
|
|
316
|
+
finish(resolve);
|
|
317
|
+
};
|
|
273
318
|
const call = client.subscribeEntries({}, new grpc.Metadata());
|
|
274
319
|
this.activeCall = call;
|
|
320
|
+
signal.addEventListener("abort", abort, { once: true });
|
|
275
321
|
console.info("ShredStream connected, receiving entries...");
|
|
276
322
|
call.on("data", (entry) => {
|
|
277
|
-
if (signal.aborted)
|
|
323
|
+
if (signal.aborted || settled)
|
|
278
324
|
return;
|
|
279
325
|
try {
|
|
280
|
-
|
|
326
|
+
this.processEntryMessage(entry, queue, eventTypeFilter);
|
|
281
327
|
}
|
|
282
328
|
catch (err) {
|
|
283
329
|
console.error("processEntryMessage:", err);
|
|
284
330
|
}
|
|
285
331
|
});
|
|
286
332
|
call.on("error", (err) => {
|
|
287
|
-
client.close();
|
|
288
333
|
if (signal.aborted || err.code === grpc.status.CANCELLED) {
|
|
289
334
|
finish(() => resolve());
|
|
290
335
|
return;
|
|
@@ -293,12 +338,11 @@ class ShredStreamClient {
|
|
|
293
338
|
finish(() => reject(err));
|
|
294
339
|
});
|
|
295
340
|
call.on("end", () => {
|
|
296
|
-
client.close();
|
|
297
341
|
finish(() => resolve());
|
|
298
342
|
});
|
|
299
343
|
});
|
|
300
344
|
}
|
|
301
|
-
|
|
345
|
+
processEntryMessage(entry, queue, eventTypeFilter) {
|
|
302
346
|
this.receiveStats.entryMessagesReceived += 1;
|
|
303
347
|
const recvUs = (0, unified_parser_js_1.nowUs)();
|
|
304
348
|
const slotNum = toSlotNumber(entry.slot);
|
|
@@ -314,20 +358,9 @@ class ShredStreamClient {
|
|
|
314
358
|
console.warn(`[shredstream] bincode 解码失败 slot=${slotNum} bytes=${bytes.length}: ${msg}`);
|
|
315
359
|
return;
|
|
316
360
|
}
|
|
317
|
-
const conn = this.config.connection;
|
|
318
|
-
if (conn) {
|
|
319
|
-
try {
|
|
320
|
-
await this.processEntryMessageWithAlt(decoded, slotNum, recvUs, queue, bytes.length, conn, eventTypeFilter);
|
|
321
|
-
}
|
|
322
|
-
catch (e) {
|
|
323
|
-
console.warn(`[shredstream] ALT/RPC 解析失败 slot=${slotNum}:`, e);
|
|
324
|
-
this.processEntryMessageSync(decoded, slotNum, recvUs, queue, bytes.length, eventTypeFilter);
|
|
325
|
-
}
|
|
326
|
-
return;
|
|
327
|
-
}
|
|
328
361
|
this.processEntryMessageSync(decoded, slotNum, recvUs, queue, bytes.length, eventTypeFilter);
|
|
329
362
|
}
|
|
330
|
-
/**
|
|
363
|
+
/** Synchronous parsing: static keys or complete prewarmed ALT snapshots only. */
|
|
331
364
|
processEntryMessageSync(decoded, slotNum, recvUs, queue, entriesBytesLen, eventTypeFilter) {
|
|
332
365
|
let txTotal = 0;
|
|
333
366
|
let evTotal = 0;
|
|
@@ -341,44 +374,15 @@ class ShredStreamClient {
|
|
|
341
374
|
const txIndex = globalTxIndex++;
|
|
342
375
|
if (!tx?.signature)
|
|
343
376
|
continue;
|
|
344
|
-
const
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
queue.push(ev);
|
|
349
|
-
}
|
|
350
|
-
}
|
|
351
|
-
}
|
|
352
|
-
this.receiveStats.transactionsDecoded += txTotal;
|
|
353
|
-
this.receiveStats.dexEventsQueued += evTotal;
|
|
354
|
-
if (shredDebugEnabled()) {
|
|
355
|
-
console.info(`[shredstream] slot=${slotNum} entries_bytes=${entriesBytesLen} solana_entries=${decoded.length} txs=${txTotal} dex_events=${evTotal} (static_accounts_only)`);
|
|
356
|
-
}
|
|
357
|
-
}
|
|
358
|
-
/** 拉取 ALT 后完整账户表解析 */
|
|
359
|
-
async processEntryMessageWithAlt(decoded, slotNum, recvUs, queue, entriesBytesLen, conn, eventTypeFilter) {
|
|
360
|
-
const altKeys = new Set();
|
|
361
|
-
for (const outer of decoded) {
|
|
362
|
-
for (const tx of outer) {
|
|
363
|
-
for (const l of tx.addressTableLookups ?? []) {
|
|
364
|
-
altKeys.add(l.accountKey);
|
|
365
|
-
}
|
|
366
|
-
}
|
|
367
|
-
}
|
|
368
|
-
const altMap = await (0, alt_lookup_js_1.loadAddressLookupTableAccounts)(conn, [...altKeys]);
|
|
369
|
-
let txTotal = 0;
|
|
370
|
-
let evTotal = 0;
|
|
371
|
-
let globalTxIndex = 0;
|
|
372
|
-
for (const outer of decoded) {
|
|
373
|
-
const txs = outer;
|
|
374
|
-
txTotal += txs.length;
|
|
375
|
-
for (let i = 0; i < txs.length; i++) {
|
|
376
|
-
const tx = txs[i];
|
|
377
|
-
const txIndex = globalTxIndex++;
|
|
378
|
-
if (!tx?.signature)
|
|
377
|
+
const useCache = this.config.connection || this.config.address_lookup_tables;
|
|
378
|
+
const fullKeys = useCache ? this.altCache.resolve(tx) : tx.accounts;
|
|
379
|
+
if (!fullKeys) {
|
|
380
|
+
this.receiveStats.altCacheMissTransactions++;
|
|
379
381
|
continue;
|
|
380
|
-
|
|
381
|
-
const events =
|
|
382
|
+
}
|
|
383
|
+
const events = useCache
|
|
384
|
+
? (0, instruction_parse_js_1.dexEventsFromShredWasmTxWithFullKeys)(tx, fullKeys, slotNum, txIndex, recvUs, eventTypeFilter)
|
|
385
|
+
: (0, instruction_parse_js_1.dexEventsFromShredWasmTx)(tx, slotNum, txIndex, recvUs, eventTypeFilter);
|
|
382
386
|
evTotal += events.length;
|
|
383
387
|
for (const ev of events) {
|
|
384
388
|
setGrpcRecvUsMut(ev, recvUs);
|
|
@@ -389,7 +393,7 @@ class ShredStreamClient {
|
|
|
389
393
|
this.receiveStats.transactionsDecoded += txTotal;
|
|
390
394
|
this.receiveStats.dexEventsQueued += evTotal;
|
|
391
395
|
if (shredDebugEnabled()) {
|
|
392
|
-
console.info(`[shredstream] slot=${slotNum} entries_bytes=${entriesBytesLen} solana_entries=${decoded.length} txs=${txTotal} dex_events=${evTotal}
|
|
396
|
+
console.info(`[shredstream] slot=${slotNum} entries_bytes=${entriesBytesLen} solana_entries=${decoded.length} txs=${txTotal} dex_events=${evTotal} (static_accounts_only)`);
|
|
393
397
|
}
|
|
394
398
|
}
|
|
395
399
|
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { Connection } from "@solana/web3.js";
|
|
1
|
+
import type { AddressLookupTableAccount, Connection } from "@solana/web3.js";
|
|
2
2
|
/**
|
|
3
3
|
* 与 Rust `sol-parser-sdk/src/shredstream/config.rs` 字段一一对应。
|
|
4
4
|
*
|
|
@@ -7,7 +7,7 @@ import type { Connection } from "@solana/web3.js";
|
|
|
7
7
|
* (`request_timeout_ms` 保留与 Rust `config.rs` 一致,流式路径不填 deadline)。
|
|
8
8
|
* `reconnect_delay_ms` / `max_reconnect_attempts` 由 `client.rs` 重连循环使用。
|
|
9
9
|
*
|
|
10
|
-
* **TS
|
|
10
|
+
* **TS 扩展**:ALT 使用预热快照和独立刷新任务;解析热路径不发送 RPC。
|
|
11
11
|
*/
|
|
12
12
|
export interface ShredStreamConfig {
|
|
13
13
|
/** 连接超时(毫秒)→ tonic `Endpoint::connect_timeout` / gRPC `waitForReady` */
|
|
@@ -20,8 +20,12 @@ export interface ShredStreamConfig {
|
|
|
20
20
|
reconnect_delay_ms: number;
|
|
21
21
|
/** 最大重连次数(0 表示无限重连) */
|
|
22
22
|
max_reconnect_attempts: number;
|
|
23
|
-
/**
|
|
23
|
+
/** 可选:只供显式预热及后台 ALT 刷新使用,解析热路径不发送 RPC。 */
|
|
24
24
|
connection?: Connection;
|
|
25
|
+
/** 可选:订阅前已加载的 ALT 快照;未命中时跳过整笔 V0 交易并累计计数。 */
|
|
26
|
+
address_lookup_tables?: readonly AddressLookupTableAccount[];
|
|
27
|
+
/** 独立 ALT 刷新间隔(毫秒),默认 1000;最多一个刷新请求在途。 */
|
|
28
|
+
alt_refresh_interval_ms?: number;
|
|
25
29
|
}
|
|
26
30
|
export declare function defaultShredStreamConfig(): ShredStreamConfig;
|
|
27
31
|
/** 低延迟:更短超时与重连间隔 */
|
|
@@ -2,10 +2,10 @@
|
|
|
2
2
|
* Jito ShredStream 超低延迟订阅(与 Rust `sol-parser-sdk::shredstream` 对齐)。
|
|
3
3
|
*
|
|
4
4
|
* 限制(与 Rust `shredstream/mod.rs` 文档一致):
|
|
5
|
-
* -
|
|
5
|
+
* - ALT 仅使用预热缓存;启用缓存后 miss 会跳过该交易并记录 altCacheMissTransactions,不回放
|
|
6
6
|
* - 无 inner instructions,无法 CPI 解析
|
|
7
7
|
* - 无 block_time
|
|
8
|
-
* - 无交易日志(program logs
|
|
8
|
+
* - 无交易日志(program logs);客户端使用外层指令解析。未配置 ALT 缓存时保留静态账户 best-effort 行为。
|
|
9
9
|
* - `metadata.tx_index` 为**单条 gRPC `Entry` 消息内**跨所有 Solana `Entry` 分组的连续下标(与 golang `shredstream_entries` 扁平 `ti` 对齐),非 slot 级全局序号
|
|
10
10
|
*/
|
|
11
11
|
export { type ShredStreamConfig, defaultShredStreamConfig, lowLatencyShredStreamConfig, highThroughputShredStreamConfig, } from "./config.js";
|
|
@@ -15,3 +15,4 @@ export { fullAccountKeyStringsFromShredTx, loadAddressLookupTableAccounts } from
|
|
|
15
15
|
export type { SubscribeEntriesRequest, ShredstreamEntryMessage } from "./proto_types.js";
|
|
16
16
|
export { bincodeVecEntryCount, decodeEntriesBincodeFlat, decodeEntriesBincodeNested, decodeShredstreamEntriesBincode, type DecodedWireTransaction, } from "./entries_decode.js";
|
|
17
17
|
export { wireBytesToShredWasmTx } from "./wire_to_shred_tx.js";
|
|
18
|
+
export { AddressLookupTableCache } from "./alt_cache.js";
|
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
"use strict";
|
|
2
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;
|
|
3
|
+
exports.AddressLookupTableCache = 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
4
|
/**
|
|
5
5
|
* Jito ShredStream 超低延迟订阅(与 Rust `sol-parser-sdk::shredstream` 对齐)。
|
|
6
6
|
*
|
|
7
7
|
* 限制(与 Rust `shredstream/mod.rs` 文档一致):
|
|
8
|
-
* -
|
|
8
|
+
* - ALT 仅使用预热缓存;启用缓存后 miss 会跳过该交易并记录 altCacheMissTransactions,不回放
|
|
9
9
|
* - 无 inner instructions,无法 CPI 解析
|
|
10
10
|
* - 无 block_time
|
|
11
|
-
* - 无交易日志(program logs
|
|
11
|
+
* - 无交易日志(program logs);客户端使用外层指令解析。未配置 ALT 缓存时保留静态账户 best-effort 行为。
|
|
12
12
|
* - `metadata.tx_index` 为**单条 gRPC `Entry` 消息内**跨所有 Solana `Entry` 分组的连续下标(与 golang `shredstream_entries` 扁平 `ti` 对齐),非 slot 级全局序号
|
|
13
13
|
*/
|
|
14
14
|
var config_js_1 = require("./config.js");
|
|
@@ -31,3 +31,5 @@ Object.defineProperty(exports, "decodeEntriesBincodeNested", { enumerable: true,
|
|
|
31
31
|
Object.defineProperty(exports, "decodeShredstreamEntriesBincode", { enumerable: true, get: function () { return entries_decode_js_1.decodeShredstreamEntriesBincode; } });
|
|
32
32
|
var wire_to_shred_tx_js_1 = require("./wire_to_shred_tx.js");
|
|
33
33
|
Object.defineProperty(exports, "wireBytesToShredWasmTx", { enumerable: true, get: function () { return wire_to_shred_tx_js_1.wireBytesToShredWasmTx; } });
|
|
34
|
+
var alt_cache_js_1 = require("./alt_cache.js");
|
|
35
|
+
Object.defineProperty(exports, "AddressLookupTableCache", { enumerable: true, get: function () { return alt_cache_js_1.AddressLookupTableCache; } });
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* ShredStream:外层编译指令 → `parseInstructionUnified`(与 gRPC 指令解析同源)。
|
|
3
3
|
* `ShredWasmTx` 来自线格式交易经 `@solana/web3.js` 反序列化(见 `wire_to_shred_tx.ts`),非 WASM。
|
|
4
|
-
*
|
|
4
|
+
* 客户端通过预热 ALT 缓存展开 V0 账户;connection 只供独立刷新使用。
|
|
5
5
|
*/
|
|
6
6
|
import type { MessageHeader } from "@solana/web3.js";
|
|
7
7
|
import { type DexEvent } from "../core/dex_event.js";
|
|
@@ -22,6 +22,7 @@ const UNKNOWN_PROGRAM_CANDIDATES = [
|
|
|
22
22
|
program_ids_js_1.RAYDIUM_AMM_V4_PROGRAM_ID,
|
|
23
23
|
program_ids_js_1.ORCA_WHIRLPOOL_PROGRAM_ID,
|
|
24
24
|
program_ids_js_1.METEORA_POOLS_PROGRAM_ID,
|
|
25
|
+
program_ids_js_1.METEORA_DBC_PROGRAM_ID,
|
|
25
26
|
program_ids_js_1.METEORA_DAMM_V2_PROGRAM_ID,
|
|
26
27
|
program_ids_js_1.METEORA_DLMM_PROGRAM_ID,
|
|
27
28
|
];
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { type TransactionRoute } from "./transaction_route.js";
|
|
2
2
|
/** Original wire plus simulateTransaction response. Parsed SPL transfers and ATA setup.
|
|
3
3
|
* Unsupported parsed CPI fail explicitly; raw/compiled CPI are preserved.
|
|
4
|
-
* V0
|
|
4
|
+
* V0 requires caller-resolved addresses in result.value.loadedAddresses.
|
|
5
5
|
*/
|
|
6
6
|
export declare function analyzeSimulationRoutes(wire: Uint8Array, response: unknown, graduatedPools?: readonly string[]): TransactionRoute;
|
package/dist/simulation_route.js
CHANGED
|
@@ -21,12 +21,10 @@ function unsigned(value, bits) {
|
|
|
21
21
|
}
|
|
22
22
|
/** Original wire plus simulateTransaction response. Parsed SPL transfers and ATA setup.
|
|
23
23
|
* Unsupported parsed CPI fail explicitly; raw/compiled CPI are preserved.
|
|
24
|
-
* V0
|
|
24
|
+
* V0 requires caller-resolved addresses in result.value.loadedAddresses.
|
|
25
25
|
*/
|
|
26
26
|
function analyzeSimulationRoutes(wire, response, graduatedPools = []) {
|
|
27
27
|
const tx = (0, wire_transaction_js_1.decodeWireTransaction)(wire).transaction;
|
|
28
|
-
if (tx.message.addressTableLookups.length)
|
|
29
|
-
throw Error("V0 ALT addresses unavailable; use compiled route input with loadedAddresses");
|
|
30
28
|
const root = response, value = root?.result?.value;
|
|
31
29
|
if (root?.error ||
|
|
32
30
|
!value ||
|
|
@@ -37,7 +35,22 @@ function analyzeSimulationRoutes(wire, response, graduatedPools = []) {
|
|
|
37
35
|
if (value.innerInstructions !== null &&
|
|
38
36
|
!Array.isArray(value.innerInstructions))
|
|
39
37
|
throw Error("Invalid simulation inner instructions");
|
|
40
|
-
const
|
|
38
|
+
const lookups = tx.message.addressTableLookups;
|
|
39
|
+
const loaded = value.loadedAddresses ?? { writable: [], readonly: [] };
|
|
40
|
+
if (lookups.length && value.loadedAddresses == null)
|
|
41
|
+
throw Error("V0 ALT addresses unavailable; provide loadedAddresses");
|
|
42
|
+
if (!loaded || typeof loaded !== "object" || Array.isArray(loaded))
|
|
43
|
+
throw Error("Invalid simulation loaded addresses");
|
|
44
|
+
for (const side of ["writable", "readonly"]) {
|
|
45
|
+
const expected = lookups.reduce((n, lookup) => n + lookup[side === "writable" ? "writableIndexes" : "readonlyIndexes"].length, 0);
|
|
46
|
+
if (!Array.isArray(loaded[side]) || loaded[side].length !== expected)
|
|
47
|
+
throw Error("Simulation loaded address count does not match wire lookups");
|
|
48
|
+
for (const key of loaded[side]) {
|
|
49
|
+
if (typeof key !== "string" || bs58_1.default.decode(key).length !== 32)
|
|
50
|
+
throw Error("Invalid simulation loaded public key");
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
const keys = [...tx.message.accountKeys, ...loaded.writable, ...loaded.readonly];
|
|
41
54
|
const index = (key) => {
|
|
42
55
|
if (typeof key !== "string")
|
|
43
56
|
throw Error("Missing simulation account key");
|
|
@@ -47,10 +47,14 @@ const LAB = new Map([
|
|
|
47
47
|
[hex([95, 200, 71, 34, 8, 9, 11, 166]), [false, false]],
|
|
48
48
|
]);
|
|
49
49
|
const PUMP = new Map([
|
|
50
|
+
[hex([184, 23, 238, 97, 103, 197, 211, 61]), [true, false]],
|
|
51
|
+
[hex([194, 171, 28, 70, 104, 77, 91, 47]), [true, true]],
|
|
52
|
+
[hex([93, 246, 130, 60, 231, 233, 64, 178]), [false, true]],
|
|
50
53
|
[hex([198, 46, 21, 82, 180, 217, 232, 112]), [true, true]],
|
|
51
54
|
[hex([102, 6, 61, 18, 1, 218, 235, 234]), [true, false]],
|
|
52
55
|
[hex([51, 230, 133, 164, 1, 127, 131, 173]), [false, true]],
|
|
53
56
|
]);
|
|
57
|
+
const CURVE_V3 = new Map([[hex([7, 5, 29, 196, 245, 23, 101, 80]), [true, false]], [hex([225, 247, 80, 30, 213, 179, 132, 136]), [true, true]], [hex([28, 146, 222, 119, 38, 196, 105, 213]), [false, true]]]);
|
|
54
58
|
const CURVE_V2 = new Map([
|
|
55
59
|
[hex([194, 171, 28, 70, 104, 77, 91, 47]), [true, true]],
|
|
56
60
|
[hex([184, 23, 238, 97, 103, 197, 211, 61]), [true, false]],
|
|
@@ -132,7 +136,7 @@ function swap(ix, mints, graduated) {
|
|
|
132
136
|
(protocol === "LaunchLab" ? LAB : PUMP).has(disc)) {
|
|
133
137
|
const [buy, input] = (protocol === "LaunchLab" ? LAB : PUMP).get(disc);
|
|
134
138
|
exact = input;
|
|
135
|
-
if (n < (protocol === "LaunchLab" ? 18 : 21))
|
|
139
|
+
if (n < (protocol === "LaunchLab" ? 18 : (["b817ee6167c5d33d", "c2ab1c46684d5b2f", "5df6823ce7e940b2"].includes(disc) ? 17 : 21)))
|
|
136
140
|
return null;
|
|
137
141
|
source = a(buy ? 6 : 5);
|
|
138
142
|
dest = a(buy ? 5 : 6);
|
|
@@ -148,6 +152,21 @@ function swap(ix, mints, graduated) {
|
|
|
148
152
|
pair = buy ? [a(4), a(3)] : [a(3), a(4)];
|
|
149
153
|
}
|
|
150
154
|
}
|
|
155
|
+
else if (protocol === 'PumpFun' && CURVE_V3.has(disc) && n === 17 && d.length >= 24) {
|
|
156
|
+
const [buy, input] = CURVE_V3.get(disc);
|
|
157
|
+
exact = input;
|
|
158
|
+
pool = a(5);
|
|
159
|
+
user = a(8);
|
|
160
|
+
source = a(buy ? 10 : 9);
|
|
161
|
+
dest = a(buy ? 9 : 10);
|
|
162
|
+
pair = buy ? [a(2), a(1)] : [a(1), a(2)];
|
|
163
|
+
if (a(2) === exports.WSOL) {
|
|
164
|
+
if (buy)
|
|
165
|
+
source = user;
|
|
166
|
+
else
|
|
167
|
+
dest = user;
|
|
168
|
+
}
|
|
169
|
+
}
|
|
151
170
|
else if (protocol === 'PumpFun' && CURVE_V2.has(disc) && n >= 16) {
|
|
152
171
|
const [buy, input] = CURVE_V2.get(disc);
|
|
153
172
|
exact = input;
|
package/dist/wire_transaction.js
CHANGED
|
@@ -145,25 +145,24 @@ function decodeWireTransaction(data, offset = 0, requireComplete = true) {
|
|
|
145
145
|
throw new Error("Signature count does not match header");
|
|
146
146
|
}
|
|
147
147
|
if (header[0] > keys.length ||
|
|
148
|
-
header[1]
|
|
148
|
+
header[1] >= header[0] ||
|
|
149
149
|
header[2] > keys.length - header[0])
|
|
150
150
|
throw new Error("Invalid message header");
|
|
151
151
|
if (version === 1) {
|
|
152
|
-
if (header[1] >= header[0])
|
|
153
|
-
throw new Error("V1 requires a writable fee payer");
|
|
154
152
|
if (new Set(keys.map((k) => bs58_1.default.encode(k))).size !== keys.length)
|
|
155
153
|
throw new Error("Duplicate V1 accounts");
|
|
156
|
-
for (const ix of instructions)
|
|
157
|
-
if (ix.programIdIndex === 0 ||
|
|
158
|
-
ix.programIdIndex >= keys.length ||
|
|
159
|
-
ix.accounts.some((i) => i >= keys.length))
|
|
160
|
-
throw new Error("Invalid V1 instruction index");
|
|
161
154
|
}
|
|
162
|
-
|
|
155
|
+
let accountCount = keys.length;
|
|
156
|
+
for (const lookup of lookups) {
|
|
157
|
+
const count = lookup.writableIndexes.length + lookup.readonlyIndexes.length;
|
|
158
|
+
if (!count)
|
|
159
|
+
throw new Error("Empty address table lookup");
|
|
160
|
+
accountCount += count;
|
|
161
|
+
}
|
|
163
162
|
if (accountCount > 256)
|
|
164
163
|
throw new Error("Too many resolved account keys");
|
|
165
164
|
for (const ix of instructions)
|
|
166
|
-
if (ix.programIdIndex >= keys.length || ix.accounts.some(i => i >= accountCount))
|
|
165
|
+
if (ix.programIdIndex === 0 || ix.programIdIndex >= keys.length || ix.accounts.some(i => i >= accountCount))
|
|
167
166
|
throw new Error("Invalid instruction index");
|
|
168
167
|
if (requireComplete && r.pos !== data.length)
|
|
169
168
|
throw new Error("Trailing transaction bytes");
|