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.
Files changed (87) hide show
  1. package/README.md +390 -0
  2. package/dist/chunk-MMQAMIKR.mjs +3735 -0
  3. package/dist/chunk-NEZDFAYA.mjs +7744 -0
  4. package/dist/clients-VITWK7B6.mjs +1370 -0
  5. package/dist/index-1BK_FXsW.d.mts +2327 -0
  6. package/dist/index-1BK_FXsW.d.ts +2327 -0
  7. package/dist/index.d.mts +2659 -0
  8. package/dist/index.d.ts +2659 -0
  9. package/dist/index.js +13265 -0
  10. package/dist/index.mjs +562 -0
  11. package/dist/perf/index.d.mts +2 -0
  12. package/dist/perf/index.d.ts +2 -0
  13. package/dist/perf/index.js +3742 -0
  14. package/dist/perf/index.mjs +214 -0
  15. package/package.json +101 -0
  16. package/src/__tests__/complete_sdk.test.ts +354 -0
  17. package/src/__tests__/hotpath.test.ts +486 -0
  18. package/src/__tests__/nonce.test.ts +45 -0
  19. package/src/__tests__/sdk.test.ts +425 -0
  20. package/src/address-lookup/index.ts +197 -0
  21. package/src/cache/cache.ts +308 -0
  22. package/src/calc/index.ts +1058 -0
  23. package/src/calc/pumpfun.ts +124 -0
  24. package/src/common/bonding_curve.ts +272 -0
  25. package/src/common/compute-budget.ts +148 -0
  26. package/src/common/confirm-any-signature.ts +184 -0
  27. package/src/common/fast-timing.ts +481 -0
  28. package/src/common/fast_fn.ts +150 -0
  29. package/src/common/gas-fee-strategy.ts +253 -0
  30. package/src/common/map-pool.ts +23 -0
  31. package/src/common/nonce.ts +40 -0
  32. package/src/common/sdk-log.ts +460 -0
  33. package/src/common/seed.ts +381 -0
  34. package/src/common/spl-token.ts +578 -0
  35. package/src/common/subscription-handle.ts +644 -0
  36. package/src/common/trading-utils.ts +239 -0
  37. package/src/common/wsol-manager.ts +325 -0
  38. package/src/compute/compute_budget_manager.ts +187 -0
  39. package/src/compute/index.ts +21 -0
  40. package/src/constants/index.ts +96 -0
  41. package/src/execution/execution.ts +532 -0
  42. package/src/execution/index.ts +42 -0
  43. package/src/hotpath/executor.ts +464 -0
  44. package/src/hotpath/index.ts +64 -0
  45. package/src/hotpath/state.ts +435 -0
  46. package/src/index.ts +2117 -0
  47. package/src/instruction/bonk_builder.ts +730 -0
  48. package/src/instruction/index.ts +24 -0
  49. package/src/instruction/meteora_damm_v2_builder.ts +509 -0
  50. package/src/instruction/pumpfun_builder.ts +1183 -0
  51. package/src/instruction/pumpswap.ts +1123 -0
  52. package/src/instruction/raydium_amm_v4_builder.ts +692 -0
  53. package/src/instruction/raydium_cpmm_builder.ts +795 -0
  54. package/src/middleware/traits.ts +407 -0
  55. package/src/params/index.ts +483 -0
  56. package/src/perf/compiler-optimization.ts +529 -0
  57. package/src/perf/hardware.ts +631 -0
  58. package/src/perf/index.ts +9 -0
  59. package/src/perf/kernel-bypass.ts +656 -0
  60. package/src/perf/protocol.ts +682 -0
  61. package/src/perf/realtime.ts +592 -0
  62. package/src/perf/simd.ts +668 -0
  63. package/src/perf/syscall-bypass.ts +331 -0
  64. package/src/perf/ultra-low-latency.ts +505 -0
  65. package/src/perf/zero-copy.ts +589 -0
  66. package/src/pool/pool.ts +294 -0
  67. package/src/rpc/client.ts +345 -0
  68. package/src/sdk-errors.ts +13 -0
  69. package/src/security/index.ts +26 -0
  70. package/src/security/secure-key.ts +303 -0
  71. package/src/security/validators.ts +281 -0
  72. package/src/seed/pda.ts +262 -0
  73. package/src/serialization/index.ts +28 -0
  74. package/src/serialization/serialization.ts +288 -0
  75. package/src/swqos/clients.ts +1754 -0
  76. package/src/swqos/index.ts +50 -0
  77. package/src/swqos/providers.ts +1707 -0
  78. package/src/trading/core/async-executor.ts +702 -0
  79. package/src/trading/core/confirmation-monitor.ts +711 -0
  80. package/src/trading/core/index.ts +82 -0
  81. package/src/trading/core/retry-handler.ts +683 -0
  82. package/src/trading/core/transaction-pool.ts +780 -0
  83. package/src/trading/executor.ts +385 -0
  84. package/src/trading/factory.ts +282 -0
  85. package/src/trading/index.ts +30 -0
  86. package/src/types.ts +8 -0
  87. 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
+ }