sol-parser-sdk-nodejs 0.3.0 → 0.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.env.example +24 -0
- package/README.md +94 -352
- package/README_CN.md +101 -253
- package/dist/accounts/mod.d.ts +2 -0
- package/dist/accounts/mod.js +5 -1
- package/dist/accounts/rpc_wallet.d.ts +5 -0
- package/dist/accounts/rpc_wallet.js +18 -0
- package/dist/accounts/rust_aliases.d.ts +9 -0
- package/dist/accounts/rust_aliases.js +19 -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_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 +25 -44
- package/dist/core/metadata.d.ts +1 -0
- package/dist/core/unified_parser.d.ts +2 -2
- package/dist/core/unified_parser.js +6 -6
- package/dist/grpc/client.d.ts +6 -0
- package/dist/grpc/client.js +121 -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/program_ids.d.ts +25 -0
- package/dist/grpc/program_ids.js +54 -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 +53 -2
- package/dist/grpc/types.js +98 -7
- package/dist/grpc/yellowstone_parse.d.ts +5 -0
- package/dist/grpc/yellowstone_parse.js +15 -2
- package/dist/index.d.ts +36 -6
- package/dist/index.js +172 -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.js +7 -2
- 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 +19 -15
- 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 +59 -26
- 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 +7 -0
- package/dist/instr/rust_aliases.js +14 -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 +60 -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 +3 -3
- 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/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 +47 -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
package/dist/instr/utils.d.ts
CHANGED
|
@@ -7,4 +7,4 @@ export declare function readBorshStrAt(data: Uint8Array, offset: number): {
|
|
|
7
7
|
next: number;
|
|
8
8
|
} | null;
|
|
9
9
|
export declare function readPubkeyIx(data: Uint8Array, o: number): string | null;
|
|
10
|
-
export { readU64LE, readU128LE, readU8, readBool, readI64LE, } from "../util/binary.js";
|
|
10
|
+
export { readU64LE, readU128LE, readU8, readBool, readI64LE, readI32LE, } from "../util/binary.js";
|
package/dist/instr/utils.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.readI64LE = exports.readBool = exports.readU8 = exports.readU128LE = exports.readU64LE = void 0;
|
|
3
|
+
exports.readI32LE = exports.readI64LE = exports.readBool = exports.readU8 = exports.readU128LE = exports.readU64LE = void 0;
|
|
4
4
|
exports.ixMeta = ixMeta;
|
|
5
5
|
exports.getAccount = getAccount;
|
|
6
6
|
exports.readBorshStrAt = readBorshStrAt;
|
|
@@ -31,3 +31,4 @@ Object.defineProperty(exports, "readU128LE", { enumerable: true, get: function (
|
|
|
31
31
|
Object.defineProperty(exports, "readU8", { enumerable: true, get: function () { return binary_js_2.readU8; } });
|
|
32
32
|
Object.defineProperty(exports, "readBool", { enumerable: true, get: function () { return binary_js_2.readBool; } });
|
|
33
33
|
Object.defineProperty(exports, "readI64LE", { enumerable: true, get: function () { return binary_js_2.readI64LE; } });
|
|
34
|
+
Object.defineProperty(exports, "readI32LE", { enumerable: true, get: function () { return binary_js_2.readI32LE; } });
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/** 与 Rust `discriminator_lut::Protocol` 变体名一致(字符串联合,便于 JSON / 日志)。 */
|
|
2
|
+
export type LogProtocol = "PumpFun" | "PumpSwap" | "RaydiumClmm" | "RaydiumCpmm" | "RaydiumAmm" | "OrcaWhirlpool" | "MeteoraAmm" | "MeteoraDamm";
|
|
3
|
+
/** Rust `discriminator_to_name` */
|
|
4
|
+
export declare function discriminatorToName(discriminator: bigint): string | undefined;
|
|
5
|
+
/** Rust `discriminator_to_protocol` */
|
|
6
|
+
export declare function discriminatorToProtocol(discriminator: bigint): LogProtocol | undefined;
|
|
7
|
+
export type LutDiscriminatorInfo = {
|
|
8
|
+
discriminator: bigint;
|
|
9
|
+
name: string;
|
|
10
|
+
protocol: LogProtocol;
|
|
11
|
+
};
|
|
12
|
+
/** Rust `lookup_discriminator`(无 parser 函数指针;含 `protocol`) */
|
|
13
|
+
export declare function lookupDiscriminator(discriminator: bigint): LutDiscriminatorInfo | undefined;
|
|
14
|
+
/** Rust `discriminator_to_name` */
|
|
15
|
+
export declare const discriminator_to_name: typeof discriminatorToName;
|
|
16
|
+
/** Rust `discriminator_to_protocol` */
|
|
17
|
+
export declare const discriminator_to_protocol: typeof discriminatorToProtocol;
|
|
18
|
+
/** Rust `lookup_discriminator` */
|
|
19
|
+
export declare const lookup_discriminator: typeof lookupDiscriminator;
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.lookup_discriminator = exports.discriminator_to_protocol = exports.discriminator_to_name = void 0;
|
|
4
|
+
exports.discriminatorToName = discriminatorToName;
|
|
5
|
+
exports.discriminatorToProtocol = discriminatorToProtocol;
|
|
6
|
+
exports.lookupDiscriminator = lookupDiscriminator;
|
|
7
|
+
/**
|
|
8
|
+
* 与 Rust `logs::discriminator_lut` 中的名称 / 协议查询对齐(基于 `PROGRAM_LOG_DISC`,含 PumpSwap 子表)。
|
|
9
|
+
* 不含 `parse_with_discriminator`:完整数据路径请用 `parseLogOptimized` / `parse_log_optimized`。
|
|
10
|
+
*/
|
|
11
|
+
const program_log_discriminators_js_1 = require("./program_log_discriminators.js");
|
|
12
|
+
function protocolForProgramLogKey(key) {
|
|
13
|
+
if (key.startsWith("PUMPFUN_"))
|
|
14
|
+
return "PumpFun";
|
|
15
|
+
if (key.startsWith("PUMPSWAP_"))
|
|
16
|
+
return "PumpSwap";
|
|
17
|
+
if (key.startsWith("RAYDIUM_CLMM_"))
|
|
18
|
+
return "RaydiumClmm";
|
|
19
|
+
if (key.startsWith("RAYDIUM_CPMM_"))
|
|
20
|
+
return "RaydiumCpmm";
|
|
21
|
+
if (key.startsWith("RAYDIUM_AMM_"))
|
|
22
|
+
return "RaydiumAmm";
|
|
23
|
+
if (key.startsWith("ORCA_"))
|
|
24
|
+
return "OrcaWhirlpool";
|
|
25
|
+
if (key.startsWith("METEORA_AMM_"))
|
|
26
|
+
return "MeteoraAmm";
|
|
27
|
+
if (key.startsWith("METEORA_DAMM_"))
|
|
28
|
+
return "MeteoraDamm";
|
|
29
|
+
throw new Error(`discriminator_lut: 无法从键推断协议: ${key}`);
|
|
30
|
+
}
|
|
31
|
+
const NAME_BY_DISC = new Map();
|
|
32
|
+
const PROTOCOL_BY_DISC = new Map();
|
|
33
|
+
for (const [name, disc] of Object.entries(program_log_discriminators_js_1.PROGRAM_LOG_DISC)) {
|
|
34
|
+
NAME_BY_DISC.set(disc, name);
|
|
35
|
+
PROTOCOL_BY_DISC.set(disc, protocolForProgramLogKey(name));
|
|
36
|
+
}
|
|
37
|
+
/** Rust `discriminator_to_name` */
|
|
38
|
+
function discriminatorToName(discriminator) {
|
|
39
|
+
return NAME_BY_DISC.get(discriminator);
|
|
40
|
+
}
|
|
41
|
+
/** Rust `discriminator_to_protocol` */
|
|
42
|
+
function discriminatorToProtocol(discriminator) {
|
|
43
|
+
return PROTOCOL_BY_DISC.get(discriminator);
|
|
44
|
+
}
|
|
45
|
+
/** Rust `lookup_discriminator`(无 parser 函数指针;含 `protocol`) */
|
|
46
|
+
function lookupDiscriminator(discriminator) {
|
|
47
|
+
const name = NAME_BY_DISC.get(discriminator);
|
|
48
|
+
if (name === undefined)
|
|
49
|
+
return undefined;
|
|
50
|
+
const protocol = PROTOCOL_BY_DISC.get(discriminator);
|
|
51
|
+
if (protocol === undefined)
|
|
52
|
+
return undefined;
|
|
53
|
+
return { discriminator, name, protocol };
|
|
54
|
+
}
|
|
55
|
+
/** Rust `discriminator_to_name` */
|
|
56
|
+
exports.discriminator_to_name = discriminatorToName;
|
|
57
|
+
/** Rust `discriminator_to_protocol` */
|
|
58
|
+
exports.discriminator_to_protocol = discriminatorToProtocol;
|
|
59
|
+
/** Rust `lookup_discriminator` */
|
|
60
|
+
exports.lookup_discriminator = lookupDiscriminator;
|
|
@@ -1,6 +1,5 @@
|
|
|
1
|
-
import { type EventMetadata } from "../core/metadata.js";
|
|
2
1
|
import type { DexEvent } from "../core/dex_event.js";
|
|
3
|
-
/**
|
|
4
|
-
|
|
5
|
-
|
|
2
|
+
/**
|
|
3
|
+
* Meteora DAMM Program data 入口;与 Rust `logs/meteora_damm.rs` 一致,仅解析 Swap / Swap2(其余 discriminator 不产出事件)。
|
|
4
|
+
*/
|
|
6
5
|
export declare function parseMeteoraDammLog(log: string, signature: string, slot: number, txIndex: number, blockTimeUs: number | undefined, grpcRecvUs: number): DexEvent | null;
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.parseInitializePoolEvent = parseInitializePoolEvent;
|
|
4
3
|
exports.parseMeteoraDammLog = parseMeteoraDammLog;
|
|
5
4
|
const metadata_js_1 = require("../core/metadata.js");
|
|
6
5
|
const dex_event_js_1 = require("../core/dex_event.js");
|
|
@@ -14,13 +13,6 @@ function discOf(bytes) {
|
|
|
14
13
|
}
|
|
15
14
|
const SWAP = discOf([27, 60, 21, 213, 138, 170, 187, 147]);
|
|
16
15
|
const SWAP2 = discOf([189, 66, 51, 168, 38, 80, 117, 153]);
|
|
17
|
-
/** 与 `instr/meteora_damm_ix.ts` CPI 内层事件 disc 一致;Program data 去掉前 8 字节后与 CPI 载荷布局相同 */
|
|
18
|
-
const CREATE_POSITION = discOf([156, 15, 119, 198, 29, 181, 221, 55]);
|
|
19
|
-
const CLOSE_POSITION = discOf([20, 145, 144, 68, 143, 142, 214, 178]);
|
|
20
|
-
const ADD_LIQUIDITY = discOf([175, 242, 8, 157, 30, 247, 185, 169]);
|
|
21
|
-
const REMOVE_LIQUIDITY = discOf([87, 46, 88, 98, 175, 96, 34, 91]);
|
|
22
|
-
/** Meteora `cp-amm` `EvtInitializePool`(见 `programs/cp-amm/src/event.rs`) */
|
|
23
|
-
const INITIALIZE_POOL = discOf([228, 50, 246, 85, 203, 66, 134, 37]);
|
|
24
16
|
function bn64(v) {
|
|
25
17
|
return v ?? 0n;
|
|
26
18
|
}
|
|
@@ -133,357 +125,9 @@ function parseSwap2Event(data, meta) {
|
|
|
133
125
|
};
|
|
134
126
|
return { MeteoraDammV2Swap: ev };
|
|
135
127
|
}
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
let o = 0;
|
|
140
|
-
const pool = (0, binary_js_1.readPubkey)(data, o);
|
|
141
|
-
if (!pool)
|
|
142
|
-
return null;
|
|
143
|
-
o += 32;
|
|
144
|
-
const owner = (0, binary_js_1.readPubkey)(data, o);
|
|
145
|
-
if (!owner)
|
|
146
|
-
return null;
|
|
147
|
-
o += 32;
|
|
148
|
-
const position = (0, binary_js_1.readPubkey)(data, o);
|
|
149
|
-
if (!position)
|
|
150
|
-
return null;
|
|
151
|
-
o += 32;
|
|
152
|
-
const position_nft_mint = (0, binary_js_1.readPubkey)(data, o);
|
|
153
|
-
if (!position_nft_mint)
|
|
154
|
-
return null;
|
|
155
|
-
return { MeteoraDammV2CreatePosition: { metadata: meta, pool, owner, position, position_nft_mint } };
|
|
156
|
-
}
|
|
157
|
-
function parseClosePositionEvent(data, meta) {
|
|
158
|
-
if (data.length < 32 * 4)
|
|
159
|
-
return null;
|
|
160
|
-
let o = 0;
|
|
161
|
-
const pool = (0, binary_js_1.readPubkey)(data, o);
|
|
162
|
-
if (!pool)
|
|
163
|
-
return null;
|
|
164
|
-
o += 32;
|
|
165
|
-
const owner = (0, binary_js_1.readPubkey)(data, o);
|
|
166
|
-
if (!owner)
|
|
167
|
-
return null;
|
|
168
|
-
o += 32;
|
|
169
|
-
const position = (0, binary_js_1.readPubkey)(data, o);
|
|
170
|
-
if (!position)
|
|
171
|
-
return null;
|
|
172
|
-
o += 32;
|
|
173
|
-
const position_nft_mint = (0, binary_js_1.readPubkey)(data, o);
|
|
174
|
-
if (!position_nft_mint)
|
|
175
|
-
return null;
|
|
176
|
-
return { MeteoraDammV2ClosePosition: { metadata: meta, pool, owner, position, position_nft_mint } };
|
|
177
|
-
}
|
|
178
|
-
function parseAddLiquidityEvent(data, meta) {
|
|
179
|
-
if (data.length < 32 * 3 + 16 + 8 * 6)
|
|
180
|
-
return null;
|
|
181
|
-
let o = 0;
|
|
182
|
-
const pool = (0, binary_js_1.readPubkey)(data, o);
|
|
183
|
-
if (!pool)
|
|
184
|
-
return null;
|
|
185
|
-
o += 32;
|
|
186
|
-
const position = (0, binary_js_1.readPubkey)(data, o);
|
|
187
|
-
if (!position)
|
|
188
|
-
return null;
|
|
189
|
-
o += 32;
|
|
190
|
-
const owner = (0, binary_js_1.readPubkey)(data, o);
|
|
191
|
-
if (!owner)
|
|
192
|
-
return null;
|
|
193
|
-
o += 32;
|
|
194
|
-
const liquidity_delta = (0, binary_js_1.readU128LE)(data, o);
|
|
195
|
-
if (liquidity_delta === null)
|
|
196
|
-
return null;
|
|
197
|
-
o += 16;
|
|
198
|
-
const token_a_amount_threshold = (0, binary_js_1.readU64LE)(data, o);
|
|
199
|
-
if (token_a_amount_threshold === null)
|
|
200
|
-
return null;
|
|
201
|
-
o += 8;
|
|
202
|
-
const token_b_amount_threshold = (0, binary_js_1.readU64LE)(data, o);
|
|
203
|
-
if (token_b_amount_threshold === null)
|
|
204
|
-
return null;
|
|
205
|
-
o += 8;
|
|
206
|
-
const token_a_amount = (0, binary_js_1.readU64LE)(data, o);
|
|
207
|
-
if (token_a_amount === null)
|
|
208
|
-
return null;
|
|
209
|
-
o += 8;
|
|
210
|
-
const token_b_amount = (0, binary_js_1.readU64LE)(data, o);
|
|
211
|
-
if (token_b_amount === null)
|
|
212
|
-
return null;
|
|
213
|
-
o += 8;
|
|
214
|
-
const total_amount_a = (0, binary_js_1.readU64LE)(data, o);
|
|
215
|
-
if (total_amount_a === null)
|
|
216
|
-
return null;
|
|
217
|
-
o += 8;
|
|
218
|
-
const total_amount_b = (0, binary_js_1.readU64LE)(data, o);
|
|
219
|
-
if (total_amount_b === null)
|
|
220
|
-
return null;
|
|
221
|
-
return {
|
|
222
|
-
MeteoraDammV2AddLiquidity: {
|
|
223
|
-
metadata: meta,
|
|
224
|
-
pool,
|
|
225
|
-
position,
|
|
226
|
-
owner,
|
|
227
|
-
liquidity_delta,
|
|
228
|
-
token_a_amount_threshold,
|
|
229
|
-
token_b_amount_threshold,
|
|
230
|
-
token_a_amount,
|
|
231
|
-
token_b_amount,
|
|
232
|
-
total_amount_a,
|
|
233
|
-
total_amount_b,
|
|
234
|
-
},
|
|
235
|
-
};
|
|
236
|
-
}
|
|
237
|
-
function parseRemoveLiquidityEvent(data, meta) {
|
|
238
|
-
if (data.length < 32 * 3 + 16 + 8 * 4)
|
|
239
|
-
return null;
|
|
240
|
-
let o = 0;
|
|
241
|
-
const pool = (0, binary_js_1.readPubkey)(data, o);
|
|
242
|
-
if (!pool)
|
|
243
|
-
return null;
|
|
244
|
-
o += 32;
|
|
245
|
-
const position = (0, binary_js_1.readPubkey)(data, o);
|
|
246
|
-
if (!position)
|
|
247
|
-
return null;
|
|
248
|
-
o += 32;
|
|
249
|
-
const owner = (0, binary_js_1.readPubkey)(data, o);
|
|
250
|
-
if (!owner)
|
|
251
|
-
return null;
|
|
252
|
-
o += 32;
|
|
253
|
-
const liquidity_delta = (0, binary_js_1.readU128LE)(data, o);
|
|
254
|
-
if (liquidity_delta === null)
|
|
255
|
-
return null;
|
|
256
|
-
o += 16;
|
|
257
|
-
const token_a_amount_threshold = (0, binary_js_1.readU64LE)(data, o);
|
|
258
|
-
if (token_a_amount_threshold === null)
|
|
259
|
-
return null;
|
|
260
|
-
o += 8;
|
|
261
|
-
const token_b_amount_threshold = (0, binary_js_1.readU64LE)(data, o);
|
|
262
|
-
if (token_b_amount_threshold === null)
|
|
263
|
-
return null;
|
|
264
|
-
o += 8;
|
|
265
|
-
const token_a_amount = (0, binary_js_1.readU64LE)(data, o);
|
|
266
|
-
if (token_a_amount === null)
|
|
267
|
-
return null;
|
|
268
|
-
o += 8;
|
|
269
|
-
const token_b_amount = (0, binary_js_1.readU64LE)(data, o);
|
|
270
|
-
if (token_b_amount === null)
|
|
271
|
-
return null;
|
|
272
|
-
return {
|
|
273
|
-
MeteoraDammV2RemoveLiquidity: {
|
|
274
|
-
metadata: meta,
|
|
275
|
-
pool,
|
|
276
|
-
position,
|
|
277
|
-
owner,
|
|
278
|
-
liquidity_delta,
|
|
279
|
-
token_a_amount_threshold,
|
|
280
|
-
token_b_amount_threshold,
|
|
281
|
-
token_a_amount,
|
|
282
|
-
token_b_amount,
|
|
283
|
-
},
|
|
284
|
-
};
|
|
285
|
-
}
|
|
286
|
-
function bytesToHex(u8, start, len) {
|
|
287
|
-
let out = "";
|
|
288
|
-
for (let i = 0; i < len; i++)
|
|
289
|
-
out += u8[start + i].toString(16).padStart(2, "0");
|
|
290
|
-
return out;
|
|
291
|
-
}
|
|
292
|
-
function parseDynamicFeeParameters(data, start) {
|
|
293
|
-
let o = start;
|
|
294
|
-
const bin_step = (0, binary_js_1.readU16LE)(data, o);
|
|
295
|
-
if (bin_step === null)
|
|
296
|
-
return null;
|
|
297
|
-
o += 2;
|
|
298
|
-
const bin_step_u128 = (0, binary_js_1.readU128LE)(data, o);
|
|
299
|
-
if (bin_step_u128 === null)
|
|
300
|
-
return null;
|
|
301
|
-
o += 16;
|
|
302
|
-
const filter_period = (0, binary_js_1.readU16LE)(data, o);
|
|
303
|
-
if (filter_period === null)
|
|
304
|
-
return null;
|
|
305
|
-
o += 2;
|
|
306
|
-
const decay_period = (0, binary_js_1.readU16LE)(data, o);
|
|
307
|
-
if (decay_period === null)
|
|
308
|
-
return null;
|
|
309
|
-
o += 2;
|
|
310
|
-
const reduction_factor = (0, binary_js_1.readU16LE)(data, o);
|
|
311
|
-
if (reduction_factor === null)
|
|
312
|
-
return null;
|
|
313
|
-
o += 2;
|
|
314
|
-
const max_volatility_accumulator = (0, binary_js_1.readU32LE)(data, o);
|
|
315
|
-
if (max_volatility_accumulator === null)
|
|
316
|
-
return null;
|
|
317
|
-
o += 4;
|
|
318
|
-
const variable_fee_control = (0, binary_js_1.readU32LE)(data, o);
|
|
319
|
-
if (variable_fee_control === null)
|
|
320
|
-
return null;
|
|
321
|
-
o += 4;
|
|
322
|
-
return {
|
|
323
|
-
v: {
|
|
324
|
-
bin_step,
|
|
325
|
-
bin_step_u128,
|
|
326
|
-
filter_period,
|
|
327
|
-
decay_period,
|
|
328
|
-
reduction_factor,
|
|
329
|
-
max_volatility_accumulator,
|
|
330
|
-
variable_fee_control,
|
|
331
|
-
},
|
|
332
|
-
next: o,
|
|
333
|
-
};
|
|
334
|
-
}
|
|
335
|
-
function parsePoolFeeParametersParsed(data, start) {
|
|
336
|
-
let o = start;
|
|
337
|
-
if (o + 30 > data.length)
|
|
338
|
-
return null;
|
|
339
|
-
const base_fee_data = bytesToHex(data, o, 27);
|
|
340
|
-
o += 27;
|
|
341
|
-
const compounding_fee_bps = (0, binary_js_1.readU16LE)(data, o);
|
|
342
|
-
if (compounding_fee_bps === null)
|
|
343
|
-
return null;
|
|
344
|
-
o += 2;
|
|
345
|
-
const padding = (0, binary_js_1.readU8)(data, o);
|
|
346
|
-
if (padding === null)
|
|
347
|
-
return null;
|
|
348
|
-
o += 1;
|
|
349
|
-
const tag = (0, binary_js_1.readU8)(data, o);
|
|
350
|
-
if (tag === null)
|
|
351
|
-
return null;
|
|
352
|
-
o += 1;
|
|
353
|
-
let dynamic_fee = null;
|
|
354
|
-
if (tag === 1) {
|
|
355
|
-
const inner = parseDynamicFeeParameters(data, o);
|
|
356
|
-
if (!inner)
|
|
357
|
-
return null;
|
|
358
|
-
dynamic_fee = inner.v;
|
|
359
|
-
o = inner.next;
|
|
360
|
-
}
|
|
361
|
-
else if (tag !== 0)
|
|
362
|
-
return null;
|
|
363
|
-
return {
|
|
364
|
-
v: { base_fee_data, compounding_fee_bps, padding, dynamic_fee },
|
|
365
|
-
next: o,
|
|
366
|
-
};
|
|
367
|
-
}
|
|
368
|
-
/** `EvtInitializePool`:6 个 pubkey 后接 `PoolFeeParameters`(变长 Option)再接固定尾部 109 字节 */
|
|
369
|
-
function parseInitializePoolEvent(data, meta) {
|
|
370
|
-
const minTail = 31 + 109;
|
|
371
|
-
if (data.length < 32 * 6 + minTail)
|
|
372
|
-
return null;
|
|
373
|
-
let o = 0;
|
|
374
|
-
const pool = (0, binary_js_1.readPubkey)(data, o);
|
|
375
|
-
if (!pool)
|
|
376
|
-
return null;
|
|
377
|
-
o += 32;
|
|
378
|
-
const token_a_mint = (0, binary_js_1.readPubkey)(data, o);
|
|
379
|
-
if (!token_a_mint)
|
|
380
|
-
return null;
|
|
381
|
-
o += 32;
|
|
382
|
-
const token_b_mint = (0, binary_js_1.readPubkey)(data, o);
|
|
383
|
-
if (!token_b_mint)
|
|
384
|
-
return null;
|
|
385
|
-
o += 32;
|
|
386
|
-
const creator = (0, binary_js_1.readPubkey)(data, o);
|
|
387
|
-
if (!creator)
|
|
388
|
-
return null;
|
|
389
|
-
o += 32;
|
|
390
|
-
const payer = (0, binary_js_1.readPubkey)(data, o);
|
|
391
|
-
if (!payer)
|
|
392
|
-
return null;
|
|
393
|
-
o += 32;
|
|
394
|
-
const alpha_vault = (0, binary_js_1.readPubkey)(data, o);
|
|
395
|
-
if (!alpha_vault)
|
|
396
|
-
return null;
|
|
397
|
-
o += 32;
|
|
398
|
-
const pfp = parsePoolFeeParametersParsed(data, o);
|
|
399
|
-
if (!pfp)
|
|
400
|
-
return null;
|
|
401
|
-
o = pfp.next;
|
|
402
|
-
if (o + 109 > data.length)
|
|
403
|
-
return null;
|
|
404
|
-
const sqrt_min_price = (0, binary_js_1.readU128LE)(data, o);
|
|
405
|
-
if (sqrt_min_price === null)
|
|
406
|
-
return null;
|
|
407
|
-
o += 16;
|
|
408
|
-
const sqrt_max_price = (0, binary_js_1.readU128LE)(data, o);
|
|
409
|
-
if (sqrt_max_price === null)
|
|
410
|
-
return null;
|
|
411
|
-
o += 16;
|
|
412
|
-
const activation_type = (0, binary_js_1.readU8)(data, o);
|
|
413
|
-
if (activation_type === null)
|
|
414
|
-
return null;
|
|
415
|
-
o += 1;
|
|
416
|
-
const collect_fee_mode = (0, binary_js_1.readU8)(data, o);
|
|
417
|
-
if (collect_fee_mode === null)
|
|
418
|
-
return null;
|
|
419
|
-
o += 1;
|
|
420
|
-
const liquidity = (0, binary_js_1.readU128LE)(data, o);
|
|
421
|
-
if (liquidity === null)
|
|
422
|
-
return null;
|
|
423
|
-
o += 16;
|
|
424
|
-
const sqrt_price = (0, binary_js_1.readU128LE)(data, o);
|
|
425
|
-
if (sqrt_price === null)
|
|
426
|
-
return null;
|
|
427
|
-
o += 16;
|
|
428
|
-
const activation_point = (0, binary_js_1.readU64LE)(data, o);
|
|
429
|
-
if (activation_point === null)
|
|
430
|
-
return null;
|
|
431
|
-
o += 8;
|
|
432
|
-
const token_a_flag = (0, binary_js_1.readU8)(data, o);
|
|
433
|
-
if (token_a_flag === null)
|
|
434
|
-
return null;
|
|
435
|
-
o += 1;
|
|
436
|
-
const token_b_flag = (0, binary_js_1.readU8)(data, o);
|
|
437
|
-
if (token_b_flag === null)
|
|
438
|
-
return null;
|
|
439
|
-
o += 1;
|
|
440
|
-
const token_a_amount = (0, binary_js_1.readU64LE)(data, o);
|
|
441
|
-
if (token_a_amount === null)
|
|
442
|
-
return null;
|
|
443
|
-
o += 8;
|
|
444
|
-
const token_b_amount = (0, binary_js_1.readU64LE)(data, o);
|
|
445
|
-
if (token_b_amount === null)
|
|
446
|
-
return null;
|
|
447
|
-
o += 8;
|
|
448
|
-
const total_amount_a = (0, binary_js_1.readU64LE)(data, o);
|
|
449
|
-
if (total_amount_a === null)
|
|
450
|
-
return null;
|
|
451
|
-
o += 8;
|
|
452
|
-
const total_amount_b = (0, binary_js_1.readU64LE)(data, o);
|
|
453
|
-
if (total_amount_b === null)
|
|
454
|
-
return null;
|
|
455
|
-
o += 8;
|
|
456
|
-
const pool_type = (0, binary_js_1.readU8)(data, o);
|
|
457
|
-
if (pool_type === null)
|
|
458
|
-
return null;
|
|
459
|
-
return {
|
|
460
|
-
MeteoraDammV2InitializePool: {
|
|
461
|
-
metadata: meta,
|
|
462
|
-
pool,
|
|
463
|
-
token_a_mint,
|
|
464
|
-
token_b_mint,
|
|
465
|
-
creator,
|
|
466
|
-
payer,
|
|
467
|
-
alpha_vault,
|
|
468
|
-
pool_fees: pfp.v,
|
|
469
|
-
sqrt_min_price,
|
|
470
|
-
sqrt_max_price,
|
|
471
|
-
activation_type,
|
|
472
|
-
collect_fee_mode,
|
|
473
|
-
liquidity,
|
|
474
|
-
sqrt_price,
|
|
475
|
-
activation_point,
|
|
476
|
-
token_a_flag,
|
|
477
|
-
token_b_flag,
|
|
478
|
-
token_a_amount,
|
|
479
|
-
token_b_amount,
|
|
480
|
-
total_amount_a,
|
|
481
|
-
total_amount_b,
|
|
482
|
-
pool_type,
|
|
483
|
-
},
|
|
484
|
-
};
|
|
485
|
-
}
|
|
486
|
-
/** Meteora DAMM Program data 入口;布局与 `parseMeteoraDammInstruction` CPI 内层载荷一致(Swap/Swap2 另含日志侧 32 字节填充,见 parseSwapEvent) */
|
|
128
|
+
/**
|
|
129
|
+
* Meteora DAMM Program data 入口;与 Rust `logs/meteora_damm.rs` 一致,仅解析 Swap / Swap2(其余 discriminator 不产出事件)。
|
|
130
|
+
*/
|
|
487
131
|
function parseMeteoraDammLog(log, signature, slot, txIndex, blockTimeUs, grpcRecvUs) {
|
|
488
132
|
const programData = (0, program_data_js_1.decodeProgramDataLine)(log);
|
|
489
133
|
if (!programData)
|
|
@@ -495,15 +139,5 @@ function parseMeteoraDammLog(log, signature, slot, txIndex, blockTimeUs, grpcRec
|
|
|
495
139
|
return parseSwapEvent(data, meta);
|
|
496
140
|
if (disc === SWAP2)
|
|
497
141
|
return parseSwap2Event(data, meta);
|
|
498
|
-
if (disc === CREATE_POSITION)
|
|
499
|
-
return parseCreatePositionEvent(data, meta);
|
|
500
|
-
if (disc === CLOSE_POSITION)
|
|
501
|
-
return parseClosePositionEvent(data, meta);
|
|
502
|
-
if (disc === ADD_LIQUIDITY)
|
|
503
|
-
return parseAddLiquidityEvent(data, meta);
|
|
504
|
-
if (disc === REMOVE_LIQUIDITY)
|
|
505
|
-
return parseRemoveLiquidityEvent(data, meta);
|
|
506
|
-
if (disc === INITIALIZE_POOL)
|
|
507
|
-
return parseInitializePoolEvent(data, meta);
|
|
508
142
|
return null;
|
|
509
143
|
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { DexEvent } from "../core/dex_event.js";
|
|
2
2
|
import type { EventTypeFilter } from "../grpc/types.js";
|
|
3
3
|
export declare function parseLogOptimized(log: string, signature: string, slot: number, txIndex: number, blockTimeUs: number | undefined, grpcRecvUs: number, eventTypeFilter: EventTypeFilter | undefined, isCreatedBuy: boolean, recentBlockhash?: Uint8Array): DexEvent | null;
|
|
4
|
-
/**
|
|
5
|
-
export declare function parseLogUnified(log: string, signature: string, slot: number, blockTimeUs: number | undefined): DexEvent | null;
|
|
4
|
+
/** 单行日志统一解析;`txIndex` 与 Rust gRPC `parse_logs(..., tx_idx, ...)` / `info.index` 对齐。 */
|
|
5
|
+
export declare function parseLogUnified(log: string, signature: string, slot: number, blockTimeUs: number | undefined, txIndex?: number): DexEvent | null;
|
|
@@ -187,8 +187,8 @@ function parseLogOptimized(log, signature, slot, txIndex, blockTimeUs, grpcRecvU
|
|
|
187
187
|
}
|
|
188
188
|
}
|
|
189
189
|
}
|
|
190
|
-
/**
|
|
191
|
-
function parseLogUnified(log, signature, slot, blockTimeUs) {
|
|
190
|
+
/** 单行日志统一解析;`txIndex` 与 Rust gRPC `parse_logs(..., tx_idx, ...)` / `info.index` 对齐。 */
|
|
191
|
+
function parseLogUnified(log, signature, slot, blockTimeUs, txIndex = 0) {
|
|
192
192
|
const grpcRecvUs = (0, clock_js_1.nowUs)();
|
|
193
|
-
return parseLogOptimized(log, signature, slot,
|
|
193
|
+
return parseLogOptimized(log, signature, slot, txIndex, blockTimeUs, grpcRecvUs, undefined, false, undefined);
|
|
194
194
|
}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Rust `logs` 模块中 `parse_*_log` / `parse_log_*` 命名风格的别名(与现有 TS 实现对应)。
|
|
3
|
+
*/
|
|
4
|
+
export { parseMeteoraDammLog as parse_meteora_damm_log } from "./meteora_damm.js";
|
|
5
|
+
export { parseMeteoraDlmmLog as parse_meteora_dlmm_log } from "./meteora_dlmm.js";
|
|
6
|
+
export { parseLogOptimized as parse_log_optimized, parseLogUnified as parse_log_unified } from "./optimized_matcher.js";
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.parse_log_unified = exports.parse_log_optimized = exports.parse_meteora_dlmm_log = exports.parse_meteora_damm_log = void 0;
|
|
4
|
+
/**
|
|
5
|
+
* Rust `logs` 模块中 `parse_*_log` / `parse_log_*` 命名风格的别名(与现有 TS 实现对应)。
|
|
6
|
+
*/
|
|
7
|
+
var meteora_damm_js_1 = require("./meteora_damm.js");
|
|
8
|
+
Object.defineProperty(exports, "parse_meteora_damm_log", { enumerable: true, get: function () { return meteora_damm_js_1.parseMeteoraDammLog; } });
|
|
9
|
+
var meteora_dlmm_js_1 = require("./meteora_dlmm.js");
|
|
10
|
+
Object.defineProperty(exports, "parse_meteora_dlmm_log", { enumerable: true, get: function () { return meteora_dlmm_js_1.parseMeteoraDlmmLog; } });
|
|
11
|
+
var optimized_matcher_js_1 = require("./optimized_matcher.js");
|
|
12
|
+
Object.defineProperty(exports, "parse_log_optimized", { enumerable: true, get: function () { return optimized_matcher_js_1.parseLogOptimized; } });
|
|
13
|
+
Object.defineProperty(exports, "parse_log_unified", { enumerable: true, get: function () { return optimized_matcher_js_1.parseLogUnified; } });
|
package/dist/rpc_parser.d.ts
CHANGED
|
@@ -3,6 +3,7 @@ import type { DexEvent } from "./core/dex_event.js";
|
|
|
3
3
|
import type { EventTypeFilter } from "./grpc/types.js";
|
|
4
4
|
import type { ParseError } from "./core/error.js";
|
|
5
5
|
export { parseRpcTransaction, fillAccountsFromTransactionDataRpc, fillDataRpc, applyAccountFillsToLogEvents, } from "./rpc_transaction.js";
|
|
6
|
+
export { convertRpcToGrpc, convert_rpc_to_grpc, type ConvertRpcToGrpcOk, type ConvertRpcToGrpcErr, } from "./grpc/rpc_to_grpc.js";
|
|
6
7
|
/**
|
|
7
8
|
* 通过 RPC 拉取交易并解析:
|
|
8
9
|
* 外层 + 内层编译指令 + 日志;不含账户字段填充(需另行调用 RPC 填充 API)。
|
package/dist/rpc_parser.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.applyAccountFillsToLogEvents = exports.fillDataRpc = exports.fillAccountsFromTransactionDataRpc = exports.parseRpcTransaction = void 0;
|
|
3
|
+
exports.convert_rpc_to_grpc = exports.convertRpcToGrpc = exports.applyAccountFillsToLogEvents = exports.fillDataRpc = exports.fillAccountsFromTransactionDataRpc = exports.parseRpcTransaction = void 0;
|
|
4
4
|
exports.parseTransactionFromRpc = parseTransactionFromRpc;
|
|
5
5
|
const rpc_transaction_js_1 = require("./rpc_transaction.js");
|
|
6
6
|
var rpc_transaction_js_2 = require("./rpc_transaction.js");
|
|
@@ -8,6 +8,9 @@ Object.defineProperty(exports, "parseRpcTransaction", { enumerable: true, get: f
|
|
|
8
8
|
Object.defineProperty(exports, "fillAccountsFromTransactionDataRpc", { enumerable: true, get: function () { return rpc_transaction_js_2.fillAccountsFromTransactionDataRpc; } });
|
|
9
9
|
Object.defineProperty(exports, "fillDataRpc", { enumerable: true, get: function () { return rpc_transaction_js_2.fillDataRpc; } });
|
|
10
10
|
Object.defineProperty(exports, "applyAccountFillsToLogEvents", { enumerable: true, get: function () { return rpc_transaction_js_2.applyAccountFillsToLogEvents; } });
|
|
11
|
+
var rpc_to_grpc_js_1 = require("./grpc/rpc_to_grpc.js");
|
|
12
|
+
Object.defineProperty(exports, "convertRpcToGrpc", { enumerable: true, get: function () { return rpc_to_grpc_js_1.convertRpcToGrpc; } });
|
|
13
|
+
Object.defineProperty(exports, "convert_rpc_to_grpc", { enumerable: true, get: function () { return rpc_to_grpc_js_1.convert_rpc_to_grpc; } });
|
|
11
14
|
/**
|
|
12
15
|
* 通过 RPC 拉取交易并解析:
|
|
13
16
|
* 外层 + 内层编译指令 + 日志;不含账户字段填充(需另行调用 RPC 填充 API)。
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* V0 交易:从链上拉取 ALT 账户,拼出与运行时一致的完整账户 key 列表(静态 + 查找表展开)。
|
|
3
|
+
*/
|
|
4
|
+
import { AddressLookupTableAccount, Connection } from "@solana/web3.js";
|
|
5
|
+
import type { ShredWasmTx } from "./instruction_parse.js";
|
|
6
|
+
/** 批量拉取 ALT,供同一 Entry 内多笔交易复用 */
|
|
7
|
+
export declare function loadAddressLookupTableAccounts(connection: Connection, lookupTableAddresses: readonly string[]): Promise<Map<string, AddressLookupTableAccount>>;
|
|
8
|
+
/** 使用已拉取的 ALT 构造完整账户表(base58);失败时退回仅静态表 */
|
|
9
|
+
export declare function fullAccountKeyStringsFromShredTx(tx: ShredWasmTx, altByAddress: Map<string, AddressLookupTableAccount>): string[];
|
|
@@ -0,0 +1,70 @@
|
|
|
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.loadAddressLookupTableAccounts = loadAddressLookupTableAccounts;
|
|
7
|
+
exports.fullAccountKeyStringsFromShredTx = fullAccountKeyStringsFromShredTx;
|
|
8
|
+
/**
|
|
9
|
+
* V0 交易:从链上拉取 ALT 账户,拼出与运行时一致的完整账户 key 列表(静态 + 查找表展开)。
|
|
10
|
+
*/
|
|
11
|
+
const web3_js_1 = require("@solana/web3.js");
|
|
12
|
+
const bs58_1 = __importDefault(require("bs58"));
|
|
13
|
+
/** 批量拉取 ALT,供同一 Entry 内多笔交易复用 */
|
|
14
|
+
async function loadAddressLookupTableAccounts(connection, lookupTableAddresses) {
|
|
15
|
+
const map = new Map();
|
|
16
|
+
if (lookupTableAddresses.length === 0)
|
|
17
|
+
return map;
|
|
18
|
+
const pks = lookupTableAddresses.map((s) => new web3_js_1.PublicKey(s));
|
|
19
|
+
const infos = await connection.getMultipleAccountsInfo(pks);
|
|
20
|
+
for (let i = 0; i < pks.length; i++) {
|
|
21
|
+
const info = infos[i];
|
|
22
|
+
if (!info?.data)
|
|
23
|
+
continue;
|
|
24
|
+
const state = web3_js_1.AddressLookupTableAccount.deserialize(info.data);
|
|
25
|
+
const alt = new web3_js_1.AddressLookupTableAccount({ key: pks[i], state });
|
|
26
|
+
map.set(pks[i].toBase58(), alt);
|
|
27
|
+
}
|
|
28
|
+
return map;
|
|
29
|
+
}
|
|
30
|
+
/** 使用已拉取的 ALT 构造完整账户表(base58);失败时退回仅静态表 */
|
|
31
|
+
function fullAccountKeyStringsFromShredTx(tx, altByAddress) {
|
|
32
|
+
if (tx.messageVersion !== "v0" || !tx.addressTableLookups?.length) {
|
|
33
|
+
return tx.accounts;
|
|
34
|
+
}
|
|
35
|
+
const lookups = tx.addressTableLookups;
|
|
36
|
+
const altAccounts = [];
|
|
37
|
+
for (const l of lookups) {
|
|
38
|
+
const alt = altByAddress.get(l.accountKey);
|
|
39
|
+
if (!alt) {
|
|
40
|
+
return tx.accounts;
|
|
41
|
+
}
|
|
42
|
+
altAccounts.push(alt);
|
|
43
|
+
}
|
|
44
|
+
const header = tx.header;
|
|
45
|
+
const rb = tx.recentBlockhash;
|
|
46
|
+
if (!header || !rb || rb.length !== 32) {
|
|
47
|
+
return tx.accounts;
|
|
48
|
+
}
|
|
49
|
+
const msg = new web3_js_1.MessageV0({
|
|
50
|
+
header,
|
|
51
|
+
staticAccountKeys: tx.accounts.map((s) => new web3_js_1.PublicKey(s)),
|
|
52
|
+
recentBlockhash: bs58_1.default.encode(rb),
|
|
53
|
+
compiledInstructions: (tx.instructions ?? []).map((ix) => ({
|
|
54
|
+
programIdIndex: ix.programIdIndex,
|
|
55
|
+
accountKeyIndexes: Array.from(ix.accounts),
|
|
56
|
+
data: Buffer.from(ix.data),
|
|
57
|
+
})),
|
|
58
|
+
addressTableLookups: lookups.map((l) => ({
|
|
59
|
+
accountKey: new web3_js_1.PublicKey(l.accountKey),
|
|
60
|
+
writableIndexes: Array.from(l.writableIndexes),
|
|
61
|
+
readonlyIndexes: Array.from(l.readonlyIndexes),
|
|
62
|
+
})),
|
|
63
|
+
});
|
|
64
|
+
const keys = msg.getAccountKeys({ addressLookupTableAccounts: altAccounts });
|
|
65
|
+
const out = [];
|
|
66
|
+
for (let i = 0; i < keys.length; i++) {
|
|
67
|
+
out.push(keys.get(i).toBase58());
|
|
68
|
+
}
|
|
69
|
+
return out;
|
|
70
|
+
}
|