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,780 @@
1
+ /**
2
+ * Transaction Pool for Sol Trade SDK
3
+ * Manages a pool of pending transactions with priority-based processing.
4
+ */
5
+
6
+ import { Connection, Transaction, PublicKey } from '@solana/web3.js';
7
+ import { TradeError, SwqosType, TradeType } from '../../index';
8
+ import { SwqosClient } from '../../swqos/clients';
9
+ import { ExecutionResult, ExecutionStatus } from './async-executor';
10
+
11
+ // ===== Types =====
12
+
13
+ /**
14
+ * Transaction status in the pool
15
+ */
16
+ export enum TransactionStatus {
17
+ /** Transaction is queued and waiting */
18
+ Queued = 'Queued',
19
+ /** Transaction is being processed */
20
+ Processing = 'Processing',
21
+ /** Transaction submitted to network */
22
+ Submitted = 'Submitted',
23
+ /** Transaction confirmed on chain */
24
+ Confirmed = 'Confirmed',
25
+ /** Transaction failed */
26
+ Failed = 'Failed',
27
+ /** Transaction was dropped */
28
+ Dropped = 'Dropped',
29
+ /** Transaction expired */
30
+ Expired = 'Expired',
31
+ /** Transaction cancelled by user */
32
+ Cancelled = 'Cancelled',
33
+ }
34
+
35
+ /**
36
+ * Priority level for transactions
37
+ */
38
+ export enum PriorityLevel {
39
+ /** Critical priority - process immediately */
40
+ Critical = 0,
41
+ /** High priority - process before normal */
42
+ High = 1,
43
+ /** Normal priority */
44
+ Normal = 2,
45
+ /** Low priority - process when idle */
46
+ Low = 3,
47
+ /** Background priority - lowest */
48
+ Background = 4,
49
+ }
50
+
51
+ /**
52
+ * Configuration for the transaction pool
53
+ */
54
+ export interface PoolConfig {
55
+ /** Maximum number of concurrent transactions */
56
+ maxConcurrent: number;
57
+ /** Maximum queue size */
58
+ maxQueueSize: number;
59
+ /** Default priority for new transactions */
60
+ defaultPriority: PriorityLevel;
61
+ /** Whether to enable priority boosting */
62
+ enablePriorityBoost: boolean;
63
+ /** Time before priority boost in milliseconds */
64
+ priorityBoostDelayMs: number;
65
+ /** Maximum time in queue before expiration */
66
+ maxQueueTimeMs: number;
67
+ /** Polling interval for queue processing */
68
+ pollIntervalMs: number;
69
+ /** Whether to auto-start the pool */
70
+ autoStart: boolean;
71
+ /** Callback for transaction status changes */
72
+ onStatusChange?: (tx: PendingTransaction) => void;
73
+ /** Callback for pool statistics updates */
74
+ onStatsUpdate?: (stats: PoolStats) => void;
75
+ }
76
+
77
+ /**
78
+ * Pending transaction in the pool
79
+ */
80
+ export interface PendingTransaction {
81
+ /** Unique transaction ID */
82
+ id: string;
83
+ /** Transaction data */
84
+ transaction: Buffer;
85
+ /** Trade type */
86
+ tradeType: TradeType;
87
+ /** Associated SWQOS provider */
88
+ preferredProvider?: SwqosType;
89
+ /** Current status */
90
+ status: TransactionStatus;
91
+ /** Priority level */
92
+ priority: PriorityLevel;
93
+ /** Timestamp when added to pool */
94
+ queuedAt: number;
95
+ /** Timestamp when processing started */
96
+ processingAt?: number;
97
+ /** Timestamp when submitted */
98
+ submittedAt?: number;
99
+ /** Timestamp when confirmed */
100
+ confirmedAt?: number;
101
+ /** Transaction signature (when available) */
102
+ signature?: string;
103
+ /** Error message if failed */
104
+ error?: string;
105
+ /** Number of submission attempts */
106
+ attempts: number;
107
+ /** Maximum allowed attempts */
108
+ maxAttempts: number;
109
+ /** Associated accounts for conflict detection */
110
+ accounts: PublicKey[];
111
+ /** Custom metadata */
112
+ metadata?: Record<string, unknown>;
113
+ }
114
+
115
+ /**
116
+ * Pool statistics
117
+ */
118
+ export interface PoolStats {
119
+ /** Total transactions processed */
120
+ totalProcessed: number;
121
+ /** Total transactions successful */
122
+ totalSuccessful: number;
123
+ /** Total transactions failed */
124
+ totalFailed: number;
125
+ /** Current queue size */
126
+ queueSize: number;
127
+ /** Currently processing count */
128
+ processingCount: number;
129
+ /** Average processing time in milliseconds */
130
+ avgProcessingTimeMs: number;
131
+ /** Average wait time in milliseconds */
132
+ avgWaitTimeMs: number;
133
+ /** Transactions by status */
134
+ byStatus: Record<TransactionStatus, number>;
135
+ /** Current throughput (tx/sec) */
136
+ throughput: number;
137
+ }
138
+
139
+ /**
140
+ * Priority calculation result
141
+ */
142
+ export interface PriorityScore {
143
+ /** Calculated priority score (higher = more important) */
144
+ score: number;
145
+ /** Base priority level */
146
+ basePriority: PriorityLevel;
147
+ /** Age bonus (increases with wait time) */
148
+ ageBonus: number;
149
+ /** Custom adjustments */
150
+ adjustments: number;
151
+ }
152
+
153
+ // ===== Default Configurations =====
154
+
155
+ /**
156
+ * Get default pool configuration
157
+ */
158
+ export function defaultPoolConfig(): PoolConfig {
159
+ return {
160
+ maxConcurrent: 5,
161
+ maxQueueSize: 100,
162
+ defaultPriority: PriorityLevel.Normal,
163
+ enablePriorityBoost: true,
164
+ priorityBoostDelayMs: 5000,
165
+ maxQueueTimeMs: 60000,
166
+ pollIntervalMs: 100,
167
+ autoStart: true,
168
+ };
169
+ }
170
+
171
+ /**
172
+ * Get high-throughput pool configuration
173
+ */
174
+ export function highThroughputPoolConfig(): PoolConfig {
175
+ return {
176
+ maxConcurrent: 10,
177
+ maxQueueSize: 200,
178
+ defaultPriority: PriorityLevel.Normal,
179
+ enablePriorityBoost: true,
180
+ priorityBoostDelayMs: 2000,
181
+ maxQueueTimeMs: 30000,
182
+ pollIntervalMs: 50,
183
+ autoStart: true,
184
+ };
185
+ }
186
+
187
+ /**
188
+ * Get conservative pool configuration
189
+ */
190
+ export function conservativePoolConfig(): PoolConfig {
191
+ return {
192
+ maxConcurrent: 2,
193
+ maxQueueSize: 50,
194
+ defaultPriority: PriorityLevel.Normal,
195
+ enablePriorityBoost: false,
196
+ priorityBoostDelayMs: 10000,
197
+ maxQueueTimeMs: 120000,
198
+ pollIntervalMs: 200,
199
+ autoStart: true,
200
+ };
201
+ }
202
+
203
+ // ===== Priority Calculator =====
204
+
205
+ /**
206
+ * Calculates priority scores for transactions
207
+ */
208
+ export class PriorityCalculator {
209
+ private baseScores: Map<PriorityLevel, number> = new Map([
210
+ [PriorityLevel.Critical, 1000],
211
+ [PriorityLevel.High, 500],
212
+ [PriorityLevel.Normal, 100],
213
+ [PriorityLevel.Low, 50],
214
+ [PriorityLevel.Background, 10],
215
+ ]);
216
+
217
+ constructor(private config: PoolConfig) {}
218
+
219
+ /**
220
+ * Calculate priority score for a transaction
221
+ */
222
+ calculate(tx: PendingTransaction): PriorityScore {
223
+ const now = Date.now();
224
+ const ageMs = now - tx.queuedAt;
225
+ const baseScore = this.baseScores.get(tx.priority) || 100;
226
+
227
+ let ageBonus = 0;
228
+ if (this.config.enablePriorityBoost && ageMs > this.config.priorityBoostDelayMs) {
229
+ ageBonus = Math.min(
230
+ Math.floor((ageMs - this.config.priorityBoostDelayMs) / 1000) * 10,
231
+ 200 // Max age bonus
232
+ );
233
+ }
234
+
235
+ const adjustments = this.calculateAdjustments(tx);
236
+ const score = baseScore + ageBonus + adjustments;
237
+
238
+ return {
239
+ score,
240
+ basePriority: tx.priority,
241
+ ageBonus,
242
+ adjustments,
243
+ };
244
+ }
245
+
246
+ /**
247
+ * Compare two transactions by priority
248
+ */
249
+ compare(tx1: PendingTransaction, tx2: PendingTransaction): number {
250
+ const score1 = this.calculate(tx1).score;
251
+ const score2 = this.calculate(tx2).score;
252
+ return score2 - score1; // Higher score first
253
+ }
254
+
255
+ private calculateAdjustments(tx: PendingTransaction): number {
256
+ let adjustments = 0;
257
+
258
+ // Boost for retried transactions
259
+ if (tx.attempts > 0) {
260
+ adjustments += tx.attempts * 5;
261
+ }
262
+
263
+ // Boost for transactions nearing expiration
264
+ const ageMs = Date.now() - tx.queuedAt;
265
+ const timeRemaining = this.config.maxQueueTimeMs - ageMs;
266
+ if (timeRemaining < 10000) {
267
+ adjustments += 50;
268
+ }
269
+
270
+ return adjustments;
271
+ }
272
+ }
273
+
274
+ // ===== Transaction Pool =====
275
+
276
+ /**
277
+ * Manages a pool of transactions with priority-based processing
278
+ */
279
+ export class TransactionPool {
280
+ private queue: PendingTransaction[] = [];
281
+ private processing: Map<string, PendingTransaction> = new Map();
282
+ private completed: Map<string, PendingTransaction> = new Map();
283
+ private calculator: PriorityCalculator;
284
+ private clients: Map<SwqosType, SwqosClient> = new Map();
285
+ private connection: Connection;
286
+ private isRunning: boolean = false;
287
+ private pollInterval?: NodeJS.Timeout;
288
+ private stats: PoolStats;
289
+ private processingTimes: number[] = [];
290
+ private waitTimes: number[] = [];
291
+
292
+ constructor(
293
+ private rpcUrl: string,
294
+ private config: PoolConfig = defaultPoolConfig()
295
+ ) {
296
+ this.connection = new Connection(rpcUrl, 'confirmed');
297
+ this.calculator = new PriorityCalculator(config);
298
+ this.stats = this.initializeStats();
299
+
300
+ if (config.autoStart) {
301
+ this.start();
302
+ }
303
+ }
304
+
305
+ /**
306
+ * Add a client to the pool
307
+ */
308
+ addClient(client: SwqosClient): void {
309
+ this.clients.set(client.getSwqosType(), client);
310
+ }
311
+
312
+ /**
313
+ * Remove a client from the pool
314
+ */
315
+ removeClient(type: SwqosType): void {
316
+ this.clients.delete(type);
317
+ }
318
+
319
+ /**
320
+ * Submit a transaction to the pool
321
+ */
322
+ async submit(
323
+ transaction: Buffer,
324
+ tradeType: TradeType,
325
+ options: {
326
+ priority?: PriorityLevel;
327
+ preferredProvider?: SwqosType;
328
+ maxAttempts?: number;
329
+ accounts?: PublicKey[];
330
+ metadata?: Record<string, unknown>;
331
+ } = {}
332
+ ): Promise<string> {
333
+ if (this.queue.length >= this.config.maxQueueSize) {
334
+ throw new TradeError(429, 'Transaction pool queue is full');
335
+ }
336
+
337
+ const id = this.generateTransactionId();
338
+ const pendingTx: PendingTransaction = {
339
+ id,
340
+ transaction,
341
+ tradeType,
342
+ preferredProvider: options.preferredProvider,
343
+ status: TransactionStatus.Queued,
344
+ priority: options.priority ?? this.config.defaultPriority,
345
+ queuedAt: Date.now(),
346
+ attempts: 0,
347
+ maxAttempts: options.maxAttempts ?? 3,
348
+ accounts: options.accounts ?? [],
349
+ metadata: options.metadata,
350
+ };
351
+
352
+ this.queue.push(pendingTx);
353
+ this.sortQueue();
354
+ this.updateStats();
355
+
356
+ return id;
357
+ }
358
+
359
+ /**
360
+ * Get transaction by ID
361
+ */
362
+ getTransaction(id: string): PendingTransaction | undefined {
363
+ // Check queue
364
+ const queued = this.queue.find(tx => tx.id === id);
365
+ if (queued) return queued;
366
+
367
+ // Check processing
368
+ const processing = this.processing.get(id);
369
+ if (processing) return processing;
370
+
371
+ // Check completed
372
+ return this.completed.get(id);
373
+ }
374
+
375
+ /**
376
+ * Cancel a pending transaction
377
+ */
378
+ cancel(id: string): boolean {
379
+ // Try to remove from queue
380
+ const index = this.queue.findIndex(tx => tx.id === id);
381
+ if (index !== -1) {
382
+ const tx = this.queue.splice(index, 1)[0]!;
383
+ tx.status = TransactionStatus.Cancelled;
384
+ this.completed.set(id, tx);
385
+ this.notifyStatusChange(tx);
386
+ return true;
387
+ }
388
+
389
+ // Cannot cancel if already processing
390
+ return false;
391
+ }
392
+
393
+ /**
394
+ * Start the transaction pool processing
395
+ */
396
+ start(): void {
397
+ if (this.isRunning) return;
398
+
399
+ this.isRunning = true;
400
+ this.pollInterval = setInterval(() => {
401
+ this.processQueue();
402
+ }, this.config.pollIntervalMs);
403
+ }
404
+
405
+ /**
406
+ * Stop the transaction pool processing
407
+ */
408
+ stop(): void {
409
+ this.isRunning = false;
410
+ if (this.pollInterval) {
411
+ clearInterval(this.pollInterval);
412
+ this.pollInterval = undefined;
413
+ }
414
+ }
415
+
416
+ /**
417
+ * Check if pool is running
418
+ */
419
+ isActive(): boolean {
420
+ return this.isRunning;
421
+ }
422
+
423
+ /**
424
+ * Get current pool statistics
425
+ */
426
+ getStats(): PoolStats {
427
+ return { ...this.stats };
428
+ }
429
+
430
+ /**
431
+ * Get all queued transactions
432
+ */
433
+ getQueue(): PendingTransaction[] {
434
+ return [...this.queue];
435
+ }
436
+
437
+ /**
438
+ * Get all processing transactions
439
+ */
440
+ getProcessing(): PendingTransaction[] {
441
+ return Array.from(this.processing.values());
442
+ }
443
+
444
+ /**
445
+ * Clear completed transactions older than specified age
446
+ */
447
+ clearCompleted(maxAgeMs: number = 300000): number {
448
+ const cutoff = Date.now() - maxAgeMs;
449
+ let cleared = 0;
450
+
451
+ for (const [id, tx] of this.completed) {
452
+ const completedTime = tx.confirmedAt || tx.submittedAt || tx.processingAt;
453
+ if (completedTime && completedTime < cutoff) {
454
+ this.completed.delete(id);
455
+ cleared++;
456
+ }
457
+ }
458
+
459
+ return cleared;
460
+ }
461
+
462
+ /**
463
+ * Wait for a transaction to complete
464
+ */
465
+ async waitForTransaction(
466
+ id: string,
467
+ timeoutMs: number = 60000
468
+ ): Promise<PendingTransaction> {
469
+ const startTime = Date.now();
470
+
471
+ while (Date.now() - startTime < timeoutMs) {
472
+ const tx = this.getTransaction(id);
473
+ if (!tx) {
474
+ throw new TradeError(404, `Transaction ${id} not found`);
475
+ }
476
+
477
+ if (this.isTerminalStatus(tx.status)) {
478
+ return tx;
479
+ }
480
+
481
+ await this.sleep(100);
482
+ }
483
+
484
+ throw new TradeError(408, `Timeout waiting for transaction ${id}`);
485
+ }
486
+
487
+ private async processQueue(): Promise<void> {
488
+ if (!this.isRunning) return;
489
+
490
+ // Remove expired transactions
491
+ this.removeExpired();
492
+
493
+ // Process available slots
494
+ while (
495
+ this.processing.size < this.config.maxConcurrent &&
496
+ this.queue.length > 0
497
+ ) {
498
+ const tx = this.queue.shift();
499
+ if (!tx) break;
500
+
501
+ this.processTransaction(tx);
502
+ }
503
+
504
+ this.updateStats();
505
+ }
506
+
507
+ private async processTransaction(tx: PendingTransaction): Promise<void> {
508
+ tx.status = TransactionStatus.Processing;
509
+ tx.processingAt = Date.now();
510
+ this.processing.set(tx.id, tx);
511
+ this.notifyStatusChange(tx);
512
+
513
+ try {
514
+ const result = await this.submitTransaction(tx);
515
+
516
+ if (result.success) {
517
+ tx.status = TransactionStatus.Confirmed;
518
+ tx.signature = result.signature;
519
+ tx.confirmedAt = Date.now();
520
+ this.stats.totalSuccessful++;
521
+ } else {
522
+ tx.attempts++;
523
+ if (tx.attempts >= tx.maxAttempts) {
524
+ tx.status = TransactionStatus.Failed;
525
+ tx.error = result.error;
526
+ this.stats.totalFailed++;
527
+ } else {
528
+ // Re-queue for retry
529
+ tx.status = TransactionStatus.Queued;
530
+ this.queue.push(tx);
531
+ this.sortQueue();
532
+ }
533
+ }
534
+ } catch (error) {
535
+ tx.attempts++;
536
+ if (tx.attempts >= tx.maxAttempts) {
537
+ tx.status = TransactionStatus.Failed;
538
+ tx.error = error instanceof Error ? error.message : 'Unknown error';
539
+ this.stats.totalFailed++;
540
+ } else {
541
+ tx.status = TransactionStatus.Queued;
542
+ this.queue.push(tx);
543
+ this.sortQueue();
544
+ }
545
+ }
546
+
547
+ if (this.isTerminalStatus(tx.status)) {
548
+ this.processing.delete(tx.id);
549
+ this.completed.set(tx.id, tx);
550
+ this.stats.totalProcessed++;
551
+
552
+ // Track processing time
553
+ if (tx.processingAt && tx.confirmedAt) {
554
+ this.processingTimes.push(tx.confirmedAt - tx.processingAt);
555
+ if (this.processingTimes.length > 100) {
556
+ this.processingTimes.shift();
557
+ }
558
+ }
559
+
560
+ // Track wait time
561
+ if (tx.processingAt) {
562
+ this.waitTimes.push(tx.processingAt - tx.queuedAt);
563
+ if (this.waitTimes.length > 100) {
564
+ this.waitTimes.shift();
565
+ }
566
+ }
567
+ }
568
+
569
+ this.notifyStatusChange(tx);
570
+ }
571
+
572
+ private async submitTransaction(tx: PendingTransaction): Promise<ExecutionResult> {
573
+ const providers = this.getProviders(tx.preferredProvider);
574
+
575
+ for (const provider of providers) {
576
+ try {
577
+ tx.status = TransactionStatus.Submitted;
578
+ tx.submittedAt = Date.now();
579
+ this.notifyStatusChange(tx);
580
+
581
+ const signature = await provider.sendTransaction(
582
+ tx.tradeType,
583
+ tx.transaction,
584
+ false
585
+ );
586
+
587
+ return {
588
+ signature,
589
+ success: true,
590
+ status: ExecutionStatus.Confirmed,
591
+ provider: provider.getSwqosType(),
592
+ attempts: tx.attempts + 1,
593
+ executionTimeMs: Date.now() - tx.queuedAt,
594
+ };
595
+ } catch (error) {
596
+ // Try next provider
597
+ continue;
598
+ }
599
+ }
600
+
601
+ return {
602
+ signature: '',
603
+ success: false,
604
+ status: ExecutionStatus.Failed,
605
+ error: 'All providers failed',
606
+ attempts: tx.attempts + 1,
607
+ executionTimeMs: Date.now() - tx.queuedAt,
608
+ };
609
+ }
610
+
611
+ private getProviders(preferred?: SwqosType): SwqosClient[] {
612
+ const providers: SwqosClient[] = [];
613
+
614
+ if (preferred) {
615
+ const preferredClient = this.clients.get(preferred);
616
+ if (preferredClient) {
617
+ providers.push(preferredClient);
618
+ }
619
+ }
620
+
621
+ // Add remaining providers
622
+ for (const [type, client] of this.clients) {
623
+ if (type !== preferred) {
624
+ providers.push(client);
625
+ }
626
+ }
627
+
628
+ return providers;
629
+ }
630
+
631
+ private removeExpired(): void {
632
+ const now = Date.now();
633
+ const expired: PendingTransaction[] = [];
634
+
635
+ this.queue = this.queue.filter(tx => {
636
+ if (now - tx.queuedAt > this.config.maxQueueTimeMs) {
637
+ tx.status = TransactionStatus.Expired;
638
+ expired.push(tx);
639
+ return false;
640
+ }
641
+ return true;
642
+ });
643
+
644
+ for (const tx of expired) {
645
+ this.completed.set(tx.id, tx);
646
+ this.notifyStatusChange(tx);
647
+ }
648
+ }
649
+
650
+ private sortQueue(): void {
651
+ this.queue.sort((a, b) => this.calculator.compare(a, b));
652
+ }
653
+
654
+ private isTerminalStatus(status: TransactionStatus): boolean {
655
+ return [
656
+ TransactionStatus.Confirmed,
657
+ TransactionStatus.Failed,
658
+ TransactionStatus.Dropped,
659
+ TransactionStatus.Expired,
660
+ TransactionStatus.Cancelled,
661
+ ].includes(status);
662
+ }
663
+
664
+ private initializeStats(): PoolStats {
665
+ return {
666
+ totalProcessed: 0,
667
+ totalSuccessful: 0,
668
+ totalFailed: 0,
669
+ queueSize: 0,
670
+ processingCount: 0,
671
+ avgProcessingTimeMs: 0,
672
+ avgWaitTimeMs: 0,
673
+ byStatus: {
674
+ [TransactionStatus.Queued]: 0,
675
+ [TransactionStatus.Processing]: 0,
676
+ [TransactionStatus.Submitted]: 0,
677
+ [TransactionStatus.Confirmed]: 0,
678
+ [TransactionStatus.Failed]: 0,
679
+ [TransactionStatus.Dropped]: 0,
680
+ [TransactionStatus.Expired]: 0,
681
+ [TransactionStatus.Cancelled]: 0,
682
+ },
683
+ throughput: 0,
684
+ };
685
+ }
686
+
687
+ private updateStats(): void {
688
+ // Calculate averages
689
+ const avgProcessingTime = this.processingTimes.length > 0
690
+ ? this.processingTimes.reduce((a, b) => a + b, 0) / this.processingTimes.length
691
+ : 0;
692
+
693
+ const avgWaitTime = this.waitTimes.length > 0
694
+ ? this.waitTimes.reduce((a, b) => a + b, 0) / this.waitTimes.length
695
+ : 0;
696
+
697
+ // Count by status
698
+ const byStatus: Record<TransactionStatus, number> = { ...this.stats.byStatus };
699
+ for (const status of Object.values(TransactionStatus)) {
700
+ byStatus[status] = 0;
701
+ }
702
+
703
+ for (const tx of this.queue) {
704
+ byStatus[tx.status]++;
705
+ }
706
+ for (const tx of this.processing.values()) {
707
+ byStatus[tx.status]++;
708
+ }
709
+
710
+ this.stats = {
711
+ ...this.stats,
712
+ queueSize: this.queue.length,
713
+ processingCount: this.processing.size,
714
+ avgProcessingTimeMs: Math.round(avgProcessingTime),
715
+ avgWaitTimeMs: Math.round(avgWaitTime),
716
+ byStatus,
717
+ throughput: this.calculateThroughput(),
718
+ };
719
+
720
+ if (this.config.onStatsUpdate) {
721
+ this.config.onStatsUpdate(this.stats);
722
+ }
723
+ }
724
+
725
+ private calculateThroughput(): number {
726
+ // Simple throughput calculation based on recent completions
727
+ const recentTimeWindow = 60000; // 1 minute
728
+ const cutoff = Date.now() - recentTimeWindow;
729
+
730
+ let recentCompletions = 0;
731
+ for (const tx of this.completed.values()) {
732
+ if (tx.confirmedAt && tx.confirmedAt > cutoff) {
733
+ recentCompletions++;
734
+ }
735
+ }
736
+
737
+ return Math.round((recentCompletions / recentTimeWindow) * 1000) / 1000;
738
+ }
739
+
740
+ private notifyStatusChange(tx: PendingTransaction): void {
741
+ if (this.config.onStatusChange) {
742
+ this.config.onStatusChange(tx);
743
+ }
744
+ }
745
+
746
+ private generateTransactionId(): string {
747
+ return `tx_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;
748
+ }
749
+
750
+ private sleep(ms: number): Promise<void> {
751
+ return new Promise(resolve => setTimeout(resolve, ms));
752
+ }
753
+ }
754
+
755
+ // ===== Convenience Functions =====
756
+
757
+ /**
758
+ * Create a new transaction pool
759
+ */
760
+ export function createTransactionPool(
761
+ rpcUrl: string,
762
+ config?: Partial<PoolConfig>
763
+ ): TransactionPool {
764
+ return new TransactionPool(rpcUrl, { ...defaultPoolConfig(), ...config });
765
+ }
766
+
767
+ /**
768
+ * Submit a single transaction to a pool
769
+ */
770
+ export async function submitToPool(
771
+ pool: TransactionPool,
772
+ transaction: Buffer,
773
+ tradeType: TradeType,
774
+ options?: {
775
+ priority?: PriorityLevel;
776
+ preferredProvider?: SwqosType;
777
+ }
778
+ ): Promise<string> {
779
+ return pool.submit(transaction, tradeType, options);
780
+ }