sol-trade-sdk 0.1.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/README.md +390 -0
- package/dist/chunk-MMQAMIKR.mjs +3735 -0
- package/dist/chunk-NEZDFAYA.mjs +7744 -0
- package/dist/clients-VITWK7B6.mjs +1370 -0
- package/dist/index-1BK_FXsW.d.mts +2327 -0
- package/dist/index-1BK_FXsW.d.ts +2327 -0
- package/dist/index.d.mts +2659 -0
- package/dist/index.d.ts +2659 -0
- package/dist/index.js +13265 -0
- package/dist/index.mjs +562 -0
- package/dist/perf/index.d.mts +2 -0
- package/dist/perf/index.d.ts +2 -0
- package/dist/perf/index.js +3742 -0
- package/dist/perf/index.mjs +214 -0
- package/package.json +101 -0
- package/src/__tests__/complete_sdk.test.ts +354 -0
- package/src/__tests__/hotpath.test.ts +486 -0
- package/src/__tests__/nonce.test.ts +45 -0
- package/src/__tests__/sdk.test.ts +425 -0
- package/src/address-lookup/index.ts +197 -0
- package/src/cache/cache.ts +308 -0
- package/src/calc/index.ts +1058 -0
- package/src/calc/pumpfun.ts +124 -0
- package/src/common/bonding_curve.ts +272 -0
- package/src/common/compute-budget.ts +148 -0
- package/src/common/confirm-any-signature.ts +184 -0
- package/src/common/fast-timing.ts +481 -0
- package/src/common/fast_fn.ts +150 -0
- package/src/common/gas-fee-strategy.ts +253 -0
- package/src/common/map-pool.ts +23 -0
- package/src/common/nonce.ts +40 -0
- package/src/common/sdk-log.ts +460 -0
- package/src/common/seed.ts +381 -0
- package/src/common/spl-token.ts +578 -0
- package/src/common/subscription-handle.ts +644 -0
- package/src/common/trading-utils.ts +239 -0
- package/src/common/wsol-manager.ts +325 -0
- package/src/compute/compute_budget_manager.ts +187 -0
- package/src/compute/index.ts +21 -0
- package/src/constants/index.ts +96 -0
- package/src/execution/execution.ts +532 -0
- package/src/execution/index.ts +42 -0
- package/src/hotpath/executor.ts +464 -0
- package/src/hotpath/index.ts +64 -0
- package/src/hotpath/state.ts +435 -0
- package/src/index.ts +2117 -0
- package/src/instruction/bonk_builder.ts +730 -0
- package/src/instruction/index.ts +24 -0
- package/src/instruction/meteora_damm_v2_builder.ts +509 -0
- package/src/instruction/pumpfun_builder.ts +1183 -0
- package/src/instruction/pumpswap.ts +1123 -0
- package/src/instruction/raydium_amm_v4_builder.ts +692 -0
- package/src/instruction/raydium_cpmm_builder.ts +795 -0
- package/src/middleware/traits.ts +407 -0
- package/src/params/index.ts +483 -0
- package/src/perf/compiler-optimization.ts +529 -0
- package/src/perf/hardware.ts +631 -0
- package/src/perf/index.ts +9 -0
- package/src/perf/kernel-bypass.ts +656 -0
- package/src/perf/protocol.ts +682 -0
- package/src/perf/realtime.ts +592 -0
- package/src/perf/simd.ts +668 -0
- package/src/perf/syscall-bypass.ts +331 -0
- package/src/perf/ultra-low-latency.ts +505 -0
- package/src/perf/zero-copy.ts +589 -0
- package/src/pool/pool.ts +294 -0
- package/src/rpc/client.ts +345 -0
- package/src/sdk-errors.ts +13 -0
- package/src/security/index.ts +26 -0
- package/src/security/secure-key.ts +303 -0
- package/src/security/validators.ts +281 -0
- package/src/seed/pda.ts +262 -0
- package/src/serialization/index.ts +28 -0
- package/src/serialization/serialization.ts +288 -0
- package/src/swqos/clients.ts +1754 -0
- package/src/swqos/index.ts +50 -0
- package/src/swqos/providers.ts +1707 -0
- package/src/trading/core/async-executor.ts +702 -0
- package/src/trading/core/confirmation-monitor.ts +711 -0
- package/src/trading/core/index.ts +82 -0
- package/src/trading/core/retry-handler.ts +683 -0
- package/src/trading/core/transaction-pool.ts +780 -0
- package/src/trading/executor.ts +385 -0
- package/src/trading/factory.ts +282 -0
- package/src/trading/index.ts +30 -0
- package/src/types.ts +8 -0
- package/src/utils/index.ts +155 -0
|
@@ -0,0 +1,435 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Hot Path State Management for Sol Trade SDK
|
|
3
|
+
*
|
|
4
|
+
* Manages prefetched blockchain state for zero-latency trading execution.
|
|
5
|
+
* NO RPC calls are made during trading - all data is prefetched.
|
|
6
|
+
*
|
|
7
|
+
* Key principle: Prepare everything before the trade, execute with minimal latency.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import { Connection, PublicKey } from '@solana/web3.js';
|
|
11
|
+
|
|
12
|
+
// ===== Types =====
|
|
13
|
+
|
|
14
|
+
export interface HotPathConfig {
|
|
15
|
+
blockhashRefreshIntervalMs: number;
|
|
16
|
+
cacheTtlMs: number;
|
|
17
|
+
enablePrefetch: boolean;
|
|
18
|
+
prefetchTimeoutMs: number;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export function defaultHotPathConfig(): HotPathConfig {
|
|
22
|
+
return {
|
|
23
|
+
blockhashRefreshIntervalMs: 2000,
|
|
24
|
+
cacheTtlMs: 5000,
|
|
25
|
+
enablePrefetch: true,
|
|
26
|
+
prefetchTimeoutMs: 5000,
|
|
27
|
+
};
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export interface PrefetchedData {
|
|
31
|
+
blockhash: string;
|
|
32
|
+
lastValidBlockHeight: number;
|
|
33
|
+
slot: number;
|
|
34
|
+
fetchedAt: number; // timestamp
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export interface AccountState {
|
|
38
|
+
pubkey: string;
|
|
39
|
+
data: Buffer;
|
|
40
|
+
lamports: bigint;
|
|
41
|
+
owner: string;
|
|
42
|
+
executable: boolean;
|
|
43
|
+
rentEpoch: number;
|
|
44
|
+
slot: number;
|
|
45
|
+
fetchedAt: number;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
export interface PoolState {
|
|
49
|
+
poolAddress: string;
|
|
50
|
+
poolType: 'pumpfun' | 'pumpswap' | 'raydium' | 'meteora';
|
|
51
|
+
mintA: string;
|
|
52
|
+
mintB: string;
|
|
53
|
+
vaultA: string;
|
|
54
|
+
vaultB: string;
|
|
55
|
+
reserveA: bigint;
|
|
56
|
+
reserveB: bigint;
|
|
57
|
+
feeRate: number;
|
|
58
|
+
fetchedAt: number;
|
|
59
|
+
rawData?: Buffer;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
// ===== Helper Functions =====
|
|
63
|
+
|
|
64
|
+
function isDataFresh(fetchedAt: number, ttlMs: number): boolean {
|
|
65
|
+
return Date.now() - fetchedAt <= ttlMs;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
// ===== Hot Path State =====
|
|
69
|
+
|
|
70
|
+
export class HotPathState {
|
|
71
|
+
private config: HotPathConfig;
|
|
72
|
+
private connection: Connection;
|
|
73
|
+
|
|
74
|
+
// Prefetched data
|
|
75
|
+
private currentData: PrefetchedData | null = null;
|
|
76
|
+
private accounts: Map<string, AccountState> = new Map();
|
|
77
|
+
private pools: Map<string, PoolState> = new Map();
|
|
78
|
+
|
|
79
|
+
// Background prefetch control
|
|
80
|
+
private prefetchTimer?: ReturnType<typeof setInterval>;
|
|
81
|
+
private isRunning: boolean = false;
|
|
82
|
+
|
|
83
|
+
// Callbacks
|
|
84
|
+
private onBlockhashUpdateCallback?: (
|
|
85
|
+
blockhash: string,
|
|
86
|
+
lastValidBlockHeight: number
|
|
87
|
+
) => void;
|
|
88
|
+
|
|
89
|
+
// Metrics
|
|
90
|
+
private metrics = {
|
|
91
|
+
prefetchCount: 0,
|
|
92
|
+
prefetchErrors: 0,
|
|
93
|
+
lastPrefetchTime: 0,
|
|
94
|
+
};
|
|
95
|
+
|
|
96
|
+
constructor(connection: Connection, config?: Partial<HotPathConfig>) {
|
|
97
|
+
this.config = { ...defaultHotPathConfig(), ...config };
|
|
98
|
+
this.connection = connection;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
/**
|
|
102
|
+
* Start background prefetching
|
|
103
|
+
* Call this BEFORE any hot path execution
|
|
104
|
+
*/
|
|
105
|
+
async start(): Promise<void> {
|
|
106
|
+
if (!this.config.enablePrefetch) {
|
|
107
|
+
return;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
// Initial synchronous prefetch
|
|
111
|
+
await this.prefetchBlockhash();
|
|
112
|
+
|
|
113
|
+
// Start background loop
|
|
114
|
+
this.isRunning = true;
|
|
115
|
+
this.prefetchTimer = setInterval(
|
|
116
|
+
() => this.prefetchBlockhash().catch(() => {}),
|
|
117
|
+
this.config.blockhashRefreshIntervalMs
|
|
118
|
+
);
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
/**
|
|
122
|
+
* Stop background prefetching
|
|
123
|
+
*/
|
|
124
|
+
stop(): void {
|
|
125
|
+
this.isRunning = false;
|
|
126
|
+
if (this.prefetchTimer) {
|
|
127
|
+
clearInterval(this.prefetchTimer);
|
|
128
|
+
this.prefetchTimer = undefined;
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
/**
|
|
133
|
+
* Check if prefetching is active
|
|
134
|
+
*/
|
|
135
|
+
isActive(): boolean {
|
|
136
|
+
return this.isRunning;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
/**
|
|
140
|
+
* Prefetch latest blockhash - RPC call happens here (background only)
|
|
141
|
+
*/
|
|
142
|
+
private async prefetchBlockhash(): Promise<void> {
|
|
143
|
+
try {
|
|
144
|
+
const result = await Promise.race([
|
|
145
|
+
this.connection.getLatestBlockhash('processed'),
|
|
146
|
+
new Promise<never>((_, reject) =>
|
|
147
|
+
setTimeout(
|
|
148
|
+
() => reject(new Error('Timeout')),
|
|
149
|
+
this.config.prefetchTimeoutMs
|
|
150
|
+
)
|
|
151
|
+
),
|
|
152
|
+
]);
|
|
153
|
+
|
|
154
|
+
this.currentData = {
|
|
155
|
+
blockhash: result.blockhash,
|
|
156
|
+
lastValidBlockHeight: result.lastValidBlockHeight,
|
|
157
|
+
slot: 0, // Not directly available
|
|
158
|
+
fetchedAt: Date.now(),
|
|
159
|
+
};
|
|
160
|
+
|
|
161
|
+
this.metrics.prefetchCount++;
|
|
162
|
+
this.metrics.lastPrefetchTime = Date.now();
|
|
163
|
+
|
|
164
|
+
if (this.onBlockhashUpdateCallback) {
|
|
165
|
+
this.onBlockhashUpdateCallback(
|
|
166
|
+
this.currentData.blockhash,
|
|
167
|
+
this.currentData.lastValidBlockHeight
|
|
168
|
+
);
|
|
169
|
+
}
|
|
170
|
+
} catch (error) {
|
|
171
|
+
this.metrics.prefetchErrors++;
|
|
172
|
+
throw error;
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
/**
|
|
177
|
+
* Get current cached blockhash - NO RPC CALL
|
|
178
|
+
*/
|
|
179
|
+
getBlockhash(): { blockhash: string; lastValidBlockHeight: number } | null {
|
|
180
|
+
if (
|
|
181
|
+
!this.currentData ||
|
|
182
|
+
!isDataFresh(this.currentData.fetchedAt, this.config.cacheTtlMs)
|
|
183
|
+
) {
|
|
184
|
+
return null;
|
|
185
|
+
}
|
|
186
|
+
return {
|
|
187
|
+
blockhash: this.currentData.blockhash,
|
|
188
|
+
lastValidBlockHeight: this.currentData.lastValidBlockHeight,
|
|
189
|
+
};
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
/**
|
|
193
|
+
* Check if prefetched data is still valid
|
|
194
|
+
*/
|
|
195
|
+
isDataFresh(): boolean {
|
|
196
|
+
return (
|
|
197
|
+
this.currentData !== null &&
|
|
198
|
+
isDataFresh(this.currentData.fetchedAt, this.config.cacheTtlMs)
|
|
199
|
+
);
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
/**
|
|
203
|
+
* Get all prefetched data
|
|
204
|
+
*/
|
|
205
|
+
getPrefetchedData(): PrefetchedData | null {
|
|
206
|
+
return this.currentData;
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
/**
|
|
210
|
+
* Set callback for blockhash updates
|
|
211
|
+
*/
|
|
212
|
+
onBlockhashUpdate(
|
|
213
|
+
callback: (blockhash: string, lastValidBlockHeight: number) => void
|
|
214
|
+
): void {
|
|
215
|
+
this.onBlockhashUpdateCallback = callback;
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
// ===== Account State Management =====
|
|
219
|
+
|
|
220
|
+
/**
|
|
221
|
+
* Update account state in cache
|
|
222
|
+
*/
|
|
223
|
+
updateAccount(pubkey: string, state: AccountState): void {
|
|
224
|
+
this.accounts.set(pubkey, state);
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
/**
|
|
228
|
+
* Get account state from cache - NO RPC CALL
|
|
229
|
+
*/
|
|
230
|
+
getAccount(pubkey: string): AccountState | null {
|
|
231
|
+
const state = this.accounts.get(pubkey);
|
|
232
|
+
if (state && isDataFresh(state.fetchedAt, this.config.cacheTtlMs)) {
|
|
233
|
+
return state;
|
|
234
|
+
}
|
|
235
|
+
return null;
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
/**
|
|
239
|
+
* Get multiple account states - NO RPC CALL
|
|
240
|
+
*/
|
|
241
|
+
getAccounts(pubkeys: string[]): Map<string, AccountState> {
|
|
242
|
+
const result = new Map<string, AccountState>();
|
|
243
|
+
for (const pubkey of pubkeys) {
|
|
244
|
+
const state = this.getAccount(pubkey);
|
|
245
|
+
if (state) {
|
|
246
|
+
result.set(pubkey, state);
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
return result;
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
/**
|
|
253
|
+
* Prefetch accounts - RPC call happens here (before trading)
|
|
254
|
+
* Call this BEFORE entering the hot path
|
|
255
|
+
*/
|
|
256
|
+
async prefetchAccounts(pubkeys: string[]): Promise<void> {
|
|
257
|
+
if (pubkeys.length === 0) return;
|
|
258
|
+
|
|
259
|
+
try {
|
|
260
|
+
const keys = pubkeys.map((p) => new PublicKey(p));
|
|
261
|
+
const result = await Promise.race([
|
|
262
|
+
this.connection.getMultipleAccountsInfo(keys, 'processed'),
|
|
263
|
+
new Promise<never>((_, reject) =>
|
|
264
|
+
setTimeout(
|
|
265
|
+
() => reject(new Error('Timeout')),
|
|
266
|
+
this.config.prefetchTimeoutMs
|
|
267
|
+
)
|
|
268
|
+
),
|
|
269
|
+
]);
|
|
270
|
+
|
|
271
|
+
for (let i = 0; i < pubkeys.length; i++) {
|
|
272
|
+
const pubkey = pubkeys[i];
|
|
273
|
+
const account = result[i];
|
|
274
|
+
if (account && pubkey) {
|
|
275
|
+
this.updateAccount(pubkey, {
|
|
276
|
+
pubkey: pubkey,
|
|
277
|
+
data: account.data,
|
|
278
|
+
lamports: BigInt(account.lamports),
|
|
279
|
+
owner: account.owner.toBase58(),
|
|
280
|
+
executable: account.executable,
|
|
281
|
+
rentEpoch: account.rentEpoch ?? 0,
|
|
282
|
+
slot: 0,
|
|
283
|
+
fetchedAt: Date.now(),
|
|
284
|
+
});
|
|
285
|
+
}
|
|
286
|
+
}
|
|
287
|
+
} catch (error) {
|
|
288
|
+
this.metrics.prefetchErrors++;
|
|
289
|
+
throw error;
|
|
290
|
+
}
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
// ===== Pool State Management =====
|
|
294
|
+
|
|
295
|
+
/**
|
|
296
|
+
* Update pool state in cache
|
|
297
|
+
*/
|
|
298
|
+
updatePool(poolAddress: string, state: PoolState): void {
|
|
299
|
+
this.pools.set(poolAddress, state);
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
/**
|
|
303
|
+
* Get pool state from cache - NO RPC CALL
|
|
304
|
+
*/
|
|
305
|
+
getPool(poolAddress: string): PoolState | null {
|
|
306
|
+
const state = this.pools.get(poolAddress);
|
|
307
|
+
if (state && isDataFresh(state.fetchedAt, this.config.cacheTtlMs)) {
|
|
308
|
+
return state;
|
|
309
|
+
}
|
|
310
|
+
return null;
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
// ===== Metrics =====
|
|
314
|
+
|
|
315
|
+
getMetrics(): {
|
|
316
|
+
prefetchCount: number;
|
|
317
|
+
prefetchErrors: number;
|
|
318
|
+
lastPrefetchTime: number;
|
|
319
|
+
accountsCached: number;
|
|
320
|
+
poolsCached: number;
|
|
321
|
+
dataFresh: boolean;
|
|
322
|
+
} {
|
|
323
|
+
return {
|
|
324
|
+
...this.metrics,
|
|
325
|
+
accountsCached: this.accounts.size,
|
|
326
|
+
poolsCached: this.pools.size,
|
|
327
|
+
dataFresh: this.isDataFresh(),
|
|
328
|
+
};
|
|
329
|
+
}
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
// ===== Trading Context =====
|
|
333
|
+
|
|
334
|
+
/**
|
|
335
|
+
* Trading context with all prefetched data needed for a trade.
|
|
336
|
+
* Created BEFORE hot path execution, contains all necessary state.
|
|
337
|
+
* NO RPC calls during trade execution.
|
|
338
|
+
*/
|
|
339
|
+
export class TradingContext {
|
|
340
|
+
public readonly payer: string;
|
|
341
|
+
public readonly blockhash: string;
|
|
342
|
+
public readonly lastValidBlockHeight: number;
|
|
343
|
+
public readonly createdAt: number;
|
|
344
|
+
|
|
345
|
+
public readonly accountStates: Map<string, AccountState> = new Map();
|
|
346
|
+
public readonly poolStates: Map<string, PoolState> = new Map();
|
|
347
|
+
|
|
348
|
+
constructor(hotPathState: HotPathState, payer: string) {
|
|
349
|
+
this.payer = payer;
|
|
350
|
+
this.createdAt = Date.now();
|
|
351
|
+
|
|
352
|
+
const blockhashData = hotPathState.getBlockhash();
|
|
353
|
+
if (!blockhashData) {
|
|
354
|
+
throw new StaleBlockhashError('Stale or missing blockhash - prefetch required');
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
this.blockhash = blockhashData.blockhash;
|
|
358
|
+
this.lastValidBlockHeight = blockhashData.lastValidBlockHeight;
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
/**
|
|
362
|
+
* Add account state from cache
|
|
363
|
+
*/
|
|
364
|
+
addAccount(pubkey: string, hotPathState: HotPathState): boolean {
|
|
365
|
+
const state = hotPathState.getAccount(pubkey);
|
|
366
|
+
if (state) {
|
|
367
|
+
this.accountStates.set(pubkey, state);
|
|
368
|
+
return true;
|
|
369
|
+
}
|
|
370
|
+
return false;
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
/**
|
|
374
|
+
* Add pool state from cache
|
|
375
|
+
*/
|
|
376
|
+
addPool(poolAddress: string, hotPathState: HotPathState): boolean {
|
|
377
|
+
const state = hotPathState.getPool(poolAddress);
|
|
378
|
+
if (state) {
|
|
379
|
+
this.poolStates.set(poolAddress, state);
|
|
380
|
+
return true;
|
|
381
|
+
}
|
|
382
|
+
return false;
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
/**
|
|
386
|
+
* Get age of context in milliseconds
|
|
387
|
+
*/
|
|
388
|
+
age(): number {
|
|
389
|
+
return Date.now() - this.createdAt;
|
|
390
|
+
}
|
|
391
|
+
|
|
392
|
+
/**
|
|
393
|
+
* Check if context is still valid
|
|
394
|
+
*/
|
|
395
|
+
isValid(maxAgeMs: number = 5000): boolean {
|
|
396
|
+
return this.age() <= maxAgeMs;
|
|
397
|
+
}
|
|
398
|
+
|
|
399
|
+
/**
|
|
400
|
+
* Get token account data from context
|
|
401
|
+
*/
|
|
402
|
+
getTokenAccountData(pubkey: string): Buffer | undefined {
|
|
403
|
+
return this.accountStates.get(pubkey)?.data;
|
|
404
|
+
}
|
|
405
|
+
}
|
|
406
|
+
|
|
407
|
+
// ===== Errors =====
|
|
408
|
+
|
|
409
|
+
export class HotPathError extends Error {
|
|
410
|
+
constructor(message: string) {
|
|
411
|
+
super(message);
|
|
412
|
+
this.name = 'HotPathError';
|
|
413
|
+
}
|
|
414
|
+
}
|
|
415
|
+
|
|
416
|
+
export class StaleBlockhashError extends HotPathError {
|
|
417
|
+
constructor(message: string = 'Blockhash is stale or not available') {
|
|
418
|
+
super(message);
|
|
419
|
+
this.name = 'StaleBlockhashError';
|
|
420
|
+
}
|
|
421
|
+
}
|
|
422
|
+
|
|
423
|
+
export class MissingAccountError extends HotPathError {
|
|
424
|
+
constructor(message: string = 'Required account not in cache') {
|
|
425
|
+
super(message);
|
|
426
|
+
this.name = 'MissingAccountError';
|
|
427
|
+
}
|
|
428
|
+
}
|
|
429
|
+
|
|
430
|
+
export class ContextExpiredError extends HotPathError {
|
|
431
|
+
constructor(message: string = 'Trading context has expired') {
|
|
432
|
+
super(message);
|
|
433
|
+
this.name = 'ContextExpiredError';
|
|
434
|
+
}
|
|
435
|
+
}
|