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,683 @@
1
+ /**
2
+ * Retry Handler for Sol Trade SDK
3
+ * Implements retry strategies with circuit breaker pattern.
4
+ */
5
+
6
+ import { TradeError } from '../../index';
7
+
8
+ // ===== Types =====
9
+
10
+ /**
11
+ * Retry strategy types
12
+ */
13
+ export enum RetryStrategy {
14
+ /** No retry - fail immediately */
15
+ None = 'None',
16
+ /** Fixed delay between retries */
17
+ Fixed = 'Fixed',
18
+ /** Linear increasing delay */
19
+ Linear = 'Linear',
20
+ /** Exponential backoff */
21
+ Exponential = 'Exponential',
22
+ /** Exponential backoff with jitter */
23
+ ExponentialJitter = 'ExponentialJitter',
24
+ /** Custom retry strategy */
25
+ Custom = 'Custom',
26
+ }
27
+
28
+ /**
29
+ * Configuration for retry behavior
30
+ */
31
+ export interface RetryConfig {
32
+ /** Maximum number of retry attempts */
33
+ maxAttempts: number;
34
+ /** Initial delay between retries in milliseconds */
35
+ initialDelayMs: number;
36
+ /** Maximum delay between retries in milliseconds */
37
+ maxDelayMs: number;
38
+ /** Multiplier for exponential backoff */
39
+ backoffMultiplier: number;
40
+ /** Retry strategy to use */
41
+ strategy: RetryStrategy;
42
+ /** Whether to retry on all errors or specific ones */
43
+ retryAllErrors: boolean;
44
+ /** Specific error codes to retry on */
45
+ retryableErrorCodes: number[];
46
+ /** Specific error messages to retry on */
47
+ retryableErrorMessages: string[];
48
+ /** Whether to retry on timeout */
49
+ retryOnTimeout: boolean;
50
+ /** Timeout for each attempt in milliseconds */
51
+ attemptTimeoutMs: number;
52
+ /** Callback before each retry attempt */
53
+ onRetry?: (attempt: number, error: Error, nextDelayMs: number) => void;
54
+ /** Callback when all attempts exhausted */
55
+ onExhausted?: (lastError: Error, attempts: number) => void;
56
+ }
57
+
58
+ /**
59
+ * Result of a retry operation
60
+ */
61
+ export interface RetryResult<T> {
62
+ /** Whether the operation succeeded */
63
+ success: boolean;
64
+ /** Result value if succeeded */
65
+ value?: T;
66
+ /** Error if failed */
67
+ error?: Error;
68
+ /** Number of attempts made */
69
+ attempts: number;
70
+ /** Total time elapsed in milliseconds */
71
+ totalTimeMs: number;
72
+ /** Whether the result was from cache */
73
+ fromCache?: boolean;
74
+ }
75
+
76
+ /**
77
+ * Circuit breaker states
78
+ */
79
+ export enum CircuitState {
80
+ /** Normal operation - requests allowed */
81
+ Closed = 'Closed',
82
+ /** Failure threshold reached - requests blocked */
83
+ Open = 'Open',
84
+ /** Testing if service has recovered */
85
+ HalfOpen = 'HalfOpen',
86
+ }
87
+
88
+ /**
89
+ * Circuit breaker configuration
90
+ */
91
+ export interface CircuitBreakerConfig {
92
+ /** Number of failures before opening circuit */
93
+ failureThreshold: number;
94
+ /** Time to wait before attempting reset in milliseconds */
95
+ resetTimeoutMs: number;
96
+ /** Number of successes required to close circuit from half-open */
97
+ successThreshold: number;
98
+ /** Time window for counting failures in milliseconds */
99
+ failureWindowMs: number;
100
+ /** Half-open request probability (0-1) */
101
+ halfOpenProbability: number;
102
+ /** Callback when circuit opens */
103
+ onOpen?: () => void;
104
+ /** Callback when circuit closes */
105
+ onClose?: () => void;
106
+ /** Callback when circuit enters half-open */
107
+ onHalfOpen?: () => void;
108
+ }
109
+
110
+ /**
111
+ * Circuit breaker statistics
112
+ */
113
+ export interface CircuitStats {
114
+ /** Current circuit state */
115
+ state: CircuitState;
116
+ /** Total requests made */
117
+ totalRequests: number;
118
+ /** Total successful requests */
119
+ successfulRequests: number;
120
+ /** Total failed requests */
121
+ failedRequests: number;
122
+ /** Current consecutive failures */
123
+ consecutiveFailures: number;
124
+ /** Last failure timestamp */
125
+ lastFailureTime?: number;
126
+ /** Last success timestamp */
127
+ lastSuccessTime?: number;
128
+ /** Circuit open timestamp */
129
+ circuitOpenTime?: number;
130
+ /** Current failure rate (0-1) */
131
+ failureRate: number;
132
+ }
133
+
134
+ // ===== Default Configurations =====
135
+
136
+ /**
137
+ * Get default retry configuration
138
+ */
139
+ export function defaultRetryConfig(): RetryConfig {
140
+ return {
141
+ maxAttempts: 3,
142
+ initialDelayMs: 100,
143
+ maxDelayMs: 10000,
144
+ backoffMultiplier: 2,
145
+ strategy: RetryStrategy.Exponential,
146
+ retryAllErrors: false,
147
+ retryableErrorCodes: [429, 500, 502, 503, 504],
148
+ retryableErrorMessages: ['timeout', 'rate limit', 'temporarily unavailable'],
149
+ retryOnTimeout: true,
150
+ attemptTimeoutMs: 30000,
151
+ };
152
+ }
153
+
154
+ /**
155
+ * Get aggressive retry configuration
156
+ */
157
+ export function aggressiveRetryConfig(): RetryConfig {
158
+ return {
159
+ maxAttempts: 5,
160
+ initialDelayMs: 50,
161
+ maxDelayMs: 5000,
162
+ backoffMultiplier: 1.5,
163
+ strategy: RetryStrategy.ExponentialJitter,
164
+ retryAllErrors: true,
165
+ retryableErrorCodes: [],
166
+ retryableErrorMessages: [],
167
+ retryOnTimeout: true,
168
+ attemptTimeoutMs: 15000,
169
+ };
170
+ }
171
+
172
+ /**
173
+ * Get conservative retry configuration
174
+ */
175
+ export function conservativeRetryConfig(): RetryConfig {
176
+ return {
177
+ maxAttempts: 2,
178
+ initialDelayMs: 500,
179
+ maxDelayMs: 30000,
180
+ backoffMultiplier: 2,
181
+ strategy: RetryStrategy.Exponential,
182
+ retryAllErrors: false,
183
+ retryableErrorCodes: [503, 504],
184
+ retryableErrorMessages: ['service unavailable'],
185
+ retryOnTimeout: false,
186
+ attemptTimeoutMs: 60000,
187
+ };
188
+ }
189
+
190
+ /**
191
+ * Get default circuit breaker configuration
192
+ */
193
+ export function defaultCircuitBreakerConfig(): CircuitBreakerConfig {
194
+ return {
195
+ failureThreshold: 5,
196
+ resetTimeoutMs: 30000,
197
+ successThreshold: 2,
198
+ failureWindowMs: 60000,
199
+ halfOpenProbability: 0.5,
200
+ };
201
+ }
202
+
203
+ // ===== Exponential Backoff =====
204
+
205
+ /**
206
+ * Calculates delays for exponential backoff strategies
207
+ */
208
+ export class ExponentialBackoff {
209
+ constructor(private config: RetryConfig) {}
210
+
211
+ /**
212
+ * Calculate delay for a specific attempt
213
+ */
214
+ calculateDelay(attempt: number): number {
215
+ switch (this.config.strategy) {
216
+ case RetryStrategy.None:
217
+ return 0;
218
+ case RetryStrategy.Fixed:
219
+ return this.config.initialDelayMs;
220
+ case RetryStrategy.Linear:
221
+ return Math.min(
222
+ this.config.initialDelayMs * attempt,
223
+ this.config.maxDelayMs
224
+ );
225
+ case RetryStrategy.Exponential:
226
+ return Math.min(
227
+ this.config.initialDelayMs * Math.pow(this.config.backoffMultiplier, attempt - 1),
228
+ this.config.maxDelayMs
229
+ );
230
+ case RetryStrategy.ExponentialJitter:
231
+ const baseDelay = Math.min(
232
+ this.config.initialDelayMs * Math.pow(this.config.backoffMultiplier, attempt - 1),
233
+ this.config.maxDelayMs
234
+ );
235
+ // Add random jitter (±25%)
236
+ const jitter = baseDelay * 0.25 * (Math.random() * 2 - 1);
237
+ return Math.max(0, baseDelay + jitter);
238
+ default:
239
+ return this.config.initialDelayMs;
240
+ }
241
+ }
242
+
243
+ /**
244
+ * Calculate all delays for all attempts
245
+ */
246
+ calculateAllDelays(): number[] {
247
+ const delays: number[] = [];
248
+ for (let i = 1; i <= this.config.maxAttempts; i++) {
249
+ delays.push(this.calculateDelay(i));
250
+ }
251
+ return delays;
252
+ }
253
+
254
+ /**
255
+ * Get total estimated time for all retries
256
+ */
257
+ getTotalEstimatedTime(): number {
258
+ const delays = this.calculateAllDelays();
259
+ return delays.reduce((sum, delay) => sum + delay, 0);
260
+ }
261
+ }
262
+
263
+ // ===== Circuit Breaker =====
264
+
265
+ /**
266
+ * Circuit breaker implementation for fault tolerance
267
+ */
268
+ export class CircuitBreaker {
269
+ private state: CircuitState = CircuitState.Closed;
270
+ private stats: CircuitStats;
271
+ private failureTimes: number[] = [];
272
+ private consecutiveSuccesses: number = 0;
273
+ private halfOpenAttempts: number = 0;
274
+
275
+ constructor(private config: CircuitBreakerConfig = defaultCircuitBreakerConfig()) {
276
+ this.stats = this.initializeStats();
277
+ }
278
+
279
+ /**
280
+ * Execute a function with circuit breaker protection
281
+ */
282
+ async execute<T>(fn: () => Promise<T>): Promise<T> {
283
+ if (!this.canExecute()) {
284
+ throw new TradeError(503, 'Circuit breaker is open');
285
+ }
286
+
287
+ try {
288
+ const result = await fn();
289
+ this.recordSuccess();
290
+ return result;
291
+ } catch (error) {
292
+ this.recordFailure();
293
+ throw error;
294
+ }
295
+ }
296
+
297
+ /**
298
+ * Check if execution is allowed
299
+ */
300
+ canExecute(): boolean {
301
+ this.updateState();
302
+
303
+ switch (this.state) {
304
+ case CircuitState.Closed:
305
+ return true;
306
+ case CircuitState.Open:
307
+ return false;
308
+ case CircuitState.HalfOpen:
309
+ // Allow some requests through in half-open state
310
+ return Math.random() < this.config.halfOpenProbability;
311
+ default:
312
+ return false;
313
+ }
314
+ }
315
+
316
+ /**
317
+ * Force open the circuit
318
+ */
319
+ forceOpen(): void {
320
+ this.transitionTo(CircuitState.Open);
321
+ }
322
+
323
+ /**
324
+ * Force close the circuit
325
+ */
326
+ forceClose(): void {
327
+ this.transitionTo(CircuitState.Closed);
328
+ }
329
+
330
+ /**
331
+ * Get current circuit state
332
+ */
333
+ getState(): CircuitState {
334
+ this.updateState();
335
+ return this.state;
336
+ }
337
+
338
+ /**
339
+ * Get circuit statistics
340
+ */
341
+ getStats(): CircuitStats {
342
+ this.updateStats();
343
+ return { ...this.stats };
344
+ }
345
+
346
+ /**
347
+ * Reset circuit statistics
348
+ */
349
+ reset(): void {
350
+ this.state = CircuitState.Closed;
351
+ this.stats = this.initializeStats();
352
+ this.failureTimes = [];
353
+ this.consecutiveSuccesses = 0;
354
+ this.halfOpenAttempts = 0;
355
+ }
356
+
357
+ private recordSuccess(): void {
358
+ this.stats.totalRequests++;
359
+ this.stats.successfulRequests++;
360
+ this.stats.lastSuccessTime = Date.now();
361
+ this.consecutiveSuccesses++;
362
+
363
+ if (this.state === CircuitState.HalfOpen) {
364
+ if (this.consecutiveSuccesses >= this.config.successThreshold) {
365
+ this.transitionTo(CircuitState.Closed);
366
+ }
367
+ } else {
368
+ this.stats.consecutiveFailures = 0;
369
+ }
370
+
371
+ this.updateStats();
372
+ }
373
+
374
+ private recordFailure(): void {
375
+ const now = Date.now();
376
+ this.stats.totalRequests++;
377
+ this.stats.failedRequests++;
378
+ this.stats.consecutiveFailures++;
379
+ this.stats.lastFailureTime = now;
380
+ this.consecutiveSuccesses = 0;
381
+
382
+ this.failureTimes.push(now);
383
+ this.cleanupOldFailures();
384
+
385
+ if (this.state === CircuitState.HalfOpen) {
386
+ this.transitionTo(CircuitState.Open);
387
+ } else if (this.failureTimes.length >= this.config.failureThreshold) {
388
+ this.transitionTo(CircuitState.Open);
389
+ }
390
+
391
+ this.updateStats();
392
+ }
393
+
394
+ private updateState(): void {
395
+ if (this.state === CircuitState.Open) {
396
+ const timeSinceOpen = Date.now() - (this.stats.circuitOpenTime || 0);
397
+ if (timeSinceOpen >= this.config.resetTimeoutMs) {
398
+ this.transitionTo(CircuitState.HalfOpen);
399
+ }
400
+ }
401
+ }
402
+
403
+ private transitionTo(newState: CircuitState): void {
404
+ const oldState = this.state;
405
+ this.state = newState;
406
+
407
+ if (newState === CircuitState.Open) {
408
+ this.stats.circuitOpenTime = Date.now();
409
+ if (this.config.onOpen) this.config.onOpen();
410
+ } else if (newState === CircuitState.Closed) {
411
+ this.consecutiveSuccesses = 0;
412
+ this.failureTimes = [];
413
+ this.stats.consecutiveFailures = 0;
414
+ if (this.config.onClose) this.config.onClose();
415
+ } else if (newState === CircuitState.HalfOpen) {
416
+ this.halfOpenAttempts = 0;
417
+ if (this.config.onHalfOpen) this.config.onHalfOpen();
418
+ }
419
+ }
420
+
421
+ private cleanupOldFailures(): void {
422
+ const cutoff = Date.now() - this.config.failureWindowMs;
423
+ this.failureTimes = this.failureTimes.filter(time => time > cutoff);
424
+ }
425
+
426
+ private updateStats(): void {
427
+ const totalRequests = this.stats.totalRequests;
428
+ this.stats.failureRate = totalRequests > 0
429
+ ? this.stats.failedRequests / totalRequests
430
+ : 0;
431
+ }
432
+
433
+ private initializeStats(): CircuitStats {
434
+ return {
435
+ state: CircuitState.Closed,
436
+ totalRequests: 0,
437
+ successfulRequests: 0,
438
+ failedRequests: 0,
439
+ consecutiveFailures: 0,
440
+ failureRate: 0,
441
+ };
442
+ }
443
+ }
444
+
445
+ // ===== Retry Handler =====
446
+
447
+ /**
448
+ * Handles retry logic with configurable strategies
449
+ */
450
+ export class RetryHandler {
451
+ private backoff: ExponentialBackoff;
452
+ private circuitBreaker?: CircuitBreaker;
453
+
454
+ constructor(
455
+ private config: RetryConfig = defaultRetryConfig(),
456
+ circuitBreakerConfig?: CircuitBreakerConfig
457
+ ) {
458
+ this.backoff = new ExponentialBackoff(config);
459
+ if (circuitBreakerConfig) {
460
+ this.circuitBreaker = new CircuitBreaker(circuitBreakerConfig);
461
+ }
462
+ }
463
+
464
+ /**
465
+ * Execute a function with retry logic
466
+ */
467
+ async execute<T>(fn: () => Promise<T>): Promise<RetryResult<T>> {
468
+ const startTime = Date.now();
469
+
470
+ // Check circuit breaker
471
+ if (this.circuitBreaker && !this.circuitBreaker.canExecute()) {
472
+ return {
473
+ success: false,
474
+ error: new TradeError(503, 'Circuit breaker is open'),
475
+ attempts: 0,
476
+ totalTimeMs: Date.now() - startTime,
477
+ };
478
+ }
479
+
480
+ let lastError: Error | undefined;
481
+
482
+ for (let attempt = 1; attempt <= this.config.maxAttempts; attempt++) {
483
+ try {
484
+ let result: T;
485
+
486
+ if (this.circuitBreaker) {
487
+ result = await this.circuitBreaker.execute(fn);
488
+ } else {
489
+ result = await fn();
490
+ }
491
+
492
+ return {
493
+ success: true,
494
+ value: result,
495
+ attempts: attempt,
496
+ totalTimeMs: Date.now() - startTime,
497
+ };
498
+ } catch (error) {
499
+ lastError = error instanceof Error ? error : new Error(String(error));
500
+
501
+ // Check if we should retry this error
502
+ if (!this.shouldRetry(lastError, attempt)) {
503
+ break;
504
+ }
505
+
506
+ // Calculate delay for next attempt
507
+ if (attempt < this.config.maxAttempts) {
508
+ const delayMs = this.backoff.calculateDelay(attempt);
509
+
510
+ if (this.config.onRetry) {
511
+ this.config.onRetry(attempt, lastError, delayMs);
512
+ }
513
+
514
+ await this.sleep(delayMs);
515
+ }
516
+ }
517
+ }
518
+
519
+ // All attempts exhausted
520
+ if (this.config.onExhausted && lastError) {
521
+ this.config.onExhausted(lastError, this.config.maxAttempts);
522
+ }
523
+
524
+ return {
525
+ success: false,
526
+ error: lastError,
527
+ attempts: this.config.maxAttempts,
528
+ totalTimeMs: Date.now() - startTime,
529
+ };
530
+ }
531
+
532
+ /**
533
+ * Execute with timeout for each attempt
534
+ */
535
+ async executeWithTimeout<T>(fn: () => Promise<T>, timeoutMs?: number): Promise<RetryResult<T>> {
536
+ const actualTimeout = timeoutMs ?? this.config.attemptTimeoutMs;
537
+
538
+ return this.execute(async () => {
539
+ return Promise.race([
540
+ fn(),
541
+ new Promise<T>((_, reject) => {
542
+ setTimeout(() => {
543
+ reject(new TradeError(408, `Operation timed out after ${actualTimeout}ms`));
544
+ }, actualTimeout);
545
+ }),
546
+ ]);
547
+ });
548
+ }
549
+
550
+ /**
551
+ * Get current retry configuration
552
+ */
553
+ getConfig(): RetryConfig {
554
+ return { ...this.config };
555
+ }
556
+
557
+ /**
558
+ * Update retry configuration
559
+ */
560
+ updateConfig(config: Partial<RetryConfig>): void {
561
+ this.config = { ...this.config, ...config };
562
+ this.backoff = new ExponentialBackoff(this.config);
563
+ }
564
+
565
+ /**
566
+ * Get circuit breaker stats if available
567
+ */
568
+ getCircuitStats(): CircuitStats | undefined {
569
+ return this.circuitBreaker?.getStats();
570
+ }
571
+
572
+ /**
573
+ * Reset circuit breaker if available
574
+ */
575
+ resetCircuit(): void {
576
+ this.circuitBreaker?.reset();
577
+ }
578
+
579
+ private shouldRetry(error: Error, attempt: number): boolean {
580
+ // Don't retry if we've reached max attempts
581
+ if (attempt >= this.config.maxAttempts) {
582
+ return false;
583
+ }
584
+
585
+ // Check if retrying all errors
586
+ if (this.config.retryAllErrors) {
587
+ return true;
588
+ }
589
+
590
+ // Check error code
591
+ const tradeError = error as TradeError;
592
+ if (tradeError.code !== undefined &&
593
+ this.config.retryableErrorCodes.includes(tradeError.code)) {
594
+ return true;
595
+ }
596
+
597
+ // Check error message
598
+ const errorMessage = error.message.toLowerCase();
599
+ for (const retryableMessage of this.config.retryableErrorMessages) {
600
+ if (errorMessage.includes(retryableMessage.toLowerCase())) {
601
+ return true;
602
+ }
603
+ }
604
+
605
+ // Check for timeout
606
+ if (this.config.retryOnTimeout &&
607
+ (errorMessage.includes('timeout') || errorMessage.includes('timed out'))) {
608
+ return true;
609
+ }
610
+
611
+ return false;
612
+ }
613
+
614
+ private sleep(ms: number): Promise<void> {
615
+ return new Promise(resolve => setTimeout(resolve, ms));
616
+ }
617
+ }
618
+
619
+ // ===== Convenience Functions =====
620
+
621
+ /**
622
+ * Create a new retry handler
623
+ */
624
+ export function createRetryHandler(
625
+ config?: Partial<RetryConfig>,
626
+ circuitBreakerConfig?: Partial<CircuitBreakerConfig>
627
+ ): RetryHandler {
628
+ return new RetryHandler(
629
+ { ...defaultRetryConfig(), ...config },
630
+ circuitBreakerConfig ? { ...defaultCircuitBreakerConfig(), ...circuitBreakerConfig } : undefined
631
+ );
632
+ }
633
+
634
+ /**
635
+ * Execute a function with retry logic
636
+ */
637
+ export async function withRetry<T>(
638
+ fn: () => Promise<T>,
639
+ config?: Partial<RetryConfig>
640
+ ): Promise<T> {
641
+ const handler = new RetryHandler({ ...defaultRetryConfig(), ...config });
642
+ const result = await handler.execute(fn);
643
+
644
+ if (!result.success) {
645
+ throw result.error || new TradeError(500, 'Operation failed after retries');
646
+ }
647
+
648
+ return result.value as T;
649
+ }
650
+
651
+ /**
652
+ * Execute a function with circuit breaker protection
653
+ */
654
+ export async function withCircuitBreaker<T>(
655
+ fn: () => Promise<T>,
656
+ circuitBreakerConfig?: Partial<CircuitBreakerConfig>
657
+ ): Promise<T> {
658
+ const config = { ...defaultCircuitBreakerConfig(), ...circuitBreakerConfig };
659
+ const breaker = new CircuitBreaker(config);
660
+ return breaker.execute(fn);
661
+ }
662
+
663
+ /**
664
+ * Calculate exponential backoff delay
665
+ */
666
+ export function calculateBackoff(
667
+ attempt: number,
668
+ initialDelayMs: number = 100,
669
+ multiplier: number = 2,
670
+ maxDelayMs: number = 10000,
671
+ jitter: boolean = false
672
+ ): number {
673
+ let delay = Math.min(
674
+ initialDelayMs * Math.pow(multiplier, attempt - 1),
675
+ maxDelayMs
676
+ );
677
+
678
+ if (jitter) {
679
+ delay += delay * 0.25 * (Math.random() * 2 - 1);
680
+ }
681
+
682
+ return Math.max(0, delay);
683
+ }