@deserialize/multi-vm-wallet 1.4.2 → 1.5.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 (186) hide show
  1. package/.claude/settings.local.json +7 -1
  2. package/BUILD_OPTIMIZATION_PLAN.md +640 -0
  3. package/BUILD_RESULTS.md +282 -0
  4. package/BUN_MIGRATION.md +415 -0
  5. package/CHANGELOG_SECURITY.md +573 -0
  6. package/IMPLEMENTATION_SUMMARY.md +494 -0
  7. package/SECURITY_AUDIT.md +1124 -0
  8. package/bun.lock +553 -0
  9. package/dist/IChainWallet.js +0 -5
  10. package/dist/bip32Old.js +0 -885
  11. package/dist/bip32Small.js +0 -79
  12. package/dist/bipTest.js +0 -362
  13. package/dist/constant.js +0 -17
  14. package/dist/english.js +0 -1
  15. package/dist/evm/aa-service/index.d.ts +0 -5
  16. package/dist/evm/aa-service/index.js +0 -14
  17. package/dist/evm/aa-service/lib/account-adapter.d.ts +0 -22
  18. package/dist/evm/aa-service/lib/account-adapter.js +0 -24
  19. package/dist/evm/aa-service/lib/kernel-account.d.ts +0 -30
  20. package/dist/evm/aa-service/lib/kernel-account.js +2 -67
  21. package/dist/evm/aa-service/lib/kernel-modules.d.ts +0 -177
  22. package/dist/evm/aa-service/lib/kernel-modules.js +4 -202
  23. package/dist/evm/aa-service/lib/session-keys.d.ts +0 -118
  24. package/dist/evm/aa-service/lib/session-keys.js +7 -151
  25. package/dist/evm/aa-service/lib/type.d.ts +0 -55
  26. package/dist/evm/aa-service/lib/type.js +0 -10
  27. package/dist/evm/aa-service/services/account-abstraction.d.ts +0 -426
  28. package/dist/evm/aa-service/services/account-abstraction.js +0 -461
  29. package/dist/evm/aa-service/services/bundler.d.ts +0 -6
  30. package/dist/evm/aa-service/services/bundler.js +0 -54
  31. package/dist/evm/evm.d.ts +9 -51
  32. package/dist/evm/evm.js +338 -76
  33. package/dist/evm/index.js +0 -3
  34. package/dist/evm/script.js +3 -17
  35. package/dist/evm/smartWallet.d.ts +0 -173
  36. package/dist/evm/smartWallet.js +0 -206
  37. package/dist/evm/smartWallet.types.d.ts +0 -6
  38. package/dist/evm/smartWallet.types.js +0 -8
  39. package/dist/evm/transaction.utils.d.ts +0 -242
  40. package/dist/evm/transaction.utils.js +4 -320
  41. package/dist/evm/transactionParsing.d.ts +0 -11
  42. package/dist/evm/transactionParsing.js +28 -147
  43. package/dist/evm/utils.d.ts +0 -46
  44. package/dist/evm/utils.js +1 -57
  45. package/dist/helpers/index.d.ts +0 -4
  46. package/dist/helpers/index.js +8 -44
  47. package/dist/helpers/routeScan.js +0 -1
  48. package/dist/index.js +0 -1
  49. package/dist/old.js +0 -884
  50. package/dist/price.js +0 -1
  51. package/dist/price.types.js +0 -2
  52. package/dist/rate-limiter.d.ts +28 -0
  53. package/dist/rate-limiter.js +95 -0
  54. package/dist/retry-logic.d.ts +14 -0
  55. package/dist/retry-logic.js +120 -0
  56. package/dist/savings/index.js +0 -1
  57. package/dist/savings/saving-manager.d.ts +10 -11
  58. package/dist/savings/saving-manager.js +79 -22
  59. package/dist/savings/savings-operations.d.ts +39 -0
  60. package/dist/savings/savings-operations.js +141 -0
  61. package/dist/savings/smart-savings.d.ts +0 -63
  62. package/dist/savings/smart-savings.js +0 -78
  63. package/dist/savings/types.d.ts +0 -69
  64. package/dist/savings/types.js +0 -7
  65. package/dist/savings/validation.d.ts +9 -0
  66. package/dist/savings/validation.js +85 -0
  67. package/dist/svm/constant.js +0 -1
  68. package/dist/svm/index.js +0 -1
  69. package/dist/svm/svm.d.ts +11 -1
  70. package/dist/svm/svm.js +267 -27
  71. package/dist/svm/transactionParsing.d.ts +0 -7
  72. package/dist/svm/transactionParsing.js +3 -41
  73. package/dist/svm/transactionSender.js +0 -9
  74. package/dist/svm/utils.d.ts +0 -12
  75. package/dist/svm/utils.js +9 -60
  76. package/dist/test.d.ts +0 -4
  77. package/dist/test.js +6 -98
  78. package/dist/transaction-utils.d.ts +38 -0
  79. package/dist/transaction-utils.js +168 -0
  80. package/dist/types.d.ts +36 -0
  81. package/dist/types.js +0 -1
  82. package/dist/utils.js +0 -1
  83. package/dist/vm-validation.d.ts +11 -0
  84. package/dist/vm-validation.js +151 -0
  85. package/dist/vm.d.ts +12 -2
  86. package/dist/vm.js +61 -16
  87. package/dist/walletBip32.js +15 -70
  88. package/package.json +9 -4
  89. package/test-discovery.ts +235 -0
  90. package/test-pocket-discovery.ts +84 -0
  91. package/tsconfig.json +18 -11
  92. package/tsconfig.prod.json +10 -0
  93. package/utils/evm/evm.ts +554 -8
  94. package/utils/rate-limiter.ts +179 -0
  95. package/utils/retry-logic.ts +271 -0
  96. package/utils/savings/EXAMPLES.md +883 -0
  97. package/utils/savings/SECURITY.md +731 -0
  98. package/utils/savings/saving-manager.ts +526 -16
  99. package/utils/savings/savings-operations.ts +509 -0
  100. package/utils/savings/validation.ts +187 -0
  101. package/utils/svm/svm.ts +476 -5
  102. package/utils/test.ts +2 -2
  103. package/utils/transaction-utils.ts +394 -0
  104. package/utils/types.ts +100 -0
  105. package/utils/vm-validation.ts +280 -0
  106. package/utils/vm.ts +197 -10
  107. package/utils/walletBip32.ts +39 -3
  108. package/dist/IChainWallet.js.map +0 -1
  109. package/dist/bip32.d.ts +0 -9
  110. package/dist/bip32.js +0 -172
  111. package/dist/bip32.js.map +0 -1
  112. package/dist/bip32Old.js.map +0 -1
  113. package/dist/bip32Small.js.map +0 -1
  114. package/dist/bipTest.js.map +0 -1
  115. package/dist/constant.js.map +0 -1
  116. package/dist/english.js.map +0 -1
  117. package/dist/evm/SMART_WALLET_EXAMPLES.d.ts +0 -20
  118. package/dist/evm/SMART_WALLET_EXAMPLES.js +0 -451
  119. package/dist/evm/SMART_WALLET_EXAMPLES.js.map +0 -1
  120. package/dist/evm/aa-service/index.js.map +0 -1
  121. package/dist/evm/aa-service/lib/account-adapter.js.map +0 -1
  122. package/dist/evm/aa-service/lib/kernel-account.js.map +0 -1
  123. package/dist/evm/aa-service/lib/kernel-modules.js.map +0 -1
  124. package/dist/evm/aa-service/lib/session-keys.js.map +0 -1
  125. package/dist/evm/aa-service/lib/type.js.map +0 -1
  126. package/dist/evm/aa-service/services/account-abstraction.js.map +0 -1
  127. package/dist/evm/aa-service/services/bundler.js.map +0 -1
  128. package/dist/evm/evm.js.map +0 -1
  129. package/dist/evm/index.js.map +0 -1
  130. package/dist/evm/script.js.map +0 -1
  131. package/dist/evm/smartWallet.js.map +0 -1
  132. package/dist/evm/smartWallet.types.js.map +0 -1
  133. package/dist/evm/transaction.utils.js.map +0 -1
  134. package/dist/evm/transactionParsing.js.map +0 -1
  135. package/dist/evm/utils.js.map +0 -1
  136. package/dist/helpers/index.js.map +0 -1
  137. package/dist/helpers/routeScan.js.map +0 -1
  138. package/dist/index.js.map +0 -1
  139. package/dist/old.js.map +0 -1
  140. package/dist/price.js.map +0 -1
  141. package/dist/price.types.js.map +0 -1
  142. package/dist/privacy/artifact-manager.d.ts +0 -117
  143. package/dist/privacy/artifact-manager.js +0 -251
  144. package/dist/privacy/artifact-manager.js.map +0 -1
  145. package/dist/privacy/broadcaster-client.d.ts +0 -166
  146. package/dist/privacy/broadcaster-client.js +0 -261
  147. package/dist/privacy/broadcaster-client.js.map +0 -1
  148. package/dist/privacy/index.d.ts +0 -34
  149. package/dist/privacy/index.js +0 -56
  150. package/dist/privacy/index.js.map +0 -1
  151. package/dist/privacy/network-config.d.ts +0 -57
  152. package/dist/privacy/network-config.js +0 -118
  153. package/dist/privacy/network-config.js.map +0 -1
  154. package/dist/privacy/poi-helper.d.ts +0 -161
  155. package/dist/privacy/poi-helper.js +0 -249
  156. package/dist/privacy/poi-helper.js.map +0 -1
  157. package/dist/privacy/railgun-engine.d.ts +0 -135
  158. package/dist/privacy/railgun-engine.js +0 -205
  159. package/dist/privacy/railgun-engine.js.map +0 -1
  160. package/dist/privacy/railgun-privacy-wallet.d.ts +0 -288
  161. package/dist/privacy/railgun-privacy-wallet.js +0 -539
  162. package/dist/privacy/railgun-privacy-wallet.js.map +0 -1
  163. package/dist/privacy/types.d.ts +0 -229
  164. package/dist/privacy/types.js +0 -26
  165. package/dist/privacy/types.js.map +0 -1
  166. package/dist/savings/index.js.map +0 -1
  167. package/dist/savings/saving-actions.d.ts +0 -0
  168. package/dist/savings/saving-actions.js +0 -78
  169. package/dist/savings/saving-actions.js.map +0 -1
  170. package/dist/savings/saving-manager.js.map +0 -1
  171. package/dist/savings/savings-manager.d.ts +0 -126
  172. package/dist/savings/savings-manager.js +0 -234
  173. package/dist/savings/savings-manager.js.map +0 -1
  174. package/dist/savings/smart-savings.js.map +0 -1
  175. package/dist/savings/types.js.map +0 -1
  176. package/dist/svm/constant.js.map +0 -1
  177. package/dist/svm/index.js.map +0 -1
  178. package/dist/svm/svm.js.map +0 -1
  179. package/dist/svm/transactionParsing.js.map +0 -1
  180. package/dist/svm/transactionSender.js.map +0 -1
  181. package/dist/svm/utils.js.map +0 -1
  182. package/dist/test.js.map +0 -1
  183. package/dist/types.js.map +0 -1
  184. package/dist/utils.js.map +0 -1
  185. package/dist/vm.js.map +0 -1
  186. package/dist/walletBip32.js.map +0 -1
@@ -0,0 +1,179 @@
1
+ /**
2
+ * Rate Limiter for RPC calls
3
+ *
4
+ * Prevents overwhelming RPC endpoints with too many concurrent requests.
5
+ * Useful for wallet discovery, batch operations, and API calls.
6
+ */
7
+
8
+ export interface RateLimiterOptions {
9
+ /** Maximum concurrent operations */
10
+ maxConcurrent?: number;
11
+ /** Minimum delay between operations (ms) */
12
+ delayMs?: number;
13
+ /** Maximum queue size (0 = unlimited) */
14
+ maxQueueSize?: number;
15
+ }
16
+
17
+ /**
18
+ * Simple rate limiter with concurrency control
19
+ *
20
+ * @example
21
+ * ```typescript
22
+ * const limiter = new RateLimiter({ maxConcurrent: 5, delayMs: 200 });
23
+ *
24
+ * // Schedule multiple operations
25
+ * const results = await Promise.all(
26
+ * addresses.map(addr =>
27
+ * limiter.schedule(() => provider.getBalance(addr))
28
+ * )
29
+ * );
30
+ * ```
31
+ */
32
+ export class RateLimiter {
33
+ private running = 0;
34
+ private queue: Array<() => void> = [];
35
+ private lastExecutionTime = 0;
36
+
37
+ constructor(
38
+ protected options: RateLimiterOptions = {}
39
+ ) {
40
+ this.options.maxConcurrent = options.maxConcurrent || 5;
41
+ this.options.delayMs = options.delayMs || 100;
42
+ this.options.maxQueueSize = options.maxQueueSize || 0; // 0 = unlimited
43
+ }
44
+
45
+ /**
46
+ * Schedule an async operation with rate limiting
47
+ *
48
+ * @param fn - Async function to execute
49
+ * @returns Promise resolving to function result
50
+ * @throws Error if queue is full
51
+ */
52
+ async schedule<T>(fn: () => Promise<T>): Promise<T> {
53
+ // Check queue size
54
+ if (this.options.maxQueueSize! > 0 && this.queue.length >= this.options.maxQueueSize!) {
55
+ throw new Error(`Rate limiter queue full (${this.queue.length}/${this.options.maxQueueSize})`);
56
+ }
57
+
58
+ // Wait for available slot
59
+ await this.waitForSlot();
60
+
61
+ this.running++;
62
+
63
+ try {
64
+ // Enforce minimum delay between operations
65
+ const now = Date.now();
66
+ const timeSinceLastExecution = now - this.lastExecutionTime;
67
+
68
+ if (timeSinceLastExecution < this.options.delayMs!) {
69
+ await new Promise(resolve =>
70
+ setTimeout(resolve, this.options.delayMs! - timeSinceLastExecution)
71
+ );
72
+ }
73
+
74
+ this.lastExecutionTime = Date.now();
75
+
76
+ // Execute the function
77
+ return await fn();
78
+ } finally {
79
+ this.running--;
80
+ this.processQueue();
81
+ }
82
+ }
83
+
84
+ /**
85
+ * Wait for an available execution slot
86
+ */
87
+ private async waitForSlot(): Promise<void> {
88
+ if (this.running < this.options.maxConcurrent!) {
89
+ return;
90
+ }
91
+
92
+ return new Promise<void>(resolve => {
93
+ this.queue.push(resolve);
94
+ });
95
+ }
96
+
97
+ /**
98
+ * Process queued operations
99
+ */
100
+ private processQueue(): void {
101
+ if (this.queue.length > 0 && this.running < this.options.maxConcurrent!) {
102
+ const resolve = this.queue.shift();
103
+ if (resolve) {
104
+ resolve();
105
+ }
106
+ }
107
+ }
108
+
109
+ /**
110
+ * Get current stats
111
+ */
112
+ getStats() {
113
+ return {
114
+ running: this.running,
115
+ queued: this.queue.length,
116
+ maxConcurrent: this.options.maxConcurrent
117
+ };
118
+ }
119
+
120
+ /**
121
+ * Clear the queue
122
+ */
123
+ clear(): void {
124
+ this.queue = [];
125
+ }
126
+ }
127
+
128
+ /**
129
+ * Adaptive rate limiter that adjusts based on errors
130
+ *
131
+ * Automatically backs off when rate limit errors are detected.
132
+ */
133
+ export class AdaptiveRateLimiter extends RateLimiter {
134
+ private consecutiveErrors = 0;
135
+ private baseDelayMs: number;
136
+
137
+ constructor(options: RateLimiterOptions = {}) {
138
+ super(options);
139
+ this.baseDelayMs = options.delayMs || 100;
140
+ }
141
+
142
+ async schedule<T>(fn: () => Promise<T>): Promise<T> {
143
+ try {
144
+ const result = await super.schedule(fn);
145
+ // Success - reset error count
146
+ this.consecutiveErrors = 0;
147
+ this.options.delayMs = this.baseDelayMs;
148
+ return result;
149
+ } catch (error: any) {
150
+ // Check if it's a rate limit error
151
+ if (this.isRateLimitError(error)) {
152
+ this.consecutiveErrors++;
153
+
154
+ // Exponential backoff
155
+ this.options.delayMs = Math.min(
156
+ this.baseDelayMs * Math.pow(2, this.consecutiveErrors),
157
+ 10000 // Max 10 seconds
158
+ );
159
+
160
+ console.warn(
161
+ `Rate limit detected. Backing off to ${this.options.delayMs}ms delay`
162
+ );
163
+ }
164
+
165
+ throw error;
166
+ }
167
+ }
168
+
169
+ private isRateLimitError(error: any): boolean {
170
+ const message = error.message?.toLowerCase() || '';
171
+ return (
172
+ message.includes('rate limit') ||
173
+ message.includes('too many requests') ||
174
+ message.includes('429') ||
175
+ error.code === 'RATE_LIMIT' ||
176
+ error.code === 429
177
+ );
178
+ }
179
+ }
@@ -0,0 +1,271 @@
1
+ /**
2
+ * Intelligent retry logic for blockchain operations
3
+ *
4
+ * Distinguishes between transient and permanent errors,
5
+ * only retrying operations that have a chance of success.
6
+ */
7
+
8
+ export interface RetryOptions {
9
+ /** Maximum number of retries */
10
+ maxRetries?: number;
11
+ /** Initial delay in ms */
12
+ initialDelay?: number;
13
+ /** Maximum delay in ms */
14
+ maxDelay?: number;
15
+ /** Backoff multiplier */
16
+ backoffMultiplier?: number;
17
+ /** Custom retry predicate */
18
+ shouldRetry?: (error: any, attempt: number) => boolean;
19
+ /** Callback for retry attempts */
20
+ onRetry?: (error: any, attempt: number, delay: number) => void;
21
+ }
22
+
23
+ /**
24
+ * Retryable error codes
25
+ */
26
+ export const RETRYABLE_ERROR_CODES = new Set([
27
+ // Network errors
28
+ 'ECONNRESET',
29
+ 'ETIMEDOUT',
30
+ 'ENOTFOUND',
31
+ 'ECONNREFUSED',
32
+ 'ENETUNREACH',
33
+ 'EAI_AGAIN',
34
+
35
+ // HTTP errors
36
+ 'NETWORK_ERROR',
37
+ 'TIMEOUT',
38
+ 'SERVER_ERROR',
39
+
40
+ // RPC errors
41
+ 'RATE_LIMIT',
42
+ 'TOO_MANY_REQUESTS',
43
+ 'SERVICE_UNAVAILABLE',
44
+ 'GATEWAY_TIMEOUT',
45
+
46
+ // Blockchain errors
47
+ 'NONCE_EXPIRED',
48
+ 'REPLACEMENT_UNDERPRICED',
49
+ 'NETWORK_CHANGED'
50
+ ]);
51
+
52
+ /**
53
+ * Non-retryable error patterns
54
+ */
55
+ const NON_RETRYABLE_PATTERNS = [
56
+ 'insufficient funds',
57
+ 'invalid address',
58
+ 'invalid signature',
59
+ 'nonce too low',
60
+ 'gas too low',
61
+ 'execution reverted',
62
+ 'invalid opcode',
63
+ 'out of gas',
64
+ 'user denied',
65
+ 'user rejected',
66
+ 'already known',
67
+ 'intrinsic gas too low'
68
+ ];
69
+
70
+ /**
71
+ * Check if an error is retryable
72
+ *
73
+ * @param error - Error to check
74
+ * @returns true if error might be transient
75
+ */
76
+ export function isRetryableError(error: any): boolean {
77
+ // Check error code
78
+ if (error.code && RETRYABLE_ERROR_CODES.has(error.code)) {
79
+ return true;
80
+ }
81
+
82
+ // Check status code
83
+ if (error.status === 429 || error.status === 503 || error.status === 504) {
84
+ return true;
85
+ }
86
+
87
+ // Check error message
88
+ const message = error.message?.toLowerCase() || '';
89
+
90
+ // Check for retryable patterns
91
+ if (
92
+ message.includes('rate limit') ||
93
+ message.includes('timeout') ||
94
+ message.includes('network') ||
95
+ message.includes('connection') ||
96
+ message.includes('temporary') ||
97
+ message.includes('try again')
98
+ ) {
99
+ return true;
100
+ }
101
+
102
+ // Check for non-retryable patterns
103
+ for (const pattern of NON_RETRYABLE_PATTERNS) {
104
+ if (message.includes(pattern)) {
105
+ return false;
106
+ }
107
+ }
108
+
109
+ // Default: don't retry unknown errors
110
+ return false;
111
+ }
112
+
113
+ /**
114
+ * Calculate delay with exponential backoff and jitter
115
+ *
116
+ * @param attempt - Current attempt number (1-based)
117
+ * @param initialDelay - Initial delay in ms
118
+ * @param maxDelay - Maximum delay in ms
119
+ * @param multiplier - Backoff multiplier
120
+ * @returns Delay in ms
121
+ */
122
+ export function calculateBackoffDelay(
123
+ attempt: number,
124
+ initialDelay: number = 1000,
125
+ maxDelay: number = 30000,
126
+ multiplier: number = 2
127
+ ): number {
128
+ // Exponential backoff: initialDelay * multiplier^(attempt-1)
129
+ const exponentialDelay = initialDelay * Math.pow(multiplier, attempt - 1);
130
+
131
+ // Cap at maxDelay
132
+ const cappedDelay = Math.min(exponentialDelay, maxDelay);
133
+
134
+ // Add jitter (±20%) to prevent thundering herd
135
+ const jitter = cappedDelay * 0.2 * (Math.random() - 0.5);
136
+
137
+ return Math.floor(cappedDelay + jitter);
138
+ }
139
+
140
+ /**
141
+ * Retry an async operation with intelligent backoff
142
+ *
143
+ * @param operation - Async function to retry
144
+ * @param options - Retry options
145
+ * @returns Result of the operation
146
+ * @throws Last error if all retries exhausted
147
+ *
148
+ * @example
149
+ * ```typescript
150
+ * const balance = await retryWithBackoff(
151
+ * () => provider.getBalance(address),
152
+ * {
153
+ * maxRetries: 3,
154
+ * initialDelay: 1000,
155
+ * onRetry: (error, attempt, delay) => {
156
+ * console.log(`Retry ${attempt} after ${delay}ms:`, error.message);
157
+ * }
158
+ * }
159
+ * );
160
+ * ```
161
+ */
162
+ export async function retryWithBackoff<T>(
163
+ operation: () => Promise<T>,
164
+ options: RetryOptions = {}
165
+ ): Promise<T> {
166
+ const {
167
+ maxRetries = 3,
168
+ initialDelay = 1000,
169
+ maxDelay = 30000,
170
+ backoffMultiplier = 2,
171
+ shouldRetry = isRetryableError,
172
+ onRetry
173
+ } = options;
174
+
175
+ let lastError: any;
176
+
177
+ for (let attempt = 1; attempt <= maxRetries + 1; attempt++) {
178
+ try {
179
+ return await operation();
180
+ } catch (error: any) {
181
+ lastError = error;
182
+
183
+ // Check if we should retry
184
+ const isLastAttempt = attempt === maxRetries + 1;
185
+ const canRetry = shouldRetry(error, attempt);
186
+
187
+ if (isLastAttempt || !canRetry) {
188
+ // Attach retry metadata to error
189
+ error.attempts = attempt;
190
+ error.retryable = canRetry;
191
+ throw error;
192
+ }
193
+
194
+ // Calculate delay
195
+ const delay = calculateBackoffDelay(
196
+ attempt,
197
+ initialDelay,
198
+ maxDelay,
199
+ backoffMultiplier
200
+ );
201
+
202
+ // Notify caller
203
+ if (onRetry) {
204
+ onRetry(error, attempt, delay);
205
+ }
206
+
207
+ // Wait before retry
208
+ await new Promise(resolve => setTimeout(resolve, delay));
209
+ }
210
+ }
211
+
212
+ // Should not reach here, but TypeScript needs it
213
+ throw lastError;
214
+ }
215
+
216
+ /**
217
+ * Retry with linear backoff (simpler, more predictable)
218
+ *
219
+ * @param operation - Async function to retry
220
+ * @param maxRetries - Maximum retries
221
+ * @param delayMs - Fixed delay between retries
222
+ * @returns Result of the operation
223
+ */
224
+ export async function retryWithLinearBackoff<T>(
225
+ operation: () => Promise<T>,
226
+ maxRetries: number = 3,
227
+ delayMs: number = 1000
228
+ ): Promise<T> {
229
+ return retryWithBackoff(operation, {
230
+ maxRetries,
231
+ initialDelay: delayMs,
232
+ maxDelay: delayMs,
233
+ backoffMultiplier: 1 // No exponential growth
234
+ });
235
+ }
236
+
237
+ /**
238
+ * Retry for blockchain transactions with custom logic
239
+ *
240
+ * @param operation - Transaction operation
241
+ * @param options - Retry options
242
+ * @returns Transaction result
243
+ */
244
+ export async function retryTransaction<T>(
245
+ operation: () => Promise<T>,
246
+ options: RetryOptions = {}
247
+ ): Promise<T> {
248
+ return retryWithBackoff(operation, {
249
+ maxRetries: 5, // More retries for transactions
250
+ initialDelay: 2000, // Longer initial delay
251
+ maxDelay: 60000, // Up to 1 minute
252
+ ...options,
253
+ shouldRetry: (error, attempt) => {
254
+ // Custom logic for transactions
255
+ const message = error.message?.toLowerCase() || '';
256
+
257
+ // Don't retry user rejections
258
+ if (message.includes('user denied') || message.includes('user rejected')) {
259
+ return false;
260
+ }
261
+
262
+ // Don't retry if transaction already mined
263
+ if (message.includes('already known') || message.includes('nonce too low')) {
264
+ return false;
265
+ }
266
+
267
+ // Retry network/rate limit errors
268
+ return isRetryableError(error);
269
+ }
270
+ });
271
+ }