@circle-fin/adapter-ethers-v6 1.1.0 → 1.2.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/CHANGELOG.md +35 -0
- package/README.md +64 -52
- package/index.cjs +1743 -628
- package/index.d.ts +307 -69
- package/index.mjs +1742 -629
- package/package.json +1 -1
package/index.cjs
CHANGED
|
@@ -20,247 +20,1055 @@
|
|
|
20
20
|
|
|
21
21
|
var ethers = require('ethers');
|
|
22
22
|
var zod = require('zod');
|
|
23
|
-
require('@ethersproject/units');
|
|
24
23
|
var bytes = require('@ethersproject/bytes');
|
|
25
24
|
var address = require('@ethersproject/address');
|
|
26
25
|
var bs58 = require('bs58');
|
|
26
|
+
require('@ethersproject/units');
|
|
27
27
|
|
|
28
28
|
function _interopDefault (e) { return e && e.__esModule ? e.default : e; }
|
|
29
29
|
|
|
30
30
|
var bs58__default = /*#__PURE__*/_interopDefault(bs58);
|
|
31
31
|
|
|
32
32
|
/**
|
|
33
|
-
*
|
|
33
|
+
* Valid recoverability values for error handling strategies.
|
|
34
34
|
*
|
|
35
|
-
*
|
|
36
|
-
*
|
|
37
|
-
*
|
|
38
|
-
|
|
35
|
+
* - FATAL errors are thrown immediately (invalid inputs, insufficient funds)
|
|
36
|
+
* - RETRYABLE errors are returned when a flow fails to start but could work later
|
|
37
|
+
* - RESUMABLE errors are returned when a flow fails mid-execution but can be continued
|
|
38
|
+
*/
|
|
39
|
+
const RECOVERABILITY_VALUES = [
|
|
40
|
+
'RETRYABLE',
|
|
41
|
+
'RESUMABLE',
|
|
42
|
+
'FATAL',
|
|
43
|
+
];
|
|
44
|
+
/**
|
|
45
|
+
* Error type constants for categorizing errors by origin.
|
|
46
|
+
*
|
|
47
|
+
* This const object provides a reference for error types, enabling
|
|
48
|
+
* IDE autocomplete and preventing typos when creating custom errors.
|
|
39
49
|
*
|
|
40
50
|
* @remarks
|
|
41
|
-
*
|
|
42
|
-
* safety
|
|
43
|
-
*
|
|
44
|
-
*
|
|
51
|
+
* While internal error definitions use string literals with type annotations
|
|
52
|
+
* for strict type safety, this constant is useful for developers creating
|
|
53
|
+
* custom error instances or checking error types programmatically.
|
|
54
|
+
*
|
|
55
|
+
* @example
|
|
56
|
+
* ```typescript
|
|
57
|
+
* import { ERROR_TYPES, KitError } from '@core/errors'
|
|
58
|
+
*
|
|
59
|
+
* // Use for type checking
|
|
60
|
+
* if (error.type === ERROR_TYPES.BALANCE) {
|
|
61
|
+
* console.log('This is a balance error')
|
|
62
|
+
* }
|
|
63
|
+
* ```
|
|
64
|
+
*
|
|
65
|
+
* @example
|
|
66
|
+
* ```typescript
|
|
67
|
+
* // Use as reference when creating custom errors
|
|
68
|
+
* const error = new KitError({
|
|
69
|
+
* code: 9999,
|
|
70
|
+
* name: 'CUSTOM_ERROR',
|
|
71
|
+
* type: ERROR_TYPES.BALANCE, // IDE autocomplete works here
|
|
72
|
+
* recoverability: 'FATAL',
|
|
73
|
+
* message: 'Custom balance error'
|
|
74
|
+
* })
|
|
75
|
+
* ```
|
|
45
76
|
*/
|
|
46
|
-
|
|
47
|
-
|
|
77
|
+
const ERROR_TYPES = {
|
|
78
|
+
/** User input validation and parameter checking */
|
|
79
|
+
INPUT: 'INPUT',
|
|
80
|
+
/** Insufficient token balances and amount validation */
|
|
81
|
+
BALANCE: 'BALANCE',
|
|
82
|
+
/** On-chain execution: reverts, gas issues, transaction failures */
|
|
83
|
+
ONCHAIN: 'ONCHAIN',
|
|
84
|
+
/** Blockchain RPC provider issues and endpoint problems */
|
|
85
|
+
RPC: 'RPC',
|
|
86
|
+
/** Internet connectivity, DNS resolution, connection issues */
|
|
87
|
+
NETWORK: 'NETWORK',
|
|
88
|
+
};
|
|
89
|
+
/**
|
|
90
|
+
* Array of valid error type values for validation.
|
|
91
|
+
* Derived from ERROR_TYPES const object.
|
|
92
|
+
*/
|
|
93
|
+
const ERROR_TYPE_VALUES = Object.values(ERROR_TYPES);
|
|
94
|
+
|
|
95
|
+
// Create mutable arrays for Zod enum validation
|
|
96
|
+
const RECOVERABILITY_ARRAY = [...RECOVERABILITY_VALUES];
|
|
97
|
+
const ERROR_TYPE_ARRAY = [...ERROR_TYPE_VALUES];
|
|
98
|
+
/**
|
|
99
|
+
* Error code ranges for validation.
|
|
100
|
+
* Single source of truth for valid error code ranges.
|
|
101
|
+
*/
|
|
102
|
+
const ERROR_CODE_RANGES = [
|
|
103
|
+
{ min: 1000, max: 1999, type: 'INPUT' },
|
|
104
|
+
{ min: 3000, max: 3999, type: 'NETWORK' },
|
|
105
|
+
{ min: 4000, max: 4999, type: 'RPC' },
|
|
106
|
+
{ min: 5000, max: 5999, type: 'ONCHAIN' },
|
|
107
|
+
{ min: 9000, max: 9999, type: 'BALANCE' },
|
|
108
|
+
];
|
|
109
|
+
/**
|
|
110
|
+
* Zod schema for validating ErrorDetails objects.
|
|
111
|
+
*
|
|
112
|
+
* This schema provides runtime validation for all ErrorDetails properties,
|
|
113
|
+
* ensuring type safety and proper error handling for JavaScript consumers.
|
|
114
|
+
*
|
|
115
|
+
* @example
|
|
116
|
+
* ```typescript
|
|
117
|
+
* import { errorDetailsSchema } from '@core/errors'
|
|
118
|
+
*
|
|
119
|
+
* const result = errorDetailsSchema.safeParse({
|
|
120
|
+
* code: 1001,
|
|
121
|
+
* name: 'INPUT_NETWORK_MISMATCH',
|
|
122
|
+
* type: 'INPUT',
|
|
123
|
+
* recoverability: 'FATAL',
|
|
124
|
+
* message: 'Source and destination networks must be different'
|
|
125
|
+
* })
|
|
126
|
+
*
|
|
127
|
+
* if (!result.success) {
|
|
128
|
+
* console.error('Validation failed:', result.error.issues)
|
|
129
|
+
* }
|
|
130
|
+
* ```
|
|
131
|
+
*
|
|
132
|
+
* @example
|
|
133
|
+
* ```typescript
|
|
134
|
+
* // Runtime error
|
|
135
|
+
* const result = errorDetailsSchema.safeParse({
|
|
136
|
+
* code: 9001,
|
|
137
|
+
* name: 'BALANCE_INSUFFICIENT_TOKEN',
|
|
138
|
+
* type: 'BALANCE',
|
|
139
|
+
* recoverability: 'FATAL',
|
|
140
|
+
* message: 'Insufficient USDC balance'
|
|
141
|
+
* })
|
|
142
|
+
* ```
|
|
143
|
+
*/
|
|
144
|
+
const errorDetailsSchema = zod.z.object({
|
|
48
145
|
/**
|
|
49
|
-
*
|
|
50
|
-
*
|
|
51
|
-
*
|
|
52
|
-
*
|
|
53
|
-
*
|
|
54
|
-
*
|
|
55
|
-
*
|
|
56
|
-
* @typeParam TActionKey - The specific action key being registered.
|
|
57
|
-
* @param action - The action key to register the handler for.
|
|
58
|
-
* @param handler - The handler function for processing this action type.
|
|
59
|
-
* @returns Void.
|
|
60
|
-
*
|
|
61
|
-
* @throws Error When action parameter is not a valid string.
|
|
62
|
-
* @throws TypeError When handler parameter is not a function.
|
|
63
|
-
*
|
|
64
|
-
* @example
|
|
65
|
-
* ```typescript
|
|
66
|
-
* import { ActionRegistry } from '@core/adapter'
|
|
67
|
-
* import type { ActionHandler } from '@core/adapter'
|
|
68
|
-
*
|
|
69
|
-
* const registry = new ActionRegistry()
|
|
70
|
-
*
|
|
71
|
-
* // Register a CCTP deposit handler
|
|
72
|
-
* const depositHandler: ActionHandler<'cctp.v2.depositForBurn'> = async (params, resolved) => {
|
|
73
|
-
* console.log('Processing deposit:', params.amount)
|
|
74
|
-
* return {
|
|
75
|
-
* chainId: params.chainId,
|
|
76
|
-
* data: '0x...',
|
|
77
|
-
* to: '0x...',
|
|
78
|
-
* value: '0'
|
|
79
|
-
* }
|
|
80
|
-
* }
|
|
81
|
-
*
|
|
82
|
-
* registry.registerHandler('cctp.v2.depositForBurn', depositHandler)
|
|
83
|
-
* ```
|
|
146
|
+
* Numeric identifier following standardized ranges:
|
|
147
|
+
* - 1000-1999: INPUT errors - Parameter validation
|
|
148
|
+
* - 3000-3999: NETWORK errors - Connectivity issues
|
|
149
|
+
* - 4000-4999: RPC errors - Provider issues, gas estimation
|
|
150
|
+
* - 5000-5999: ONCHAIN errors - Transaction/simulation failures
|
|
151
|
+
* - 9000-9999: BALANCE errors - Insufficient funds
|
|
84
152
|
*/
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
153
|
+
code: zod.z
|
|
154
|
+
.number()
|
|
155
|
+
.int('Error code must be an integer')
|
|
156
|
+
.refine((code) => ERROR_CODE_RANGES.some((range) => code >= range.min && code <= range.max), {
|
|
157
|
+
message: 'Error code must be in valid ranges: 1000-1999 (INPUT), 3000-3999 (NETWORK), 4000-4999 (RPC), 5000-5999 (ONCHAIN), 9000-9999 (BALANCE)',
|
|
158
|
+
}),
|
|
159
|
+
/** Human-readable ID (e.g., "INPUT_NETWORK_MISMATCH", "BALANCE_INSUFFICIENT_TOKEN") */
|
|
160
|
+
name: zod.z
|
|
161
|
+
.string()
|
|
162
|
+
.min(1, 'Error name must be a non-empty string')
|
|
163
|
+
.regex(/^[A-Z_][A-Z0-9_]*$/, 'Error name must match pattern: ^[A-Z_][A-Z0-9_]*$'),
|
|
164
|
+
/** Error category indicating where the error originated */
|
|
165
|
+
type: zod.z.enum(ERROR_TYPE_ARRAY, {
|
|
166
|
+
errorMap: () => ({
|
|
167
|
+
message: 'Error type must be one of: INPUT, BALANCE, ONCHAIN, RPC, NETWORK',
|
|
168
|
+
}),
|
|
169
|
+
}),
|
|
170
|
+
/** Error handling strategy */
|
|
171
|
+
recoverability: zod.z.enum(RECOVERABILITY_ARRAY, {
|
|
172
|
+
errorMap: () => ({
|
|
173
|
+
message: 'Recoverability must be one of: RETRYABLE, RESUMABLE, FATAL',
|
|
174
|
+
}),
|
|
175
|
+
}),
|
|
176
|
+
/** User-friendly explanation with context */
|
|
177
|
+
message: zod.z
|
|
178
|
+
.string()
|
|
179
|
+
.min(1, 'Error message must be a non-empty string')
|
|
180
|
+
.max(1000, 'Error message must be 1000 characters or less'),
|
|
181
|
+
/** Raw error details, context, or the original error that caused this one. */
|
|
182
|
+
cause: zod.z
|
|
183
|
+
.object({
|
|
184
|
+
/** Free-form error payload from underlying system */
|
|
185
|
+
trace: zod.z.unknown().optional(),
|
|
186
|
+
})
|
|
187
|
+
.optional(),
|
|
188
|
+
});
|
|
189
|
+
|
|
190
|
+
/**
|
|
191
|
+
* Validates an ErrorDetails object using Zod schema.
|
|
192
|
+
*
|
|
193
|
+
* @param details - The object to validate
|
|
194
|
+
* @returns The validated ErrorDetails object
|
|
195
|
+
* @throws TypeError When validation fails
|
|
196
|
+
*
|
|
197
|
+
* @example
|
|
198
|
+
* ```typescript
|
|
199
|
+
* import { validateErrorDetails } from '@core/errors'
|
|
200
|
+
*
|
|
201
|
+
* try {
|
|
202
|
+
* const validDetails = validateErrorDetails({
|
|
203
|
+
* code: 1001,
|
|
204
|
+
* name: 'NETWORK_MISMATCH',
|
|
205
|
+
* recoverability: 'FATAL',
|
|
206
|
+
* message: 'Source and destination networks must be different'
|
|
207
|
+
* })
|
|
208
|
+
* } catch (error) {
|
|
209
|
+
* console.error('Validation failed:', error.message)
|
|
210
|
+
* }
|
|
211
|
+
* ```
|
|
212
|
+
*/
|
|
213
|
+
function validateErrorDetails(details) {
|
|
214
|
+
const result = errorDetailsSchema.safeParse(details);
|
|
215
|
+
if (!result.success) {
|
|
216
|
+
const issues = result.error.issues
|
|
217
|
+
.map((issue) => `${issue.path.join('.')}: ${issue.message}`)
|
|
218
|
+
.join(', ');
|
|
219
|
+
throw new TypeError(`Invalid ErrorDetails: ${issues}`);
|
|
96
220
|
}
|
|
221
|
+
return result.data;
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
/**
|
|
225
|
+
* Maximum length for error messages in fallback validation errors.
|
|
226
|
+
*
|
|
227
|
+
* KitError enforces a 1000-character limit on error messages. When creating
|
|
228
|
+
* fallback validation errors that combine multiple Zod issues, we use 950
|
|
229
|
+
* characters to leave a 50-character buffer for:
|
|
230
|
+
* - The error message prefix ("Invalid bridge parameters: ")
|
|
231
|
+
* - Potential encoding differences or formatting overhead
|
|
232
|
+
* - Safety margin to prevent KitError constructor failures
|
|
233
|
+
*
|
|
234
|
+
* This ensures that even with concatenated issue summaries, the final message
|
|
235
|
+
* stays within KitError's constraints.
|
|
236
|
+
*/
|
|
237
|
+
const MAX_MESSAGE_LENGTH = 950;
|
|
238
|
+
|
|
239
|
+
/**
|
|
240
|
+
* Structured error class for Stablecoin Kit operations.
|
|
241
|
+
*
|
|
242
|
+
* This class extends the native Error class while implementing the ErrorDetails
|
|
243
|
+
* interface, providing a consistent error format for programmatic handling
|
|
244
|
+
* across the Stablecoin Kits ecosystem. All properties are immutable to ensure
|
|
245
|
+
* error objects cannot be modified after creation.
|
|
246
|
+
*
|
|
247
|
+
* @example
|
|
248
|
+
* ```typescript
|
|
249
|
+
* import { KitError } from '@core/errors'
|
|
250
|
+
*
|
|
251
|
+
* const error = new KitError({
|
|
252
|
+
* code: 1001,
|
|
253
|
+
* name: 'INPUT_NETWORK_MISMATCH',
|
|
254
|
+
* recoverability: 'FATAL',
|
|
255
|
+
* message: 'Cannot bridge between mainnet and testnet'
|
|
256
|
+
* })
|
|
257
|
+
*
|
|
258
|
+
* if (error instanceof KitError) {
|
|
259
|
+
* console.log(`Error ${error.code}: ${error.name}`)
|
|
260
|
+
* // → "Error 1001: INPUT_NETWORK_MISMATCH"
|
|
261
|
+
* }
|
|
262
|
+
* ```
|
|
263
|
+
*
|
|
264
|
+
* @example
|
|
265
|
+
* ```typescript
|
|
266
|
+
* import { KitError } from '@core/errors'
|
|
267
|
+
*
|
|
268
|
+
* // Error with cause information
|
|
269
|
+
* const error = new KitError({
|
|
270
|
+
* code: 1002,
|
|
271
|
+
* name: 'INVALID_AMOUNT',
|
|
272
|
+
* recoverability: 'FATAL',
|
|
273
|
+
* message: 'Amount must be greater than zero',
|
|
274
|
+
* cause: {
|
|
275
|
+
* trace: { providedAmount: -100, minimumAmount: 0 }
|
|
276
|
+
* }
|
|
277
|
+
* })
|
|
278
|
+
*
|
|
279
|
+
* throw error
|
|
280
|
+
* ```
|
|
281
|
+
*/
|
|
282
|
+
class KitError extends Error {
|
|
283
|
+
/** Numeric identifier following standardized ranges (1000+ for INPUT errors) */
|
|
284
|
+
code;
|
|
285
|
+
/** Human-readable ID (e.g., "NETWORK_MISMATCH") */
|
|
286
|
+
name;
|
|
287
|
+
/** Error category indicating where the error originated */
|
|
288
|
+
type;
|
|
289
|
+
/** Error handling strategy */
|
|
290
|
+
recoverability;
|
|
291
|
+
/** Raw error details, context, or the original error that caused this one. */
|
|
292
|
+
cause;
|
|
97
293
|
/**
|
|
98
|
-
*
|
|
99
|
-
*
|
|
100
|
-
* Efficiently register multiple handlers from a record object, where keys
|
|
101
|
-
* are action identifiers and values are their corresponding handler
|
|
102
|
-
* functions. Provides a convenient way to bulk-register handlers while
|
|
103
|
-
* maintaining type safety.
|
|
104
|
-
*
|
|
105
|
-
* @param handlers - A record mapping action keys to their handler functions.
|
|
106
|
-
* @returns Void.
|
|
107
|
-
*
|
|
108
|
-
* @throws {Error} When handlers parameter is not a valid object.
|
|
109
|
-
* @throws {Error} When any individual handler registration fails.
|
|
294
|
+
* Create a new KitError instance.
|
|
110
295
|
*
|
|
111
|
-
* @
|
|
112
|
-
*
|
|
113
|
-
* import { ActionRegistry } from '@core/adapter'
|
|
114
|
-
* import type { ActionHandler, ActionHandlers } from '@core/adapter'
|
|
115
|
-
*
|
|
116
|
-
* const registry = new ActionRegistry()
|
|
117
|
-
*
|
|
118
|
-
* // Register multiple handlers at once
|
|
119
|
-
* const tokenHandlers: ActionHandlers = {
|
|
120
|
-
* 'token.approve': async (params, resolved) => ({
|
|
121
|
-
* chainId: resolved.chain,
|
|
122
|
-
* data: '0x095ea7b3...',
|
|
123
|
-
* to: params.tokenAddress,
|
|
124
|
-
* value: '0'
|
|
125
|
-
* }),
|
|
126
|
-
* 'token.transfer': async (params, resolved) => ({
|
|
127
|
-
* chainId: resolved.chain,
|
|
128
|
-
* data: '0xa9059cbb...',
|
|
129
|
-
* to: params.tokenAddress,
|
|
130
|
-
* value: '0'
|
|
131
|
-
* })
|
|
132
|
-
* }
|
|
133
|
-
*
|
|
134
|
-
* registry.registerHandlers(tokenHandlers)
|
|
135
|
-
* console.log('Registered multiple token handlers')
|
|
136
|
-
* ```
|
|
296
|
+
* @param details - The error details object containing all required properties.
|
|
297
|
+
* @throws \{TypeError\} When details parameter is missing or invalid.
|
|
137
298
|
*/
|
|
138
|
-
|
|
139
|
-
//
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
// Register each handler individually to benefit from per-handler validation
|
|
144
|
-
for (const [action, handler] of Object.entries(handlers)) {
|
|
145
|
-
this.registerHandler(action, handler);
|
|
299
|
+
constructor(details) {
|
|
300
|
+
// Truncate message if it exceeds maximum length to prevent validation errors
|
|
301
|
+
let message = details.message;
|
|
302
|
+
if (message.length > MAX_MESSAGE_LENGTH) {
|
|
303
|
+
message = `${message.slice(0, MAX_MESSAGE_LENGTH - 3)}...`;
|
|
146
304
|
}
|
|
305
|
+
const truncatedDetails = { ...details, message };
|
|
306
|
+
// Validate input at runtime for JavaScript consumers using Zod
|
|
307
|
+
const validatedDetails = validateErrorDetails(truncatedDetails);
|
|
308
|
+
super(validatedDetails.message);
|
|
309
|
+
// Set properties as readonly at runtime
|
|
310
|
+
Object.defineProperties(this, {
|
|
311
|
+
name: {
|
|
312
|
+
value: validatedDetails.name,
|
|
313
|
+
writable: false,
|
|
314
|
+
enumerable: true,
|
|
315
|
+
configurable: false,
|
|
316
|
+
},
|
|
317
|
+
code: {
|
|
318
|
+
value: validatedDetails.code,
|
|
319
|
+
writable: false,
|
|
320
|
+
enumerable: true,
|
|
321
|
+
configurable: false,
|
|
322
|
+
},
|
|
323
|
+
type: {
|
|
324
|
+
value: validatedDetails.type,
|
|
325
|
+
writable: false,
|
|
326
|
+
enumerable: true,
|
|
327
|
+
configurable: false,
|
|
328
|
+
},
|
|
329
|
+
recoverability: {
|
|
330
|
+
value: validatedDetails.recoverability,
|
|
331
|
+
writable: false,
|
|
332
|
+
enumerable: true,
|
|
333
|
+
configurable: false,
|
|
334
|
+
},
|
|
335
|
+
...(validatedDetails.cause && {
|
|
336
|
+
cause: {
|
|
337
|
+
value: validatedDetails.cause,
|
|
338
|
+
writable: false,
|
|
339
|
+
enumerable: true,
|
|
340
|
+
configurable: false,
|
|
341
|
+
},
|
|
342
|
+
}),
|
|
343
|
+
});
|
|
147
344
|
}
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
/**
|
|
348
|
+
* Standardized error code ranges for consistent categorization:
|
|
349
|
+
*
|
|
350
|
+
* - 1000-1999: INPUT errors - Parameter validation, input format errors
|
|
351
|
+
* - 3000-3999: NETWORK errors - Internet connectivity, DNS, connection issues
|
|
352
|
+
* - 4000-4999: RPC errors - Blockchain provider issues, gas estimation, nonce errors
|
|
353
|
+
* - 5000-5999: ONCHAIN errors - Transaction/simulation failures, gas exhaustion, reverts
|
|
354
|
+
* - 9000-9999: BALANCE errors - Insufficient funds, token balance, allowance
|
|
355
|
+
*/
|
|
356
|
+
/**
|
|
357
|
+
* Standardized error definitions for INPUT type errors.
|
|
358
|
+
*
|
|
359
|
+
* Each entry combines the numeric error code, string name, and type
|
|
360
|
+
* to ensure consistency when creating error instances.
|
|
361
|
+
*
|
|
362
|
+
* Error codes follow a hierarchical numbering scheme where the first digit
|
|
363
|
+
* indicates the error category (1 = INPUT) and subsequent digits provide
|
|
364
|
+
* specific error identification within that category.
|
|
365
|
+
*
|
|
366
|
+
*
|
|
367
|
+
* @example
|
|
368
|
+
* ```typescript
|
|
369
|
+
* import { InputError } from '@core/errors'
|
|
370
|
+
*
|
|
371
|
+
* const error = new KitError({
|
|
372
|
+
* ...InputError.NETWORK_MISMATCH,
|
|
373
|
+
* recoverability: 'FATAL',
|
|
374
|
+
* message: 'Source and destination networks must be different'
|
|
375
|
+
* })
|
|
376
|
+
*
|
|
377
|
+
* // Access code, name, and type individually if needed
|
|
378
|
+
* console.log(InputError.NETWORK_MISMATCH.code) // 1001
|
|
379
|
+
* console.log(InputError.NETWORK_MISMATCH.name) // 'INPUT_NETWORK_MISMATCH'
|
|
380
|
+
* console.log(InputError.NETWORK_MISMATCH.type) // 'INPUT'
|
|
381
|
+
* ```
|
|
382
|
+
*/
|
|
383
|
+
const InputError = {
|
|
384
|
+
/** Invalid or unsupported chain identifier */
|
|
385
|
+
INVALID_CHAIN: {
|
|
386
|
+
code: 1005,
|
|
387
|
+
name: 'INPUT_INVALID_CHAIN',
|
|
388
|
+
type: 'INPUT',
|
|
389
|
+
},
|
|
390
|
+
/** General validation failure for complex validation rules */
|
|
391
|
+
VALIDATION_FAILED: {
|
|
392
|
+
code: 1098,
|
|
393
|
+
name: 'INPUT_VALIDATION_FAILED',
|
|
394
|
+
type: 'INPUT',
|
|
395
|
+
},
|
|
396
|
+
};
|
|
397
|
+
/**
|
|
398
|
+
* Standardized error definitions for BALANCE type errors.
|
|
399
|
+
*
|
|
400
|
+
* BALANCE errors indicate insufficient funds or allowance issues
|
|
401
|
+
* that prevent transaction execution.
|
|
402
|
+
*
|
|
403
|
+
* @example
|
|
404
|
+
* ```typescript
|
|
405
|
+
* import { BalanceError } from '@core/errors'
|
|
406
|
+
*
|
|
407
|
+
* const error = new KitError({
|
|
408
|
+
* ...BalanceError.INSUFFICIENT_TOKEN,
|
|
409
|
+
* recoverability: 'FATAL',
|
|
410
|
+
* message: 'Insufficient USDC balance on Ethereum',
|
|
411
|
+
* cause: { trace: { required: '100', available: '50' } }
|
|
412
|
+
* })
|
|
413
|
+
* ```
|
|
414
|
+
*/
|
|
415
|
+
const BalanceError = {
|
|
416
|
+
/** Insufficient token balance for transaction */
|
|
417
|
+
INSUFFICIENT_TOKEN: {
|
|
418
|
+
code: 9001,
|
|
419
|
+
name: 'BALANCE_INSUFFICIENT_TOKEN',
|
|
420
|
+
type: 'BALANCE',
|
|
421
|
+
},
|
|
422
|
+
/** Insufficient native token (ETH/SOL/etc) for gas fees */
|
|
423
|
+
INSUFFICIENT_GAS: {
|
|
424
|
+
code: 9002,
|
|
425
|
+
name: 'BALANCE_INSUFFICIENT_GAS',
|
|
426
|
+
type: 'BALANCE',
|
|
427
|
+
}};
|
|
428
|
+
/**
|
|
429
|
+
* Standardized error definitions for ONCHAIN type errors.
|
|
430
|
+
*
|
|
431
|
+
* ONCHAIN errors occur during transaction execution, simulation,
|
|
432
|
+
* or interaction with smart contracts on the blockchain.
|
|
433
|
+
*
|
|
434
|
+
* @example
|
|
435
|
+
* ```typescript
|
|
436
|
+
* import { OnchainError } from '@core/errors'
|
|
437
|
+
*
|
|
438
|
+
* const error = new KitError({
|
|
439
|
+
* ...OnchainError.SIMULATION_FAILED,
|
|
440
|
+
* recoverability: 'FATAL',
|
|
441
|
+
* message: 'Simulation failed: ERC20 transfer amount exceeds balance',
|
|
442
|
+
* cause: { trace: { reason: 'ERC20: transfer amount exceeds balance' } }
|
|
443
|
+
* })
|
|
444
|
+
* ```
|
|
445
|
+
*/
|
|
446
|
+
const OnchainError = {
|
|
447
|
+
/** Transaction reverted on-chain after execution */
|
|
448
|
+
TRANSACTION_REVERTED: {
|
|
449
|
+
code: 5001,
|
|
450
|
+
name: 'ONCHAIN_TRANSACTION_REVERTED',
|
|
451
|
+
type: 'ONCHAIN',
|
|
452
|
+
},
|
|
453
|
+
/** Pre-flight transaction simulation failed */
|
|
454
|
+
SIMULATION_FAILED: {
|
|
455
|
+
code: 5002,
|
|
456
|
+
name: 'ONCHAIN_SIMULATION_FAILED',
|
|
457
|
+
type: 'ONCHAIN',
|
|
458
|
+
},
|
|
459
|
+
/** Transaction ran out of gas during execution */
|
|
460
|
+
OUT_OF_GAS: {
|
|
461
|
+
code: 5003,
|
|
462
|
+
name: 'ONCHAIN_OUT_OF_GAS',
|
|
463
|
+
type: 'ONCHAIN',
|
|
464
|
+
}};
|
|
465
|
+
/**
|
|
466
|
+
* Standardized error definitions for RPC type errors.
|
|
467
|
+
*
|
|
468
|
+
* RPC errors occur when communicating with blockchain RPC providers,
|
|
469
|
+
* including endpoint failures, invalid responses, and provider-specific issues.
|
|
470
|
+
*
|
|
471
|
+
* @example
|
|
472
|
+
* ```typescript
|
|
473
|
+
* import { RpcError } from '@core/errors'
|
|
474
|
+
*
|
|
475
|
+
* const error = new KitError({
|
|
476
|
+
* ...RpcError.ENDPOINT_ERROR,
|
|
477
|
+
* recoverability: 'RETRYABLE',
|
|
478
|
+
* message: 'RPC endpoint unavailable on Ethereum',
|
|
479
|
+
* cause: { trace: { endpoint: 'https://mainnet.infura.io' } }
|
|
480
|
+
* })
|
|
481
|
+
* ```
|
|
482
|
+
*/
|
|
483
|
+
const RpcError = {
|
|
484
|
+
/** RPC endpoint returned error or is unavailable */
|
|
485
|
+
ENDPOINT_ERROR: {
|
|
486
|
+
code: 4001,
|
|
487
|
+
name: 'RPC_ENDPOINT_ERROR',
|
|
488
|
+
type: 'RPC',
|
|
489
|
+
}};
|
|
490
|
+
/**
|
|
491
|
+
* Standardized error definitions for NETWORK type errors.
|
|
492
|
+
*
|
|
493
|
+
* NETWORK errors indicate connectivity issues at the network layer,
|
|
494
|
+
* including DNS failures, connection timeouts, and unreachable endpoints.
|
|
495
|
+
*
|
|
496
|
+
* @example
|
|
497
|
+
* ```typescript
|
|
498
|
+
* import { NetworkError } from '@core/errors'
|
|
499
|
+
*
|
|
500
|
+
* const error = new KitError({
|
|
501
|
+
* ...NetworkError.CONNECTION_FAILED,
|
|
502
|
+
* recoverability: 'RETRYABLE',
|
|
503
|
+
* message: 'Failed to connect to Ethereum network',
|
|
504
|
+
* cause: { trace: { error: 'ECONNREFUSED' } }
|
|
505
|
+
* })
|
|
506
|
+
* ```
|
|
507
|
+
*/
|
|
508
|
+
const NetworkError = {
|
|
509
|
+
/** Network connection failed or unreachable */
|
|
510
|
+
CONNECTION_FAILED: {
|
|
511
|
+
code: 3001,
|
|
512
|
+
name: 'NETWORK_CONNECTION_FAILED',
|
|
513
|
+
type: 'NETWORK',
|
|
514
|
+
}};
|
|
515
|
+
|
|
516
|
+
/**
|
|
517
|
+
* Creates error for invalid chain configuration.
|
|
518
|
+
*
|
|
519
|
+
* This error is thrown when the provided chain doesn't meet the required
|
|
520
|
+
* configuration or is not supported for the operation.
|
|
521
|
+
*
|
|
522
|
+
* @param chain - The invalid chain name or identifier
|
|
523
|
+
* @param reason - Specific reason why chain is invalid
|
|
524
|
+
* @returns KitError with chain details and validation rule
|
|
525
|
+
*
|
|
526
|
+
* @example
|
|
527
|
+
* ```typescript
|
|
528
|
+
* import { createInvalidChainError } from '@core/errors'
|
|
529
|
+
*
|
|
530
|
+
* throw createInvalidChainError('UnknownChain', 'Chain is not supported by this bridge')
|
|
531
|
+
* // Message: "Invalid chain 'UnknownChain': Chain is not supported by this bridge"
|
|
532
|
+
* ```
|
|
533
|
+
*/
|
|
534
|
+
function createInvalidChainError(chain, reason) {
|
|
535
|
+
const errorDetails = {
|
|
536
|
+
...InputError.INVALID_CHAIN,
|
|
537
|
+
recoverability: 'FATAL',
|
|
538
|
+
message: `Invalid chain '${chain}': ${reason}`,
|
|
539
|
+
cause: {
|
|
540
|
+
trace: { chain, reason },
|
|
541
|
+
},
|
|
542
|
+
};
|
|
543
|
+
return new KitError(errorDetails);
|
|
544
|
+
}
|
|
545
|
+
/**
|
|
546
|
+
* Creates a KitError from a Zod validation error with detailed error information.
|
|
547
|
+
*
|
|
548
|
+
* This factory function converts Zod validation failures into standardized KitError
|
|
549
|
+
* instances with INPUT_VALIDATION_FAILED code (1098). It extracts all Zod issues
|
|
550
|
+
* and includes them in both the error message and trace for debugging, providing
|
|
551
|
+
* developers with comprehensive validation feedback.
|
|
552
|
+
*
|
|
553
|
+
* The error message includes all validation errors concatenated with semicolons.
|
|
554
|
+
*
|
|
555
|
+
* @param zodError - The Zod validation error containing one or more validation issues
|
|
556
|
+
* @param context - Context string describing what was being validated (e.g., 'bridge parameters', 'user input')
|
|
557
|
+
* @returns KitError with INPUT_VALIDATION_FAILED code and structured validation details
|
|
558
|
+
*
|
|
559
|
+
* @example
|
|
560
|
+
* ```typescript
|
|
561
|
+
* import { createValidationErrorFromZod } from '@core/errors'
|
|
562
|
+
* import { z } from 'zod'
|
|
563
|
+
*
|
|
564
|
+
* const schema = z.object({
|
|
565
|
+
* name: z.string().min(3),
|
|
566
|
+
* age: z.number().positive()
|
|
567
|
+
* })
|
|
568
|
+
*
|
|
569
|
+
* const result = schema.safeParse({ name: 'ab', age: -1 })
|
|
570
|
+
* if (!result.success) {
|
|
571
|
+
* throw createValidationErrorFromZod(result.error, 'user data')
|
|
572
|
+
* }
|
|
573
|
+
* // Throws: KitError with message:
|
|
574
|
+
* // "Invalid user data: name: String must contain at least 3 character(s); age: Number must be greater than 0"
|
|
575
|
+
* // And cause.trace.validationErrors containing all validation errors as an array
|
|
576
|
+
* ```
|
|
577
|
+
*
|
|
578
|
+
* @example
|
|
579
|
+
* ```typescript
|
|
580
|
+
* // Usage in validation functions
|
|
581
|
+
* import { createValidationErrorFromZod } from '@core/errors'
|
|
582
|
+
*
|
|
583
|
+
* function validateBridgeParams(params: unknown): asserts params is BridgeParams {
|
|
584
|
+
* const result = bridgeParamsSchema.safeParse(params)
|
|
585
|
+
* if (!result.success) {
|
|
586
|
+
* throw createValidationErrorFromZod(result.error, 'bridge parameters')
|
|
587
|
+
* }
|
|
588
|
+
* }
|
|
589
|
+
* ```
|
|
590
|
+
*/
|
|
591
|
+
function createValidationErrorFromZod(zodError, context) {
|
|
592
|
+
// Format each Zod issue as "path: message"
|
|
593
|
+
const validationErrors = zodError.issues.map((issue) => {
|
|
594
|
+
const path = issue.path.length > 0 ? `${issue.path.join('.')}: ` : '';
|
|
595
|
+
return `${path}${issue.message}`;
|
|
596
|
+
});
|
|
597
|
+
// Join all errors with semicolons to show complete validation feedback
|
|
598
|
+
const issueSummary = validationErrors.join('; ');
|
|
599
|
+
const allErrors = issueSummary || 'Invalid Input';
|
|
600
|
+
// Build full message from context and validation errors
|
|
601
|
+
const fullMessage = `Invalid ${context}: ${allErrors}`;
|
|
602
|
+
const errorDetails = {
|
|
603
|
+
...InputError.VALIDATION_FAILED,
|
|
604
|
+
recoverability: 'FATAL',
|
|
605
|
+
message: fullMessage,
|
|
606
|
+
cause: {
|
|
607
|
+
trace: {
|
|
608
|
+
validationErrors, // Array of formatted error strings for display
|
|
609
|
+
zodError: zodError.message, // Original Zod error message
|
|
610
|
+
zodIssues: zodError.issues, // Full Zod issues array for debugging
|
|
611
|
+
},
|
|
612
|
+
},
|
|
613
|
+
};
|
|
614
|
+
return new KitError(errorDetails);
|
|
615
|
+
}
|
|
616
|
+
|
|
617
|
+
/**
|
|
618
|
+
* Creates error for insufficient token balance.
|
|
619
|
+
*
|
|
620
|
+
* This error is thrown when a wallet does not have enough tokens to
|
|
621
|
+
* complete a transaction. The error is FATAL as it requires user
|
|
622
|
+
* intervention to add funds.
|
|
623
|
+
*
|
|
624
|
+
* @param chain - The blockchain network where the balance check failed
|
|
625
|
+
* @param token - The token symbol (e.g., 'USDC', 'ETH')
|
|
626
|
+
* @param rawError - The original error from the underlying system (optional)
|
|
627
|
+
* @returns KitError with insufficient token balance details
|
|
628
|
+
*
|
|
629
|
+
* @example
|
|
630
|
+
* ```typescript
|
|
631
|
+
* import { createInsufficientTokenBalanceError } from '@core/errors'
|
|
632
|
+
*
|
|
633
|
+
* throw createInsufficientTokenBalanceError('Ethereum', 'USDC')
|
|
634
|
+
* // Message: "Insufficient USDC balance on Ethereum"
|
|
635
|
+
* ```
|
|
636
|
+
*
|
|
637
|
+
* @example
|
|
638
|
+
* ```typescript
|
|
639
|
+
* // With raw error for debugging
|
|
640
|
+
* try {
|
|
641
|
+
* await transfer(...)
|
|
642
|
+
* } catch (error) {
|
|
643
|
+
* throw createInsufficientTokenBalanceError('Base', 'USDC', error)
|
|
644
|
+
* }
|
|
645
|
+
* ```
|
|
646
|
+
*/
|
|
647
|
+
function createInsufficientTokenBalanceError(chain, token, rawError) {
|
|
648
|
+
return new KitError({
|
|
649
|
+
...BalanceError.INSUFFICIENT_TOKEN,
|
|
650
|
+
recoverability: 'FATAL',
|
|
651
|
+
message: `Insufficient ${token} balance on ${chain}`,
|
|
652
|
+
cause: {
|
|
653
|
+
trace: {
|
|
654
|
+
chain,
|
|
655
|
+
token,
|
|
656
|
+
rawError,
|
|
657
|
+
},
|
|
658
|
+
},
|
|
659
|
+
});
|
|
660
|
+
}
|
|
661
|
+
/**
|
|
662
|
+
* Creates error for insufficient gas funds.
|
|
663
|
+
*
|
|
664
|
+
* This error is thrown when a wallet does not have enough native tokens
|
|
665
|
+
* (ETH, SOL, etc.) to pay for transaction gas fees. The error is FATAL
|
|
666
|
+
* as it requires user intervention to add gas funds.
|
|
667
|
+
*
|
|
668
|
+
* @param chain - The blockchain network where the gas check failed
|
|
669
|
+
* @param rawError - The original error from the underlying system (optional)
|
|
670
|
+
* @returns KitError with insufficient gas details
|
|
671
|
+
*
|
|
672
|
+
* @example
|
|
673
|
+
* ```typescript
|
|
674
|
+
* import { createInsufficientGasError } from '@core/errors'
|
|
675
|
+
*
|
|
676
|
+
* throw createInsufficientGasError('Ethereum')
|
|
677
|
+
* // Message: "Insufficient gas funds on Ethereum"
|
|
678
|
+
* ```
|
|
679
|
+
*/
|
|
680
|
+
function createInsufficientGasError(chain, rawError) {
|
|
681
|
+
return new KitError({
|
|
682
|
+
...BalanceError.INSUFFICIENT_GAS,
|
|
683
|
+
recoverability: 'FATAL',
|
|
684
|
+
message: `Insufficient gas funds on ${chain}`,
|
|
685
|
+
cause: {
|
|
686
|
+
trace: {
|
|
687
|
+
chain,
|
|
688
|
+
rawError,
|
|
689
|
+
},
|
|
690
|
+
},
|
|
691
|
+
});
|
|
692
|
+
}
|
|
693
|
+
|
|
694
|
+
/**
|
|
695
|
+
* Creates error for transaction simulation failures.
|
|
696
|
+
*
|
|
697
|
+
* This error is thrown when a pre-flight transaction simulation fails,
|
|
698
|
+
* typically due to contract logic that would revert. The error is FATAL
|
|
699
|
+
* as it indicates the transaction would fail if submitted.
|
|
700
|
+
*
|
|
701
|
+
* @param chain - The blockchain network where the simulation failed
|
|
702
|
+
* @param reason - The reason for simulation failure (e.g., revert message)
|
|
703
|
+
* @param rawError - The original error from the underlying system (optional)
|
|
704
|
+
* @returns KitError with simulation failure details
|
|
705
|
+
*
|
|
706
|
+
* @example
|
|
707
|
+
* ```typescript
|
|
708
|
+
* import { createSimulationFailedError } from '@core/errors'
|
|
709
|
+
*
|
|
710
|
+
* throw createSimulationFailedError('Ethereum', 'ERC20: insufficient allowance')
|
|
711
|
+
* // Message: "Simulation failed on Ethereum: ERC20: insufficient allowance"
|
|
712
|
+
* ```
|
|
713
|
+
*/
|
|
714
|
+
function createSimulationFailedError(chain, reason, rawError) {
|
|
715
|
+
return new KitError({
|
|
716
|
+
...OnchainError.SIMULATION_FAILED,
|
|
717
|
+
recoverability: 'FATAL',
|
|
718
|
+
message: `Simulation failed on ${chain}: ${reason}`,
|
|
719
|
+
cause: {
|
|
720
|
+
trace: {
|
|
721
|
+
chain,
|
|
722
|
+
reason,
|
|
723
|
+
rawError,
|
|
724
|
+
},
|
|
725
|
+
},
|
|
726
|
+
});
|
|
727
|
+
}
|
|
728
|
+
/**
|
|
729
|
+
* Creates error for transaction reverts.
|
|
730
|
+
*
|
|
731
|
+
* This error is thrown when a transaction is submitted and confirmed
|
|
732
|
+
* but reverts on-chain. The error is FATAL as it indicates the
|
|
733
|
+
* transaction executed but failed.
|
|
734
|
+
*
|
|
735
|
+
* @param chain - The blockchain network where the transaction reverted
|
|
736
|
+
* @param reason - The reason for the revert (e.g., revert message)
|
|
737
|
+
* @param rawError - The original error from the underlying system (optional)
|
|
738
|
+
* @returns KitError with transaction revert details
|
|
739
|
+
*
|
|
740
|
+
* @example
|
|
741
|
+
* ```typescript
|
|
742
|
+
* import { createTransactionRevertedError } from '@core/errors'
|
|
743
|
+
*
|
|
744
|
+
* throw createTransactionRevertedError('Base', 'Slippage exceeded')
|
|
745
|
+
* // Message: "Transaction reverted on Base: Slippage exceeded"
|
|
746
|
+
* ```
|
|
747
|
+
*/
|
|
748
|
+
function createTransactionRevertedError(chain, reason, rawError) {
|
|
749
|
+
return new KitError({
|
|
750
|
+
...OnchainError.TRANSACTION_REVERTED,
|
|
751
|
+
recoverability: 'FATAL',
|
|
752
|
+
message: `Transaction reverted on ${chain}: ${reason}`,
|
|
753
|
+
cause: {
|
|
754
|
+
trace: {
|
|
755
|
+
chain,
|
|
756
|
+
reason,
|
|
757
|
+
rawError,
|
|
758
|
+
},
|
|
759
|
+
},
|
|
760
|
+
});
|
|
761
|
+
}
|
|
762
|
+
/**
|
|
763
|
+
* Creates error for out of gas failures.
|
|
764
|
+
*
|
|
765
|
+
* This error is thrown when a transaction runs out of gas during execution.
|
|
766
|
+
* The error is FATAL as it requires adjusting gas limits or transaction logic.
|
|
767
|
+
*
|
|
768
|
+
* @param chain - The blockchain network where the transaction ran out of gas
|
|
769
|
+
* @param rawError - The original error from the underlying system (optional)
|
|
770
|
+
* @returns KitError with out of gas details
|
|
771
|
+
*
|
|
772
|
+
* @example
|
|
773
|
+
* ```typescript
|
|
774
|
+
* import { createOutOfGasError } from '@core/errors'
|
|
775
|
+
*
|
|
776
|
+
* throw createOutOfGasError('Polygon')
|
|
777
|
+
* // Message: "Transaction ran out of gas on Polygon"
|
|
778
|
+
* ```
|
|
779
|
+
*/
|
|
780
|
+
function createOutOfGasError(chain, rawError) {
|
|
781
|
+
return new KitError({
|
|
782
|
+
...OnchainError.OUT_OF_GAS,
|
|
783
|
+
recoverability: 'FATAL',
|
|
784
|
+
message: `Transaction ran out of gas on ${chain}`,
|
|
785
|
+
cause: {
|
|
786
|
+
trace: {
|
|
787
|
+
chain,
|
|
788
|
+
rawError,
|
|
789
|
+
},
|
|
790
|
+
},
|
|
791
|
+
});
|
|
792
|
+
}
|
|
793
|
+
|
|
794
|
+
/**
|
|
795
|
+
* Creates error for RPC endpoint failures.
|
|
796
|
+
*
|
|
797
|
+
* This error is thrown when an RPC provider endpoint fails, returns an error,
|
|
798
|
+
* or is unavailable. The error is RETRYABLE as RPC issues are often temporary.
|
|
799
|
+
*
|
|
800
|
+
* @param chain - The blockchain network where the RPC error occurred
|
|
801
|
+
* @param rawError - The original error from the underlying system (optional)
|
|
802
|
+
* @returns KitError with RPC endpoint error details
|
|
803
|
+
*
|
|
804
|
+
* @example
|
|
805
|
+
* ```typescript
|
|
806
|
+
* import { createRpcEndpointError } from '@core/errors'
|
|
807
|
+
*
|
|
808
|
+
* throw createRpcEndpointError('Ethereum')
|
|
809
|
+
* // Message: "RPC endpoint error on Ethereum"
|
|
810
|
+
* ```
|
|
811
|
+
*/
|
|
812
|
+
function createRpcEndpointError(chain, rawError) {
|
|
813
|
+
return new KitError({
|
|
814
|
+
...RpcError.ENDPOINT_ERROR,
|
|
815
|
+
recoverability: 'RETRYABLE',
|
|
816
|
+
message: `RPC endpoint error on ${chain}`,
|
|
817
|
+
cause: {
|
|
818
|
+
trace: {
|
|
819
|
+
chain,
|
|
820
|
+
rawError,
|
|
821
|
+
},
|
|
822
|
+
},
|
|
823
|
+
});
|
|
824
|
+
}
|
|
825
|
+
|
|
826
|
+
/**
|
|
827
|
+
* Creates error for network connection failures.
|
|
828
|
+
*
|
|
829
|
+
* This error is thrown when network connectivity issues prevent reaching
|
|
830
|
+
* the blockchain network. The error is RETRYABLE as network issues are
|
|
831
|
+
* often temporary.
|
|
832
|
+
*
|
|
833
|
+
* @param chain - The blockchain network where the connection failed
|
|
834
|
+
* @param rawError - The original error from the underlying system (optional)
|
|
835
|
+
* @returns KitError with network connection error details
|
|
836
|
+
*
|
|
837
|
+
* @example
|
|
838
|
+
* ```typescript
|
|
839
|
+
* import { createNetworkConnectionError } from '@core/errors'
|
|
840
|
+
*
|
|
841
|
+
* throw createNetworkConnectionError('Ethereum')
|
|
842
|
+
* // Message: "Network connection failed for Ethereum"
|
|
843
|
+
* ```
|
|
844
|
+
*/
|
|
845
|
+
function createNetworkConnectionError(chain, rawError) {
|
|
846
|
+
return new KitError({
|
|
847
|
+
...NetworkError.CONNECTION_FAILED,
|
|
848
|
+
recoverability: 'RETRYABLE',
|
|
849
|
+
message: `Network connection failed for ${chain}`,
|
|
850
|
+
cause: {
|
|
851
|
+
trace: {
|
|
852
|
+
chain,
|
|
853
|
+
rawError,
|
|
854
|
+
},
|
|
855
|
+
},
|
|
856
|
+
});
|
|
857
|
+
}
|
|
858
|
+
|
|
859
|
+
/**
|
|
860
|
+
* Parses raw blockchain errors into structured KitError instances.
|
|
861
|
+
*
|
|
862
|
+
* This function uses pattern matching to identify common blockchain error
|
|
863
|
+
* types and converts them into standardized KitError format. It handles
|
|
864
|
+
* errors from viem, ethers, Solana web3.js, and other blockchain libraries.
|
|
865
|
+
*
|
|
866
|
+
* The parser recognizes 5 main error patterns:
|
|
867
|
+
* 1. Insufficient balance errors
|
|
868
|
+
* 2. Simulation/execution reverted errors
|
|
869
|
+
* 3. Gas-related errors
|
|
870
|
+
* 4. Network connectivity errors
|
|
871
|
+
* 5. RPC provider errors
|
|
872
|
+
*
|
|
873
|
+
* @param error - The raw error from the blockchain library
|
|
874
|
+
* @param context - Context information including chain and optional token
|
|
875
|
+
* @returns A structured KitError instance
|
|
876
|
+
*
|
|
877
|
+
* @example
|
|
878
|
+
* ```typescript
|
|
879
|
+
* import { parseBlockchainError } from '@core/errors'
|
|
880
|
+
*
|
|
881
|
+
* try {
|
|
882
|
+
* await walletClient.sendTransaction(...)
|
|
883
|
+
* } catch (error) {
|
|
884
|
+
* throw parseBlockchainError(error, {
|
|
885
|
+
* chain: 'Ethereum',
|
|
886
|
+
* token: 'USDC',
|
|
887
|
+
* operation: 'transfer'
|
|
888
|
+
* })
|
|
889
|
+
* }
|
|
890
|
+
* ```
|
|
891
|
+
*
|
|
892
|
+
* @example
|
|
893
|
+
* ```typescript
|
|
894
|
+
* // Minimal usage
|
|
895
|
+
* try {
|
|
896
|
+
* await connection.sendTransaction(...)
|
|
897
|
+
* } catch (error) {
|
|
898
|
+
* throw parseBlockchainError(error, { chain: 'Solana' })
|
|
899
|
+
* }
|
|
900
|
+
* ```
|
|
901
|
+
*/
|
|
902
|
+
function parseBlockchainError(error, context) {
|
|
903
|
+
const msg = extractMessage(error);
|
|
904
|
+
const token = context.token ?? 'token';
|
|
905
|
+
// Pattern 1: Insufficient balance errors
|
|
906
|
+
// Matches balance-related errors from ERC20 contracts, native transfers, and Solana programs
|
|
907
|
+
if (/transfer amount exceeds balance|insufficient (balance|funds)|burn amount exceeded/i.test(msg)) {
|
|
908
|
+
return createInsufficientTokenBalanceError(context.chain, token, error);
|
|
189
909
|
}
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
* @param action - The action key identifying which handler to execute.
|
|
200
|
-
* @param params - The parameters to pass to the action handler.
|
|
201
|
-
* @param context - The resolved operation context with concrete chain and address values.
|
|
202
|
-
* @returns A promise resolving to the prepared chain request.
|
|
203
|
-
*
|
|
204
|
-
* @throws {Error} When action parameter is not a valid string.
|
|
205
|
-
* @throws {Error} When no handler is registered for the specified action.
|
|
206
|
-
* @throws {Error} When the handler execution fails.
|
|
207
|
-
*
|
|
208
|
-
* @example
|
|
209
|
-
* ```typescript
|
|
210
|
-
* import { ActionRegistry } from '@core/adapter'
|
|
211
|
-
* import type { ChainEnum } from '@core/chains'
|
|
212
|
-
*
|
|
213
|
-
* const registry = new ActionRegistry()
|
|
214
|
-
*
|
|
215
|
-
* // First register a handler
|
|
216
|
-
* registry.registerHandler('token.approve', async (params, context) => ({
|
|
217
|
-
* chainId: context.chain, // Always defined
|
|
218
|
-
* data: '0x095ea7b3...',
|
|
219
|
-
* to: params.tokenAddress,
|
|
220
|
-
* value: '0'
|
|
221
|
-
* }))
|
|
222
|
-
*
|
|
223
|
-
* // Execute the action with resolved context (typically called from adapter.prepareAction)
|
|
224
|
-
* const resolvedContext = { chain: 'Base', address: '0x123...' }
|
|
225
|
-
* const result = await registry.executeAction('token.approve', {
|
|
226
|
-
* chainId: ChainEnum.Ethereum,
|
|
227
|
-
* tokenAddress: '0xA0b86a33E6441c8C1c7C16e4c5e3e5b5e4c5e3e5b5e4c5e',
|
|
228
|
-
* delegate: '0x1234567890123456789012345678901234567890',
|
|
229
|
-
* amount: '1000000'
|
|
230
|
-
* }, resolvedContext)
|
|
231
|
-
*
|
|
232
|
-
* console.log('Transaction prepared:', result.data)
|
|
233
|
-
* ```
|
|
234
|
-
*/
|
|
235
|
-
async executeAction(action, params, context) {
|
|
236
|
-
// Runtime validation for JavaScript consumers
|
|
237
|
-
if (typeof action !== 'string' || action.length === 0) {
|
|
238
|
-
throw new TypeError(`Action must be a non-empty string, received: ${typeof action}`);
|
|
910
|
+
// Pattern 2: Simulation and execution reverts
|
|
911
|
+
// Matches contract revert errors and simulation failures
|
|
912
|
+
if (/execution reverted|simulation failed|transaction reverted|transaction failed/i.test(msg)) {
|
|
913
|
+
const reason = extractRevertReason(msg) ?? 'Transaction reverted';
|
|
914
|
+
// Distinguish between simulation failures and transaction reverts
|
|
915
|
+
// "simulation failed" or "eth_call" indicates pre-flight simulation
|
|
916
|
+
// "transaction failed" or context.operation === 'transaction' indicates post-execution
|
|
917
|
+
if (/simulation failed/i.test(msg) || context.operation === 'simulation') {
|
|
918
|
+
return createSimulationFailedError(context.chain, reason, error);
|
|
239
919
|
}
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
920
|
+
// Transaction execution failures or reverts
|
|
921
|
+
return createTransactionRevertedError(context.chain, reason, error);
|
|
922
|
+
}
|
|
923
|
+
// Pattern 3: Gas-related errors
|
|
924
|
+
// Matches gas estimation failures and gas exhaustion
|
|
925
|
+
// Check specific patterns first, then generic "gas" patterns
|
|
926
|
+
// Gas estimation failures are RPC issues
|
|
927
|
+
if (/gas estimation failed|cannot estimate gas/i.test(msg)) {
|
|
928
|
+
return createRpcEndpointError(context.chain, error);
|
|
929
|
+
}
|
|
930
|
+
// Gas exhaustion errors
|
|
931
|
+
// Use specific patterns without wildcards to avoid ReDoS
|
|
932
|
+
if (/out of gas|gas limit exceeded|exceeds block gas limit/i.test(msg)) {
|
|
933
|
+
return createOutOfGasError(context.chain, error);
|
|
934
|
+
}
|
|
935
|
+
// Insufficient funds for gas
|
|
936
|
+
if (/insufficient funds for gas/i.test(msg)) {
|
|
937
|
+
return createInsufficientGasError(context.chain, error);
|
|
938
|
+
}
|
|
939
|
+
// Pattern 4: Network connectivity errors
|
|
940
|
+
// Matches connection failures, DNS errors, and timeouts
|
|
941
|
+
if (/connection (refused|failed)|network|timeout|ENOTFOUND|ECONNREFUSED/i.test(msg)) {
|
|
942
|
+
return createNetworkConnectionError(context.chain, error);
|
|
943
|
+
}
|
|
944
|
+
// Pattern 5: RPC provider errors
|
|
945
|
+
// Matches RPC endpoint errors, invalid responses, and rate limits
|
|
946
|
+
if (/rpc|invalid response|rate limit|too many requests/i.test(msg)) {
|
|
947
|
+
return createRpcEndpointError(context.chain, error);
|
|
948
|
+
}
|
|
949
|
+
// Fallback based on operation context
|
|
950
|
+
// Gas-related operations are RPC calls
|
|
951
|
+
if (context.operation === 'estimateGas' ||
|
|
952
|
+
context.operation === 'getGasPrice') {
|
|
953
|
+
return createRpcEndpointError(context.chain, error);
|
|
954
|
+
}
|
|
955
|
+
// Fallback for unrecognized errors
|
|
956
|
+
// Defaults to simulation failed as transaction execution is the most common failure point
|
|
957
|
+
return createSimulationFailedError(context.chain, msg.length > 0 ? msg : 'Unknown error', error);
|
|
958
|
+
}
|
|
959
|
+
/**
|
|
960
|
+
* Type guard to check if error has Solana-Kit structure with logs.
|
|
961
|
+
*
|
|
962
|
+
* Checks if the error object contains a context with logs array,
|
|
963
|
+
* which is the structure used by Solana Kit errors.
|
|
964
|
+
*
|
|
965
|
+
* @param error - Unknown error to check
|
|
966
|
+
* @returns True if error has Solana-Kit logs structure
|
|
967
|
+
*/
|
|
968
|
+
function hasSolanaLogs(error) {
|
|
969
|
+
return (error !== null &&
|
|
970
|
+
typeof error === 'object' &&
|
|
971
|
+
'context' in error &&
|
|
972
|
+
error.context !== null &&
|
|
973
|
+
typeof error.context === 'object' &&
|
|
974
|
+
'logs' in error.context &&
|
|
975
|
+
Array.isArray(error.context.logs));
|
|
976
|
+
}
|
|
977
|
+
/**
|
|
978
|
+
* Extracts a human-readable error message from various error types.
|
|
979
|
+
*
|
|
980
|
+
* Handles Error objects, string errors, objects with message properties,
|
|
981
|
+
* Solana-Kit errors with context logs, and falls back to string representation.
|
|
982
|
+
* For Solana-Kit errors, extracts Anchor error messages from transaction logs.
|
|
983
|
+
*
|
|
984
|
+
* @param error - Unknown error to extract message from
|
|
985
|
+
* @returns Extracted error message string
|
|
986
|
+
*
|
|
987
|
+
* @example
|
|
988
|
+
* ```typescript
|
|
989
|
+
* const msg1 = extractMessage(new Error('test')) // 'test'
|
|
990
|
+
* const msg2 = extractMessage('string error') // 'string error'
|
|
991
|
+
* const msg3 = extractMessage({ message: 'obj' }) // 'obj'
|
|
992
|
+
* ```
|
|
993
|
+
*/
|
|
994
|
+
function extractMessage(error) {
|
|
995
|
+
// Check for Solana-Kit errors with context.logs
|
|
996
|
+
if (hasSolanaLogs(error)) {
|
|
997
|
+
// Extract Anchor error message from logs
|
|
998
|
+
const anchorLog = error.context.logs.find((log) => log.includes('AnchorError') || log.includes('Error Message'));
|
|
999
|
+
if (anchorLog !== undefined) {
|
|
1000
|
+
// Return the anchor error log which contains the detailed message
|
|
1001
|
+
return anchorLog;
|
|
247
1002
|
}
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
1003
|
+
}
|
|
1004
|
+
if (error instanceof Error) {
|
|
1005
|
+
return error.message;
|
|
1006
|
+
}
|
|
1007
|
+
if (typeof error === 'string') {
|
|
1008
|
+
return error;
|
|
1009
|
+
}
|
|
1010
|
+
if (typeof error === 'object' && error !== null && 'message' in error) {
|
|
1011
|
+
return String(error.message);
|
|
1012
|
+
}
|
|
1013
|
+
return String(error);
|
|
1014
|
+
}
|
|
1015
|
+
/**
|
|
1016
|
+
* Extracts the revert reason from an error message.
|
|
1017
|
+
*
|
|
1018
|
+
* Attempts to parse out the meaningful reason from execution revert errors,
|
|
1019
|
+
* removing common prefixes like "execution reverted:" or "reverted:".
|
|
1020
|
+
*
|
|
1021
|
+
* @param msg - The error message to extract from
|
|
1022
|
+
* @returns The extracted revert reason, or null if not found
|
|
1023
|
+
*
|
|
1024
|
+
* @example
|
|
1025
|
+
* ```typescript
|
|
1026
|
+
* const reason = extractRevertReason(
|
|
1027
|
+
* 'execution reverted: ERC20: transfer amount exceeds balance'
|
|
1028
|
+
* )
|
|
1029
|
+
* // Returns: 'ERC20: transfer amount exceeds balance'
|
|
1030
|
+
* ```
|
|
1031
|
+
*
|
|
1032
|
+
* @example
|
|
1033
|
+
* ```typescript
|
|
1034
|
+
* const reason = extractRevertReason(
|
|
1035
|
+
* 'Simulation failed: Execution reverted with reason: Insufficient allowance'
|
|
1036
|
+
* )
|
|
1037
|
+
* // Returns: 'Insufficient allowance'
|
|
1038
|
+
* ```
|
|
1039
|
+
*/
|
|
1040
|
+
function extractRevertReason(msg) {
|
|
1041
|
+
// Try to extract reason after "execution reverted:" or "reason:"
|
|
1042
|
+
// Use [^\n.]+ instead of .+? to avoid ReDoS vulnerability
|
|
1043
|
+
const patterns = [
|
|
1044
|
+
/(?:execution reverted|reverted):\s*([^\n.]+)/i,
|
|
1045
|
+
/reason:\s*([^\n.]+)/i,
|
|
1046
|
+
/with reason:\s*([^\n.]+)/i,
|
|
1047
|
+
];
|
|
1048
|
+
for (const pattern of patterns) {
|
|
1049
|
+
const match = pattern.exec(msg);
|
|
1050
|
+
const extractedReason = match?.at(1);
|
|
1051
|
+
if (extractedReason !== undefined && extractedReason.length > 0) {
|
|
1052
|
+
return extractedReason.trim();
|
|
252
1053
|
}
|
|
253
1054
|
}
|
|
1055
|
+
return null;
|
|
254
1056
|
}
|
|
255
1057
|
|
|
256
1058
|
// -----------------------------------------------------------------------------
|
|
257
1059
|
// Blockchain Enum
|
|
258
1060
|
// -----------------------------------------------------------------------------
|
|
259
1061
|
/**
|
|
260
|
-
* Enumeration of all blockchains
|
|
1062
|
+
* Enumeration of all blockchains known to this library.
|
|
1063
|
+
*
|
|
1064
|
+
* This enum contains every blockchain that has a chain definition, regardless
|
|
1065
|
+
* of whether bridging is currently supported. For chains that support bridging
|
|
1066
|
+
* via CCTPv2, see {@link BridgeChain}.
|
|
1067
|
+
*
|
|
261
1068
|
* @enum
|
|
262
1069
|
* @category Enums
|
|
263
|
-
* @description Provides string identifiers for each
|
|
1070
|
+
* @description Provides string identifiers for each blockchain with a definition.
|
|
1071
|
+
* @see {@link BridgeChain} for the subset of chains that support CCTPv2 bridging.
|
|
264
1072
|
*/
|
|
265
1073
|
exports.Blockchain = void 0;
|
|
266
1074
|
(function (Blockchain) {
|
|
@@ -320,6 +1128,95 @@ exports.Blockchain = void 0;
|
|
|
320
1128
|
Blockchain["ZKSync_Era"] = "ZKSync_Era";
|
|
321
1129
|
Blockchain["ZKSync_Sepolia"] = "ZKSync_Sepolia";
|
|
322
1130
|
})(exports.Blockchain || (exports.Blockchain = {}));
|
|
1131
|
+
// -----------------------------------------------------------------------------
|
|
1132
|
+
// Bridge Chain Enum (CCTPv2 Supported Chains)
|
|
1133
|
+
// -----------------------------------------------------------------------------
|
|
1134
|
+
/**
|
|
1135
|
+
* Enumeration of blockchains that support cross-chain bridging via CCTPv2.
|
|
1136
|
+
*
|
|
1137
|
+
* The enum is derived from the full {@link Blockchain} enum but filtered to only
|
|
1138
|
+
* include chains with active CCTPv2 support. When new chains gain CCTPv2 support,
|
|
1139
|
+
* they are added to this enum.
|
|
1140
|
+
*
|
|
1141
|
+
* @enum
|
|
1142
|
+
* @category Enums
|
|
1143
|
+
*
|
|
1144
|
+
* @remarks
|
|
1145
|
+
* - This enum is the **canonical source** of bridging-supported chains.
|
|
1146
|
+
* - Use this enum (or its string literals) in `kit.bridge()` calls for type safety.
|
|
1147
|
+
* - Attempting to use a chain not in this enum will produce a TypeScript compile error.
|
|
1148
|
+
*
|
|
1149
|
+
* @example
|
|
1150
|
+
* ```typescript
|
|
1151
|
+
* import { BridgeKit, BridgeChain } from '@circle-fin/bridge-kit'
|
|
1152
|
+
*
|
|
1153
|
+
* const kit = new BridgeKit()
|
|
1154
|
+
*
|
|
1155
|
+
* // ✅ Valid - autocomplete suggests only supported chains
|
|
1156
|
+
* await kit.bridge({
|
|
1157
|
+
* from: { adapter, chain: BridgeChain.Ethereum },
|
|
1158
|
+
* to: { adapter, chain: BridgeChain.Base },
|
|
1159
|
+
* amount: '100'
|
|
1160
|
+
* })
|
|
1161
|
+
*
|
|
1162
|
+
* // ✅ Also valid - string literals work with autocomplete
|
|
1163
|
+
* await kit.bridge({
|
|
1164
|
+
* from: { adapter, chain: 'Ethereum_Sepolia' },
|
|
1165
|
+
* to: { adapter, chain: 'Base_Sepolia' },
|
|
1166
|
+
* amount: '100'
|
|
1167
|
+
* })
|
|
1168
|
+
*
|
|
1169
|
+
* // ❌ Compile error - Algorand is not in BridgeChain
|
|
1170
|
+
* await kit.bridge({
|
|
1171
|
+
* from: { adapter, chain: 'Algorand' }, // TypeScript error!
|
|
1172
|
+
* to: { adapter, chain: 'Base' },
|
|
1173
|
+
* amount: '100'
|
|
1174
|
+
* })
|
|
1175
|
+
* ```
|
|
1176
|
+
*
|
|
1177
|
+
* @see {@link Blockchain} for the complete list of all known blockchains.
|
|
1178
|
+
* @see {@link BridgeChainIdentifier} for the type that accepts these values.
|
|
1179
|
+
*/
|
|
1180
|
+
var BridgeChain;
|
|
1181
|
+
(function (BridgeChain) {
|
|
1182
|
+
// Mainnet chains with CCTPv2 support
|
|
1183
|
+
BridgeChain["Arbitrum"] = "Arbitrum";
|
|
1184
|
+
BridgeChain["Avalanche"] = "Avalanche";
|
|
1185
|
+
BridgeChain["Base"] = "Base";
|
|
1186
|
+
BridgeChain["Codex"] = "Codex";
|
|
1187
|
+
BridgeChain["Ethereum"] = "Ethereum";
|
|
1188
|
+
BridgeChain["HyperEVM"] = "HyperEVM";
|
|
1189
|
+
BridgeChain["Ink"] = "Ink";
|
|
1190
|
+
BridgeChain["Linea"] = "Linea";
|
|
1191
|
+
BridgeChain["Optimism"] = "Optimism";
|
|
1192
|
+
BridgeChain["Plume"] = "Plume";
|
|
1193
|
+
BridgeChain["Polygon"] = "Polygon";
|
|
1194
|
+
BridgeChain["Sei"] = "Sei";
|
|
1195
|
+
BridgeChain["Solana"] = "Solana";
|
|
1196
|
+
BridgeChain["Sonic"] = "Sonic";
|
|
1197
|
+
BridgeChain["Unichain"] = "Unichain";
|
|
1198
|
+
BridgeChain["World_Chain"] = "World_Chain";
|
|
1199
|
+
BridgeChain["XDC"] = "XDC";
|
|
1200
|
+
// Testnet chains with CCTPv2 support
|
|
1201
|
+
BridgeChain["Arc_Testnet"] = "Arc_Testnet";
|
|
1202
|
+
BridgeChain["Arbitrum_Sepolia"] = "Arbitrum_Sepolia";
|
|
1203
|
+
BridgeChain["Avalanche_Fuji"] = "Avalanche_Fuji";
|
|
1204
|
+
BridgeChain["Base_Sepolia"] = "Base_Sepolia";
|
|
1205
|
+
BridgeChain["Codex_Testnet"] = "Codex_Testnet";
|
|
1206
|
+
BridgeChain["Ethereum_Sepolia"] = "Ethereum_Sepolia";
|
|
1207
|
+
BridgeChain["HyperEVM_Testnet"] = "HyperEVM_Testnet";
|
|
1208
|
+
BridgeChain["Ink_Testnet"] = "Ink_Testnet";
|
|
1209
|
+
BridgeChain["Linea_Sepolia"] = "Linea_Sepolia";
|
|
1210
|
+
BridgeChain["Optimism_Sepolia"] = "Optimism_Sepolia";
|
|
1211
|
+
BridgeChain["Plume_Testnet"] = "Plume_Testnet";
|
|
1212
|
+
BridgeChain["Polygon_Amoy_Testnet"] = "Polygon_Amoy_Testnet";
|
|
1213
|
+
BridgeChain["Sei_Testnet"] = "Sei_Testnet";
|
|
1214
|
+
BridgeChain["Solana_Devnet"] = "Solana_Devnet";
|
|
1215
|
+
BridgeChain["Sonic_Testnet"] = "Sonic_Testnet";
|
|
1216
|
+
BridgeChain["Unichain_Sepolia"] = "Unichain_Sepolia";
|
|
1217
|
+
BridgeChain["World_Chain_Sepolia"] = "World_Chain_Sepolia";
|
|
1218
|
+
BridgeChain["XDC_Apothem"] = "XDC_Apothem";
|
|
1219
|
+
})(BridgeChain || (BridgeChain = {}));
|
|
323
1220
|
|
|
324
1221
|
/**
|
|
325
1222
|
* Helper function to define a chain with proper TypeScript typing.
|
|
@@ -2608,6 +3505,44 @@ zod.z.union([
|
|
|
2608
3505
|
zod.z.nativeEnum(exports.Blockchain),
|
|
2609
3506
|
chainDefinitionSchema$1,
|
|
2610
3507
|
]);
|
|
3508
|
+
/**
|
|
3509
|
+
* Zod schema for validating bridge chain identifiers.
|
|
3510
|
+
*
|
|
3511
|
+
* This schema validates that the provided chain is supported for CCTPv2 bridging.
|
|
3512
|
+
* It accepts either a BridgeChain enum value, a string matching a BridgeChain value,
|
|
3513
|
+
* or a ChainDefinition for a supported chain.
|
|
3514
|
+
*
|
|
3515
|
+
* Use this schema when validating chain parameters for bridge operations to ensure
|
|
3516
|
+
* only CCTPv2-supported chains are accepted at runtime.
|
|
3517
|
+
*
|
|
3518
|
+
* @example
|
|
3519
|
+
* ```typescript
|
|
3520
|
+
* import { bridgeChainIdentifierSchema } from '@core/chains/validation'
|
|
3521
|
+
* import { BridgeChain, Chains } from '@core/chains'
|
|
3522
|
+
*
|
|
3523
|
+
* // Valid - BridgeChain enum value
|
|
3524
|
+
* bridgeChainIdentifierSchema.parse(BridgeChain.Ethereum)
|
|
3525
|
+
*
|
|
3526
|
+
* // Valid - string literal
|
|
3527
|
+
* bridgeChainIdentifierSchema.parse('Base_Sepolia')
|
|
3528
|
+
*
|
|
3529
|
+
* // Valid - ChainDefinition (validated by CCTP support)
|
|
3530
|
+
* bridgeChainIdentifierSchema.parse(Chains.Solana)
|
|
3531
|
+
*
|
|
3532
|
+
* // Invalid - Algorand is not in BridgeChain (throws ZodError)
|
|
3533
|
+
* bridgeChainIdentifierSchema.parse('Algorand')
|
|
3534
|
+
* ```
|
|
3535
|
+
*
|
|
3536
|
+
* @see {@link BridgeChain} for the enum of supported chains.
|
|
3537
|
+
*/
|
|
3538
|
+
zod.z.union([
|
|
3539
|
+
zod.z.string().refine((val) => val in BridgeChain, (val) => ({
|
|
3540
|
+
message: `Chain "${val}" is not supported for bridging. Only chains in the BridgeChain enum support CCTPv2 bridging.`,
|
|
3541
|
+
})),
|
|
3542
|
+
chainDefinitionSchema$1.refine((chainDef) => chainDef.chain in BridgeChain, (chainDef) => ({
|
|
3543
|
+
message: `Chain "${chainDef.name}" (${chainDef.chain}) is not supported for bridging. Only chains in the BridgeChain enum support CCTPv2 bridging.`,
|
|
3544
|
+
})),
|
|
3545
|
+
]);
|
|
2611
3546
|
|
|
2612
3547
|
/**
|
|
2613
3548
|
* Get all supported EVM chain definitions.
|
|
@@ -2755,7 +3690,235 @@ const resolveCCTPV2ContractAddress = (chain, contractType) => {
|
|
|
2755
3690
|
throw new Error(`Unsupported CCTP v2 contract type on chain ${chain.name}. Expected "split" or "merged", but received '${unknownContract.type ?? 'unknown'}'.`);
|
|
2756
3691
|
}
|
|
2757
3692
|
}
|
|
2758
|
-
};
|
|
3693
|
+
};
|
|
3694
|
+
|
|
3695
|
+
/**
|
|
3696
|
+
* Type-safe registry for managing and executing blockchain action handlers.
|
|
3697
|
+
*
|
|
3698
|
+
* Provides a centralized system for registering action handlers with full
|
|
3699
|
+
* TypeScript type safety, ensuring that handlers can only be registered
|
|
3700
|
+
* with compatible action keys and payload types. Supports both individual
|
|
3701
|
+
* handler registration and batch registration operations.
|
|
3702
|
+
*
|
|
3703
|
+
* @remarks
|
|
3704
|
+
* The registry uses a Map internally for O(1) lookups and maintains type
|
|
3705
|
+
* safety through generic constraints and careful type assertions. All
|
|
3706
|
+
* type assertions are validated at registration time to ensure runtime
|
|
3707
|
+
* type safety matches compile-time guarantees.
|
|
3708
|
+
*/
|
|
3709
|
+
class ActionRegistry {
|
|
3710
|
+
actionHandlers = new Map();
|
|
3711
|
+
/**
|
|
3712
|
+
* Register a type-safe action handler for a specific action key.
|
|
3713
|
+
*
|
|
3714
|
+
* Associates an action handler function with its corresponding action key,
|
|
3715
|
+
* ensuring compile-time type safety between the action and its expected
|
|
3716
|
+
* payload structure. The handler will be available for execution via
|
|
3717
|
+
* {@link executeAction}.
|
|
3718
|
+
*
|
|
3719
|
+
* @typeParam TActionKey - The specific action key being registered.
|
|
3720
|
+
* @param action - The action key to register the handler for.
|
|
3721
|
+
* @param handler - The handler function for processing this action type.
|
|
3722
|
+
* @returns Void.
|
|
3723
|
+
*
|
|
3724
|
+
* @throws Error When action parameter is not a valid string.
|
|
3725
|
+
* @throws TypeError When handler parameter is not a function.
|
|
3726
|
+
*
|
|
3727
|
+
* @example
|
|
3728
|
+
* ```typescript
|
|
3729
|
+
* import { ActionRegistry } from '@core/adapter'
|
|
3730
|
+
* import type { ActionHandler } from '@core/adapter'
|
|
3731
|
+
*
|
|
3732
|
+
* const registry = new ActionRegistry()
|
|
3733
|
+
*
|
|
3734
|
+
* // Register a CCTP deposit handler
|
|
3735
|
+
* const depositHandler: ActionHandler<'cctp.v2.depositForBurn'> = async (params, resolved) => {
|
|
3736
|
+
* console.log('Processing deposit:', params.amount)
|
|
3737
|
+
* return {
|
|
3738
|
+
* chainId: params.chainId,
|
|
3739
|
+
* data: '0x...',
|
|
3740
|
+
* to: '0x...',
|
|
3741
|
+
* value: '0'
|
|
3742
|
+
* }
|
|
3743
|
+
* }
|
|
3744
|
+
*
|
|
3745
|
+
* registry.registerHandler('cctp.v2.depositForBurn', depositHandler)
|
|
3746
|
+
* ```
|
|
3747
|
+
*/
|
|
3748
|
+
registerHandler(action, handler) {
|
|
3749
|
+
// Runtime validation for JavaScript consumers
|
|
3750
|
+
if (typeof action !== 'string' || action.length === 0) {
|
|
3751
|
+
throw new TypeError(`Action must be a non-empty string, received: ${typeof action}`);
|
|
3752
|
+
}
|
|
3753
|
+
if (typeof handler !== 'function') {
|
|
3754
|
+
throw new TypeError(`Handler must be a function, received: ${typeof handler}`);
|
|
3755
|
+
}
|
|
3756
|
+
// The handler is upcast to ActionHandler<ActionKeys> for storage,
|
|
3757
|
+
// but type safety is maintained at the call site through the generic constraint
|
|
3758
|
+
this.actionHandlers.set(action, handler);
|
|
3759
|
+
}
|
|
3760
|
+
/**
|
|
3761
|
+
* Register multiple action handlers in a single operation.
|
|
3762
|
+
*
|
|
3763
|
+
* Efficiently register multiple handlers from a record object, where keys
|
|
3764
|
+
* are action identifiers and values are their corresponding handler
|
|
3765
|
+
* functions. Provides a convenient way to bulk-register handlers while
|
|
3766
|
+
* maintaining type safety.
|
|
3767
|
+
*
|
|
3768
|
+
* @param handlers - A record mapping action keys to their handler functions.
|
|
3769
|
+
* @returns Void.
|
|
3770
|
+
*
|
|
3771
|
+
* @throws {Error} When handlers parameter is not a valid object.
|
|
3772
|
+
* @throws {Error} When any individual handler registration fails.
|
|
3773
|
+
*
|
|
3774
|
+
* @example
|
|
3775
|
+
* ```typescript
|
|
3776
|
+
* import { ActionRegistry } from '@core/adapter'
|
|
3777
|
+
* import type { ActionHandler, ActionHandlers } from '@core/adapter'
|
|
3778
|
+
*
|
|
3779
|
+
* const registry = new ActionRegistry()
|
|
3780
|
+
*
|
|
3781
|
+
* // Register multiple handlers at once
|
|
3782
|
+
* const tokenHandlers: ActionHandlers = {
|
|
3783
|
+
* 'token.approve': async (params, resolved) => ({
|
|
3784
|
+
* chainId: resolved.chain,
|
|
3785
|
+
* data: '0x095ea7b3...',
|
|
3786
|
+
* to: params.tokenAddress,
|
|
3787
|
+
* value: '0'
|
|
3788
|
+
* }),
|
|
3789
|
+
* 'token.transfer': async (params, resolved) => ({
|
|
3790
|
+
* chainId: resolved.chain,
|
|
3791
|
+
* data: '0xa9059cbb...',
|
|
3792
|
+
* to: params.tokenAddress,
|
|
3793
|
+
* value: '0'
|
|
3794
|
+
* })
|
|
3795
|
+
* }
|
|
3796
|
+
*
|
|
3797
|
+
* registry.registerHandlers(tokenHandlers)
|
|
3798
|
+
* console.log('Registered multiple token handlers')
|
|
3799
|
+
* ```
|
|
3800
|
+
*/
|
|
3801
|
+
registerHandlers(handlers) {
|
|
3802
|
+
// Runtime validation for JavaScript consumers
|
|
3803
|
+
if (typeof handlers !== 'object' || handlers === null) {
|
|
3804
|
+
throw new Error(`Handlers must be a non-null object, received: ${typeof handlers}`);
|
|
3805
|
+
}
|
|
3806
|
+
// Register each handler individually to benefit from per-handler validation
|
|
3807
|
+
for (const [action, handler] of Object.entries(handlers)) {
|
|
3808
|
+
this.registerHandler(action, handler);
|
|
3809
|
+
}
|
|
3810
|
+
}
|
|
3811
|
+
/**
|
|
3812
|
+
* Check whether a specific action is supported by this registry.
|
|
3813
|
+
*
|
|
3814
|
+
* Determine if a handler has been registered for the given action key.
|
|
3815
|
+
* Use this method to conditionally execute actions or provide appropriate
|
|
3816
|
+
* error messages when actions are not available.
|
|
3817
|
+
*
|
|
3818
|
+
* @param action - The action key to check for support.
|
|
3819
|
+
* @returns True if the action is supported, false otherwise.
|
|
3820
|
+
*
|
|
3821
|
+
* @throws {Error} When action parameter is not a valid string.
|
|
3822
|
+
*
|
|
3823
|
+
* @example
|
|
3824
|
+
* ```typescript
|
|
3825
|
+
* import { ActionRegistry } from '@core/adapter'
|
|
3826
|
+
*
|
|
3827
|
+
* const registry = new ActionRegistry()
|
|
3828
|
+
*
|
|
3829
|
+
* // Check if actions are supported before attempting to use them
|
|
3830
|
+
* if (registry.supportsAction('token.approve')) {
|
|
3831
|
+
* console.log('Token approval is supported')
|
|
3832
|
+
* } else {
|
|
3833
|
+
* console.log('Token approval not available')
|
|
3834
|
+
* }
|
|
3835
|
+
*
|
|
3836
|
+
* // Conditional logic based on support
|
|
3837
|
+
* const action = 'cctp.v2.depositForBurn'
|
|
3838
|
+
* if (registry.supportsAction(action)) {
|
|
3839
|
+
* // Safe to execute
|
|
3840
|
+
* console.log(`${action} is available`)
|
|
3841
|
+
* } else {
|
|
3842
|
+
* console.warn(`${action} is not registered`)
|
|
3843
|
+
* }
|
|
3844
|
+
* ```
|
|
3845
|
+
*/
|
|
3846
|
+
supportsAction(action) {
|
|
3847
|
+
// Runtime validation for JavaScript consumers
|
|
3848
|
+
if (typeof action !== 'string' || action.length === 0) {
|
|
3849
|
+
throw new TypeError(`Action must be a non-empty string, received: ${typeof action}`);
|
|
3850
|
+
}
|
|
3851
|
+
return this.actionHandlers.has(action);
|
|
3852
|
+
}
|
|
3853
|
+
/**
|
|
3854
|
+
* Execute a registered action handler with type-safe parameters.
|
|
3855
|
+
*
|
|
3856
|
+
* Look up and execute the handler associated with the given action key,
|
|
3857
|
+
* passing the provided parameters and context, returning the resulting prepared
|
|
3858
|
+
* chain request. TypeScript ensures the parameters match the expected
|
|
3859
|
+
* structure for the specified action.
|
|
3860
|
+
*
|
|
3861
|
+
* @typeParam TActionKey - The specific action key being executed.
|
|
3862
|
+
* @param action - The action key identifying which handler to execute.
|
|
3863
|
+
* @param params - The parameters to pass to the action handler.
|
|
3864
|
+
* @param context - The resolved operation context with concrete chain and address values.
|
|
3865
|
+
* @returns A promise resolving to the prepared chain request.
|
|
3866
|
+
* @throws {KitError} When the handler execution fails with a structured error.
|
|
3867
|
+
* @throws {Error} When no handler is registered for the specified action.
|
|
3868
|
+
* @throws {Error} When the handler execution fails with an unstructured error.
|
|
3869
|
+
*
|
|
3870
|
+
* @example
|
|
3871
|
+
* ```typescript
|
|
3872
|
+
* import { ActionRegistry } from '@core/adapter'
|
|
3873
|
+
* import type { ChainEnum } from '@core/chains'
|
|
3874
|
+
*
|
|
3875
|
+
* const registry = new ActionRegistry()
|
|
3876
|
+
*
|
|
3877
|
+
* // First register a handler
|
|
3878
|
+
* registry.registerHandler('token.approve', async (params, context) => ({
|
|
3879
|
+
* chainId: context.chain, // Always defined
|
|
3880
|
+
* data: '0x095ea7b3...',
|
|
3881
|
+
* to: params.tokenAddress,
|
|
3882
|
+
* value: '0'
|
|
3883
|
+
* }))
|
|
3884
|
+
*
|
|
3885
|
+
* // Execute the action with resolved context (typically called from adapter.prepareAction)
|
|
3886
|
+
* const resolvedContext = { chain: 'Base', address: '0x123...' }
|
|
3887
|
+
* const result = await registry.executeAction('token.approve', {
|
|
3888
|
+
* chainId: ChainEnum.Ethereum,
|
|
3889
|
+
* tokenAddress: '0xA0b86a33E6441c8C1c7C16e4c5e3e5b5e4c5e3e5b5e4c5e',
|
|
3890
|
+
* delegate: '0x1234567890123456789012345678901234567890',
|
|
3891
|
+
* amount: '1000000'
|
|
3892
|
+
* }, resolvedContext)
|
|
3893
|
+
*
|
|
3894
|
+
* console.log('Transaction prepared:', result.data)
|
|
3895
|
+
* ```
|
|
3896
|
+
*/
|
|
3897
|
+
async executeAction(action, params, context) {
|
|
3898
|
+
// Runtime validation for JavaScript consumers
|
|
3899
|
+
if (typeof action !== 'string' || action.length === 0) {
|
|
3900
|
+
throw new TypeError(`Action must be a non-empty string, received: ${typeof action}`);
|
|
3901
|
+
}
|
|
3902
|
+
const handler = this.actionHandlers.get(action);
|
|
3903
|
+
if (!handler) {
|
|
3904
|
+
throw new Error(`Action ${action} is not supported`);
|
|
3905
|
+
}
|
|
3906
|
+
try {
|
|
3907
|
+
// Type safety is guaranteed by the registration process
|
|
3908
|
+
return await handler(params, context);
|
|
3909
|
+
}
|
|
3910
|
+
catch (error) {
|
|
3911
|
+
// If it's already a KitError with structured information, re-throw as-is
|
|
3912
|
+
// to preserve error code, name, type, recoverability, and cause
|
|
3913
|
+
if (error instanceof KitError) {
|
|
3914
|
+
throw error;
|
|
3915
|
+
}
|
|
3916
|
+
// For other errors, re-throw with context for better debugging
|
|
3917
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
3918
|
+
throw new Error(`Failed to execute action ${action}: ${message}`);
|
|
3919
|
+
}
|
|
3920
|
+
}
|
|
3921
|
+
}
|
|
2759
3922
|
|
|
2760
3923
|
/**
|
|
2761
3924
|
* Resolves an operation context into concrete chain and address values.
|
|
@@ -2826,281 +3989,17 @@ async function resolveOperationContext(adapter, ctx) {
|
|
|
2826
3989
|
try {
|
|
2827
3990
|
// Pass resolved chain to getAddress for adapters that support it (like ViemAdapter)
|
|
2828
3991
|
// The chain parameter is optional in implementations, so this is safe
|
|
2829
|
-
resolvedAddress = await adapter.getAddress(resolvedChain);
|
|
2830
|
-
}
|
|
2831
|
-
catch (error) {
|
|
2832
|
-
const message = error instanceof Error ? error.message : String(error);
|
|
2833
|
-
throw new Error(`Failed to resolve address from user-controlled adapter: ${message}`);
|
|
2834
|
-
}
|
|
2835
|
-
}
|
|
2836
|
-
return {
|
|
2837
|
-
chain: resolvedChain,
|
|
2838
|
-
address: resolvedAddress,
|
|
2839
|
-
};
|
|
2840
|
-
}
|
|
2841
|
-
|
|
2842
|
-
/**
|
|
2843
|
-
* Valid recoverability values for error handling strategies.
|
|
2844
|
-
*
|
|
2845
|
-
* - FATAL errors are thrown immediately (invalid inputs, insufficient funds)
|
|
2846
|
-
* - RETRYABLE errors are returned when a flow fails to start but could work later
|
|
2847
|
-
* - RESUMABLE errors are returned when a flow fails mid-execution but can be continued
|
|
2848
|
-
*/
|
|
2849
|
-
const RECOVERABILITY_VALUES = [
|
|
2850
|
-
'RETRYABLE',
|
|
2851
|
-
'RESUMABLE',
|
|
2852
|
-
'FATAL',
|
|
2853
|
-
];
|
|
2854
|
-
|
|
2855
|
-
// Create a mutable array for Zod enum validation
|
|
2856
|
-
const RECOVERABILITY_ARRAY = [...RECOVERABILITY_VALUES];
|
|
2857
|
-
/**
|
|
2858
|
-
* Zod schema for validating ErrorDetails objects.
|
|
2859
|
-
*
|
|
2860
|
-
* This schema provides runtime validation for all ErrorDetails properties,
|
|
2861
|
-
* ensuring type safety and proper error handling for JavaScript consumers.
|
|
2862
|
-
*
|
|
2863
|
-
* @example
|
|
2864
|
-
* ```typescript
|
|
2865
|
-
* import { errorDetailsSchema } from '@core/errors'
|
|
2866
|
-
*
|
|
2867
|
-
* const result = errorDetailsSchema.safeParse({
|
|
2868
|
-
* code: 1001,
|
|
2869
|
-
* name: 'NETWORK_MISMATCH',
|
|
2870
|
-
* recoverability: 'FATAL',
|
|
2871
|
-
* message: 'Source and destination networks must be different'
|
|
2872
|
-
* })
|
|
2873
|
-
*
|
|
2874
|
-
* if (!result.success) {
|
|
2875
|
-
* console.error('Validation failed:', result.error.issues)
|
|
2876
|
-
* }
|
|
2877
|
-
* ```
|
|
2878
|
-
*/
|
|
2879
|
-
const errorDetailsSchema = zod.z.object({
|
|
2880
|
-
/** Numeric identifier following standardized ranges (1000+ for INPUT errors) */
|
|
2881
|
-
code: zod.z
|
|
2882
|
-
.number()
|
|
2883
|
-
.int('Error code must be an integer')
|
|
2884
|
-
.min(1000, 'Error code must be within valid range (1000+)')
|
|
2885
|
-
.max(1099, 'Error code must be within valid range (1099 max)'),
|
|
2886
|
-
/** Human-readable ID (e.g., "NETWORK_MISMATCH") */
|
|
2887
|
-
name: zod.z
|
|
2888
|
-
.string()
|
|
2889
|
-
.min(1, 'Error name must be a non-empty string')
|
|
2890
|
-
.regex(/^[A-Z_][A-Z0-9_]*$/, 'Error name must match pattern: ^[A-Z_][A-Z0-9_]*$'),
|
|
2891
|
-
/** Error handling strategy */
|
|
2892
|
-
recoverability: zod.z.enum(RECOVERABILITY_ARRAY, {
|
|
2893
|
-
errorMap: () => ({
|
|
2894
|
-
message: 'Recoverability must be one of: RETRYABLE, RESUMABLE, FATAL',
|
|
2895
|
-
}),
|
|
2896
|
-
}),
|
|
2897
|
-
/** User-friendly explanation with network context */
|
|
2898
|
-
message: zod.z
|
|
2899
|
-
.string()
|
|
2900
|
-
.min(1, 'Error message must be a non-empty string')
|
|
2901
|
-
.max(500, 'Error message must be 500 characters or less'),
|
|
2902
|
-
/** Raw error details, context, or the original error that caused this one. */
|
|
2903
|
-
cause: zod.z
|
|
2904
|
-
.object({
|
|
2905
|
-
/** Free-form error payload from underlying system */
|
|
2906
|
-
trace: zod.z.unknown().optional(),
|
|
2907
|
-
})
|
|
2908
|
-
.optional(),
|
|
2909
|
-
});
|
|
2910
|
-
|
|
2911
|
-
/**
|
|
2912
|
-
* Validates an ErrorDetails object using Zod schema.
|
|
2913
|
-
*
|
|
2914
|
-
* @param details - The object to validate
|
|
2915
|
-
* @returns The validated ErrorDetails object
|
|
2916
|
-
* @throws {TypeError} When validation fails
|
|
2917
|
-
*
|
|
2918
|
-
* @example
|
|
2919
|
-
* ```typescript
|
|
2920
|
-
* import { validateErrorDetails } from '@core/errors'
|
|
2921
|
-
*
|
|
2922
|
-
* try {
|
|
2923
|
-
* const validDetails = validateErrorDetails({
|
|
2924
|
-
* code: 1001,
|
|
2925
|
-
* name: 'NETWORK_MISMATCH',
|
|
2926
|
-
* recoverability: 'FATAL',
|
|
2927
|
-
* message: 'Source and destination networks must be different'
|
|
2928
|
-
* })
|
|
2929
|
-
* } catch (error) {
|
|
2930
|
-
* console.error('Validation failed:', error.message)
|
|
2931
|
-
* }
|
|
2932
|
-
* ```
|
|
2933
|
-
*/
|
|
2934
|
-
function validateErrorDetails(details) {
|
|
2935
|
-
const result = errorDetailsSchema.safeParse(details);
|
|
2936
|
-
if (!result.success) {
|
|
2937
|
-
const issues = result.error.issues
|
|
2938
|
-
.map((issue) => `${issue.path.join('.')}: ${issue.message}`)
|
|
2939
|
-
.join(', ');
|
|
2940
|
-
throw new TypeError(`Invalid ErrorDetails: ${issues}`);
|
|
2941
|
-
}
|
|
2942
|
-
return result.data;
|
|
2943
|
-
}
|
|
2944
|
-
|
|
2945
|
-
/**
|
|
2946
|
-
* Structured error class for Stablecoin Kit operations.
|
|
2947
|
-
*
|
|
2948
|
-
* This class extends the native Error class while implementing the ErrorDetails
|
|
2949
|
-
* interface, providing a consistent error format for programmatic handling
|
|
2950
|
-
* across the Stablecoin Kits ecosystem. All properties are immutable to ensure
|
|
2951
|
-
* error objects cannot be modified after creation.
|
|
2952
|
-
*
|
|
2953
|
-
* @example
|
|
2954
|
-
* ```typescript
|
|
2955
|
-
* import { KitError } from '@core/errors'
|
|
2956
|
-
*
|
|
2957
|
-
* const error = new KitError({
|
|
2958
|
-
* code: 1001,
|
|
2959
|
-
* name: 'INPUT_NETWORK_MISMATCH',
|
|
2960
|
-
* recoverability: 'FATAL',
|
|
2961
|
-
* message: 'Cannot bridge between mainnet and testnet'
|
|
2962
|
-
* })
|
|
2963
|
-
*
|
|
2964
|
-
* if (error instanceof KitError) {
|
|
2965
|
-
* console.log(`Error ${error.code}: ${error.name}`)
|
|
2966
|
-
* // → "Error 1001: INPUT_NETWORK_MISMATCH"
|
|
2967
|
-
* }
|
|
2968
|
-
* ```
|
|
2969
|
-
*
|
|
2970
|
-
* @example
|
|
2971
|
-
* ```typescript
|
|
2972
|
-
* import { KitError } from '@core/errors'
|
|
2973
|
-
*
|
|
2974
|
-
* // Error with cause information
|
|
2975
|
-
* const error = new KitError({
|
|
2976
|
-
* code: 1002,
|
|
2977
|
-
* name: 'INVALID_AMOUNT',
|
|
2978
|
-
* recoverability: 'FATAL',
|
|
2979
|
-
* message: 'Amount must be greater than zero',
|
|
2980
|
-
* cause: {
|
|
2981
|
-
* trace: { providedAmount: -100, minimumAmount: 0 }
|
|
2982
|
-
* }
|
|
2983
|
-
* })
|
|
2984
|
-
*
|
|
2985
|
-
* throw error
|
|
2986
|
-
* ```
|
|
2987
|
-
*/
|
|
2988
|
-
class KitError extends Error {
|
|
2989
|
-
/** Numeric identifier following standardized ranges (1000+ for INPUT errors) */
|
|
2990
|
-
code;
|
|
2991
|
-
/** Human-readable ID (e.g., "NETWORK_MISMATCH") */
|
|
2992
|
-
name;
|
|
2993
|
-
/** Error handling strategy */
|
|
2994
|
-
recoverability;
|
|
2995
|
-
/** Raw error details, context, or the original error that caused this one. */
|
|
2996
|
-
cause;
|
|
2997
|
-
/**
|
|
2998
|
-
* Create a new KitError instance.
|
|
2999
|
-
*
|
|
3000
|
-
* @param details - The error details object containing all required properties.
|
|
3001
|
-
* @throws \{TypeError\} When details parameter is missing or invalid.
|
|
3002
|
-
*/
|
|
3003
|
-
constructor(details) {
|
|
3004
|
-
// Validate input at runtime for JavaScript consumers using Zod
|
|
3005
|
-
const validatedDetails = validateErrorDetails(details);
|
|
3006
|
-
super(validatedDetails.message);
|
|
3007
|
-
// Set properties as readonly at runtime
|
|
3008
|
-
Object.defineProperties(this, {
|
|
3009
|
-
name: {
|
|
3010
|
-
value: validatedDetails.name,
|
|
3011
|
-
writable: false,
|
|
3012
|
-
enumerable: true,
|
|
3013
|
-
configurable: false,
|
|
3014
|
-
},
|
|
3015
|
-
code: {
|
|
3016
|
-
value: validatedDetails.code,
|
|
3017
|
-
writable: false,
|
|
3018
|
-
enumerable: true,
|
|
3019
|
-
configurable: false,
|
|
3020
|
-
},
|
|
3021
|
-
recoverability: {
|
|
3022
|
-
value: validatedDetails.recoverability,
|
|
3023
|
-
writable: false,
|
|
3024
|
-
enumerable: true,
|
|
3025
|
-
configurable: false,
|
|
3026
|
-
},
|
|
3027
|
-
...(validatedDetails.cause && {
|
|
3028
|
-
cause: {
|
|
3029
|
-
value: validatedDetails.cause,
|
|
3030
|
-
writable: false,
|
|
3031
|
-
enumerable: true,
|
|
3032
|
-
configurable: false,
|
|
3033
|
-
},
|
|
3034
|
-
}),
|
|
3035
|
-
});
|
|
3036
|
-
}
|
|
3037
|
-
}
|
|
3038
|
-
|
|
3039
|
-
/**
|
|
3040
|
-
* Minimum error code for INPUT type errors.
|
|
3041
|
-
* INPUT errors represent validation failures and invalid parameters.
|
|
3042
|
-
*/
|
|
3043
|
-
/**
|
|
3044
|
-
* Standardized error definitions for INPUT type errors.
|
|
3045
|
-
*
|
|
3046
|
-
* Each entry combines the numeric error code with its corresponding
|
|
3047
|
-
* string name to ensure consistency when creating error instances.
|
|
3048
|
-
*
|
|
3049
|
-
* Error codes follow a hierarchical numbering scheme where the first digit
|
|
3050
|
-
* indicates the error category (1 = INPUT) and subsequent digits provide
|
|
3051
|
-
* specific error identification within that category.
|
|
3052
|
-
*
|
|
3053
|
-
*
|
|
3054
|
-
* @example
|
|
3055
|
-
* ```typescript
|
|
3056
|
-
* import { InputError } from '@core/errors'
|
|
3057
|
-
*
|
|
3058
|
-
* const error = new KitError({
|
|
3059
|
-
* ...InputError.NETWORK_MISMATCH,
|
|
3060
|
-
* recoverability: 'FATAL',
|
|
3061
|
-
* message: 'Source and destination networks must be different'
|
|
3062
|
-
* })
|
|
3063
|
-
*
|
|
3064
|
-
* // Access code and name individually if needed
|
|
3065
|
-
* console.log(InputError.NETWORK_MISMATCH.code) // 1001
|
|
3066
|
-
* console.log(InputError.NETWORK_MISMATCH.name) // 'INPUT_NETWORK_MISMATCH'
|
|
3067
|
-
* ```
|
|
3068
|
-
*/
|
|
3069
|
-
const InputError = {
|
|
3070
|
-
/** Unsupported or invalid bridge route configuration */
|
|
3071
|
-
UNSUPPORTED_ROUTE: {
|
|
3072
|
-
code: 1003,
|
|
3073
|
-
name: 'INPUT_UNSUPPORTED_ROUTE',
|
|
3074
|
-
}};
|
|
3075
|
-
|
|
3076
|
-
/**
|
|
3077
|
-
* Creates error for unsupported bridge route.
|
|
3078
|
-
*
|
|
3079
|
-
* This error is thrown when attempting to bridge between chains that don't
|
|
3080
|
-
* have a supported bridge route configured.
|
|
3081
|
-
*
|
|
3082
|
-
* @param source - Source chain name
|
|
3083
|
-
* @param destination - Destination chain name
|
|
3084
|
-
* @returns KitError with specific route details
|
|
3085
|
-
*
|
|
3086
|
-
* @example
|
|
3087
|
-
* ```typescript
|
|
3088
|
-
* import { createUnsupportedRouteError } from '@core/errors'
|
|
3089
|
-
*
|
|
3090
|
-
* throw createUnsupportedRouteError('Ethereum', 'Solana')
|
|
3091
|
-
* // Message: "Route from Ethereum to Solana is not supported"
|
|
3092
|
-
* ```
|
|
3093
|
-
*/
|
|
3094
|
-
function createUnsupportedRouteError(source, destination) {
|
|
3095
|
-
const errorDetails = {
|
|
3096
|
-
...InputError.UNSUPPORTED_ROUTE,
|
|
3097
|
-
recoverability: 'FATAL',
|
|
3098
|
-
message: `Route from ${source} to ${destination} is not supported.`,
|
|
3099
|
-
cause: {
|
|
3100
|
-
trace: { source, destination },
|
|
3101
|
-
},
|
|
3992
|
+
resolvedAddress = await adapter.getAddress(resolvedChain);
|
|
3993
|
+
}
|
|
3994
|
+
catch (error) {
|
|
3995
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
3996
|
+
throw new Error(`Failed to resolve address from user-controlled adapter: ${message}`);
|
|
3997
|
+
}
|
|
3998
|
+
}
|
|
3999
|
+
return {
|
|
4000
|
+
chain: resolvedChain,
|
|
4001
|
+
address: resolvedAddress,
|
|
3102
4002
|
};
|
|
3103
|
-
return new KitError(errorDetails);
|
|
3104
4003
|
}
|
|
3105
4004
|
|
|
3106
4005
|
/**
|
|
@@ -3248,45 +4147,28 @@ class Adapter {
|
|
|
3248
4147
|
* Validate that the target chain is supported by this adapter.
|
|
3249
4148
|
*
|
|
3250
4149
|
* @param targetChain - The chain to validate.
|
|
3251
|
-
* @throws
|
|
4150
|
+
* @throws KitError with INVALID_CHAIN code if the chain is not supported by this adapter.
|
|
3252
4151
|
*/
|
|
3253
4152
|
validateChainSupport(targetChain) {
|
|
3254
4153
|
if (this.capabilities?.supportedChains) {
|
|
3255
4154
|
const isSupported = this.capabilities.supportedChains.some((supportedChain) => supportedChain.chain === targetChain.chain);
|
|
3256
4155
|
if (!isSupported) {
|
|
3257
4156
|
const supportedCount = this.capabilities.supportedChains.length;
|
|
3258
|
-
|
|
3259
|
-
|
|
4157
|
+
// List supported chain names for better user experience
|
|
4158
|
+
const supportedChainNames = this.capabilities.supportedChains
|
|
4159
|
+
.map((chainDef) => chainDef.name)
|
|
4160
|
+
.join(', ');
|
|
4161
|
+
const reason = `Not supported by this adapter. It supports ${supportedCount.toString()} ${supportedCount === 1 ? 'chain' : 'chains'}: ${supportedChainNames}`;
|
|
4162
|
+
throw createInvalidChainError(targetChain.name, reason);
|
|
3260
4163
|
}
|
|
3261
4164
|
}
|
|
3262
4165
|
else if (this.chainType && targetChain.type !== this.chainType) {
|
|
3263
|
-
const
|
|
3264
|
-
throw
|
|
4166
|
+
const reason = `Chain type mismatch: adapter supports ${this.chainType} chains, but received ${targetChain.type ?? 'unknown'} chain`;
|
|
4167
|
+
throw createInvalidChainError(targetChain.name, reason);
|
|
3265
4168
|
}
|
|
3266
4169
|
}
|
|
3267
4170
|
}
|
|
3268
4171
|
|
|
3269
|
-
/**
|
|
3270
|
-
* Custom error class for validation errors.
|
|
3271
|
-
* Provides structured error information while hiding implementation details.
|
|
3272
|
-
*/
|
|
3273
|
-
class ValidationError extends Error {
|
|
3274
|
-
errors;
|
|
3275
|
-
constructor(message, errors) {
|
|
3276
|
-
super(message);
|
|
3277
|
-
this.errors = errors;
|
|
3278
|
-
this.name = 'ValidationError';
|
|
3279
|
-
}
|
|
3280
|
-
}
|
|
3281
|
-
|
|
3282
|
-
/**
|
|
3283
|
-
* Formats a Zod error for display.
|
|
3284
|
-
* @internal
|
|
3285
|
-
*/
|
|
3286
|
-
const formatZodError = (error) => {
|
|
3287
|
-
const path = error.path.length > 0 ? `${error.path.join('.')}: ` : '';
|
|
3288
|
-
return `${path}${error.message}`;
|
|
3289
|
-
};
|
|
3290
4172
|
/**
|
|
3291
4173
|
* Validates data against a Zod schema with enhanced error reporting.
|
|
3292
4174
|
*
|
|
@@ -3294,23 +4176,22 @@ const formatZodError = (error) => {
|
|
|
3294
4176
|
* messages that include the validation context. It's designed to give developers
|
|
3295
4177
|
* clear feedback about what went wrong during validation.
|
|
3296
4178
|
*
|
|
4179
|
+
* @param value - The value to validate
|
|
3297
4180
|
* @param schema - The Zod schema to validate against
|
|
3298
|
-
* @param data - The data to validate
|
|
3299
4181
|
* @param context - Context string to include in error messages (e.g., 'bridge parameters')
|
|
3300
|
-
* @returns
|
|
3301
|
-
* @throws {
|
|
4182
|
+
* @returns Asserts that value is of type T (type narrowing)
|
|
4183
|
+
* @throws {KitError} If validation fails with INPUT_VALIDATION_FAILED code (1098)
|
|
3302
4184
|
*
|
|
3303
4185
|
* @example
|
|
3304
4186
|
* ```typescript
|
|
3305
|
-
*
|
|
4187
|
+
* validate(params, BridgeParamsSchema, 'bridge parameters')
|
|
4188
|
+
* // After this call, TypeScript knows params is of type BridgeParams
|
|
3306
4189
|
* ```
|
|
3307
4190
|
*/
|
|
3308
4191
|
function validate(value, schema, context) {
|
|
3309
4192
|
const result = schema.safeParse(value);
|
|
3310
4193
|
if (!result.success) {
|
|
3311
|
-
|
|
3312
|
-
const firstError = errors[0] ?? 'Invalid value';
|
|
3313
|
-
throw new ValidationError(`Invalid ${context}: ${firstError}`, errors);
|
|
4194
|
+
throw createValidationErrorFromZod(result.error, context);
|
|
3314
4195
|
}
|
|
3315
4196
|
}
|
|
3316
4197
|
|
|
@@ -3328,11 +4209,12 @@ const VALIDATION_STATE = Symbol('validationState');
|
|
|
3328
4209
|
* and providing detailed error messages. It's designed for use in scenarios where
|
|
3329
4210
|
* validation state needs to be monitored and reported.
|
|
3330
4211
|
*
|
|
4212
|
+
* @param value - The value to validate
|
|
3331
4213
|
* @param schema - The Zod schema to validate against
|
|
3332
|
-
* @param data - The data to validate
|
|
3333
4214
|
* @param context - Context string to include in error messages (e.g., 'bridge parameters')
|
|
3334
|
-
* @
|
|
3335
|
-
* @
|
|
4215
|
+
* @param validatorName - Symbol identifying the validator for state tracking
|
|
4216
|
+
* @returns Asserts that value is of type T (type narrowing)
|
|
4217
|
+
* @throws {KitError} If validation fails with INPUT_VALIDATION_FAILED code (1098)
|
|
3336
4218
|
*
|
|
3337
4219
|
* @example
|
|
3338
4220
|
* ```typescript
|
|
@@ -3342,20 +4224,41 @@ const VALIDATION_STATE = Symbol('validationState');
|
|
|
3342
4224
|
function validateWithStateTracking(value, schema, context, validatorName) {
|
|
3343
4225
|
// Skip validation for null or undefined values
|
|
3344
4226
|
if (value === null) {
|
|
3345
|
-
throw new
|
|
3346
|
-
|
|
3347
|
-
|
|
4227
|
+
throw new KitError({
|
|
4228
|
+
...InputError.VALIDATION_FAILED,
|
|
4229
|
+
recoverability: 'FATAL',
|
|
4230
|
+
message: `Invalid ${context}: Value is null`,
|
|
4231
|
+
cause: {
|
|
4232
|
+
trace: {
|
|
4233
|
+
validationErrors: ['Value is null'],
|
|
4234
|
+
},
|
|
4235
|
+
},
|
|
4236
|
+
});
|
|
3348
4237
|
}
|
|
3349
4238
|
if (value === undefined) {
|
|
3350
|
-
throw new
|
|
3351
|
-
|
|
3352
|
-
|
|
4239
|
+
throw new KitError({
|
|
4240
|
+
...InputError.VALIDATION_FAILED,
|
|
4241
|
+
recoverability: 'FATAL',
|
|
4242
|
+
message: `Invalid ${context}: Value is undefined`,
|
|
4243
|
+
cause: {
|
|
4244
|
+
trace: {
|
|
4245
|
+
validationErrors: ['Value is undefined'],
|
|
4246
|
+
},
|
|
4247
|
+
},
|
|
4248
|
+
});
|
|
3353
4249
|
}
|
|
3354
4250
|
// Ensure value is an object that can hold validation state
|
|
3355
4251
|
if (typeof value !== 'object') {
|
|
3356
|
-
throw new
|
|
3357
|
-
|
|
3358
|
-
|
|
4252
|
+
throw new KitError({
|
|
4253
|
+
...InputError.VALIDATION_FAILED,
|
|
4254
|
+
recoverability: 'FATAL',
|
|
4255
|
+
message: `Invalid ${context}: Value must be an object`,
|
|
4256
|
+
cause: {
|
|
4257
|
+
trace: {
|
|
4258
|
+
validationErrors: [`Value must be an object, got ${typeof value}`],
|
|
4259
|
+
},
|
|
4260
|
+
},
|
|
4261
|
+
});
|
|
3359
4262
|
}
|
|
3360
4263
|
// Get or initialize validation state
|
|
3361
4264
|
const valueWithState = value;
|
|
@@ -3364,7 +4267,7 @@ function validateWithStateTracking(value, schema, context, validatorName) {
|
|
|
3364
4267
|
if (state.validatedBy.includes(validatorName)) {
|
|
3365
4268
|
return;
|
|
3366
4269
|
}
|
|
3367
|
-
// Delegate to the validate function for actual validation
|
|
4270
|
+
// Delegate to the validate function for actual validation (now throws KitError)
|
|
3368
4271
|
validate(value, schema, context);
|
|
3369
4272
|
// Update validation state
|
|
3370
4273
|
state.validatedBy.push(validatorName);
|
|
@@ -3419,7 +4322,7 @@ zod.z
|
|
|
3419
4322
|
.url('Generated explorer URL is invalid');
|
|
3420
4323
|
|
|
3421
4324
|
/**
|
|
3422
|
-
* Validates data against a Zod schema and throws a
|
|
4325
|
+
* Validates data against a Zod schema and throws a KitError on failure.
|
|
3423
4326
|
*
|
|
3424
4327
|
* This utility function provides consistent validation and error formatting across the codebase.
|
|
3425
4328
|
* It performs the validation and formats error messages with contextual information while
|
|
@@ -3428,7 +4331,8 @@ zod.z
|
|
|
3428
4331
|
* @param value - The value to validate
|
|
3429
4332
|
* @param schema - The Zod schema to validate against
|
|
3430
4333
|
* @param message - Error message (e.g., 'Invalid EVM address', 'Configuration error')
|
|
3431
|
-
* @
|
|
4334
|
+
* @returns Asserts that value is of type T (type narrowing)
|
|
4335
|
+
* @throws {KitError} If validation fails with INPUT_VALIDATION_FAILED code (1098) and detailed error information
|
|
3432
4336
|
*
|
|
3433
4337
|
* @example
|
|
3434
4338
|
* ```typescript
|
|
@@ -3440,10 +4344,9 @@ zod.z
|
|
|
3440
4344
|
* age: z.number().positive()
|
|
3441
4345
|
* })
|
|
3442
4346
|
*
|
|
3443
|
-
* // This will throw
|
|
4347
|
+
* // This will throw KitError if validation fails
|
|
3444
4348
|
* validateOrThrow({ name: 'Jo', age: -1 }, userSchema, 'Invalid user data')
|
|
3445
|
-
* // Throws:
|
|
3446
|
-
* // ["name: String must contain at least 3 character(s)", "age: Number must be greater than 0"])
|
|
4349
|
+
* // Throws: KitError with code 1098 and message "Invalid user data: name: String must contain at least 3 character(s)"
|
|
3447
4350
|
* ```
|
|
3448
4351
|
*
|
|
3449
4352
|
* @example
|
|
@@ -3466,13 +4369,7 @@ zod.z
|
|
|
3466
4369
|
function validateOrThrow(value, schema, message) {
|
|
3467
4370
|
const result = schema.safeParse(value);
|
|
3468
4371
|
if (!result.success) {
|
|
3469
|
-
|
|
3470
|
-
const path = error.path.length > 0 ? `${error.path.join('.')}: ` : '';
|
|
3471
|
-
return `${path}${error.message}`;
|
|
3472
|
-
});
|
|
3473
|
-
const firstError = errors[0];
|
|
3474
|
-
const errorMessage = firstError !== undefined ? `${message}: ${firstError}` : message;
|
|
3475
|
-
throw new ValidationError(errorMessage, errors);
|
|
4372
|
+
throw createValidationErrorFromZod(result.error, message);
|
|
3476
4373
|
}
|
|
3477
4374
|
}
|
|
3478
4375
|
|
|
@@ -3650,7 +4547,7 @@ const convertAddress = (address, targetFormat) => {
|
|
|
3650
4547
|
* This schema does not validate length, making it suitable for various hex string types
|
|
3651
4548
|
* like addresses, transaction hashes, and other hex-encoded data.
|
|
3652
4549
|
*
|
|
3653
|
-
* @throws {
|
|
4550
|
+
* @throws {KitError} If validation fails with INPUT_VALIDATION_FAILED code (1098), with details about which properties failed
|
|
3654
4551
|
*
|
|
3655
4552
|
* @example
|
|
3656
4553
|
* ```typescript
|
|
@@ -3681,7 +4578,7 @@ const hexStringSchema = zod.z
|
|
|
3681
4578
|
* - Must be a valid hex string with '0x' prefix
|
|
3682
4579
|
* - Must be exactly 42 characters long (0x + 40 hex characters)
|
|
3683
4580
|
*
|
|
3684
|
-
* @throws {
|
|
4581
|
+
* @throws {KitError} If validation fails with INPUT_VALIDATION_FAILED code (1098), with details about which properties failed
|
|
3685
4582
|
*
|
|
3686
4583
|
* @example
|
|
3687
4584
|
* ```typescript
|
|
@@ -3701,7 +4598,7 @@ const evmAddressSchema = hexStringSchema.refine((value) => value.length === 42,
|
|
|
3701
4598
|
* - Must be a valid hex string with '0x' prefix
|
|
3702
4599
|
* - Must be exactly 66 characters long (0x + 64 hex characters)
|
|
3703
4600
|
*
|
|
3704
|
-
* @throws {
|
|
4601
|
+
* @throws {KitError} If validation fails with INPUT_VALIDATION_FAILED code (1098), with details about which properties failed
|
|
3705
4602
|
*
|
|
3706
4603
|
* @example
|
|
3707
4604
|
* ```typescript
|
|
@@ -3727,7 +4624,7 @@ hexStringSchema.refine((value) => value.length === 66, 'Transaction hash must be
|
|
|
3727
4624
|
* This schema does not validate length, making it suitable for various base58-encoded data
|
|
3728
4625
|
* like Solana addresses, transaction signatures, and other base58-encoded data.
|
|
3729
4626
|
*
|
|
3730
|
-
* @throws {
|
|
4627
|
+
* @throws {KitError} If validation fails with INPUT_VALIDATION_FAILED code (1098), with details about which properties failed
|
|
3731
4628
|
*
|
|
3732
4629
|
* @example
|
|
3733
4630
|
* ```typescript
|
|
@@ -3759,7 +4656,7 @@ const base58StringSchema = zod.z
|
|
|
3759
4656
|
* - Must be a valid base58-encoded string
|
|
3760
4657
|
* - Must be between 32-44 characters long (typical length for base58-encoded 32-byte addresses)
|
|
3761
4658
|
*
|
|
3762
|
-
* @throws {
|
|
4659
|
+
* @throws {KitError} If validation fails with INPUT_VALIDATION_FAILED code (1098), with details about which properties failed
|
|
3763
4660
|
*
|
|
3764
4661
|
* @example
|
|
3765
4662
|
* ```typescript
|
|
@@ -3779,7 +4676,7 @@ base58StringSchema.refine((value) => value.length >= 32 && value.length <= 44, '
|
|
|
3779
4676
|
* - Must be a valid base58-encoded string
|
|
3780
4677
|
* - Must be between 86-88 characters long (typical length for base58-encoded 64-byte signatures)
|
|
3781
4678
|
*
|
|
3782
|
-
* @throws {
|
|
4679
|
+
* @throws {KitError} If validation fails with INPUT_VALIDATION_FAILED code (1098), with details about which properties failed
|
|
3783
4680
|
*
|
|
3784
4681
|
* @example
|
|
3785
4682
|
* ```typescript
|
|
@@ -3808,7 +4705,7 @@ const adapterSchema = zod.z.object({
|
|
|
3808
4705
|
* - A valid EVM address
|
|
3809
4706
|
*
|
|
3810
4707
|
* @param address - The address to validate
|
|
3811
|
-
* @throws {
|
|
4708
|
+
* @throws {KitError} If validation fails with INPUT_VALIDATION_FAILED code (1098), with details about which properties failed
|
|
3812
4709
|
*
|
|
3813
4710
|
* @example
|
|
3814
4711
|
* ```typescript
|
|
@@ -8348,7 +9245,9 @@ function isReadOnlyFunction(abi, functionName, defaultValue) {
|
|
|
8348
9245
|
* to EVM adapters (Ethers, Viem, etc.). For non-EVM adapters, a different validation
|
|
8349
9246
|
* schema should be used.
|
|
8350
9247
|
*
|
|
8351
|
-
*
|
|
9248
|
+
* When used with the validate() function from @core/utils, this throws KitError
|
|
9249
|
+
* with INPUT_VALIDATION_FAILED code if validation fails, with details about which
|
|
9250
|
+
* properties failed.
|
|
8352
9251
|
*
|
|
8353
9252
|
* @example
|
|
8354
9253
|
* ```typescript
|
|
@@ -8379,7 +9278,7 @@ function isReadOnlyFunction(abi, functionName, defaultValue) {
|
|
|
8379
9278
|
* supportedChains: [{ type: 'solana', name: 'Solana', chainId: 0 }]
|
|
8380
9279
|
* }
|
|
8381
9280
|
*
|
|
8382
|
-
* AdapterCapabilitiesSchema.parse(invalidCapabilities) // throws
|
|
9281
|
+
* AdapterCapabilitiesSchema.parse(invalidCapabilities) // throws Zod validation error
|
|
8383
9282
|
* ```
|
|
8384
9283
|
*/
|
|
8385
9284
|
const AdapterCapabilitiesSchema = zod.z
|
|
@@ -8424,7 +9323,7 @@ const AdapterCapabilitiesSchema = zod.z
|
|
|
8424
9323
|
* specification with all required fields and proper types. The domain
|
|
8425
9324
|
* separator prevents signature replay attacks across different domains.
|
|
8426
9325
|
*
|
|
8427
|
-
* @throws
|
|
9326
|
+
* @throws KitError if validation fails
|
|
8428
9327
|
*
|
|
8429
9328
|
* @example
|
|
8430
9329
|
* ```typescript
|
|
@@ -8482,7 +9381,7 @@ const eip712DomainSchema = zod.z.object({
|
|
|
8482
9381
|
* This schema validates individual field definitions within EIP-712 type
|
|
8483
9382
|
* definitions. Each field must have a name and a valid Solidity type.
|
|
8484
9383
|
*
|
|
8485
|
-
* @throws
|
|
9384
|
+
* @throws KitError if validation fails
|
|
8486
9385
|
*
|
|
8487
9386
|
* @example
|
|
8488
9387
|
* ```typescript
|
|
@@ -8522,7 +9421,7 @@ const typedDataFieldSchema = zod.z.object({
|
|
|
8522
9421
|
* domain, types, primaryType, and message. It ensures all components are
|
|
8523
9422
|
* properly structured and the primaryType exists in the types definition.
|
|
8524
9423
|
*
|
|
8525
|
-
* @throws
|
|
9424
|
+
* @throws KitError if validation fails
|
|
8526
9425
|
*
|
|
8527
9426
|
* @example
|
|
8528
9427
|
* ```typescript
|
|
@@ -8600,7 +9499,7 @@ const abiParameterSchema = zod.z.object({
|
|
|
8600
9499
|
* This schema validates individual ABI entries that describe contract functions,
|
|
8601
9500
|
* constructors, events, and other contract interface elements.
|
|
8602
9501
|
*
|
|
8603
|
-
* @throws
|
|
9502
|
+
* @throws KitError if validation fails
|
|
8604
9503
|
*
|
|
8605
9504
|
* @example
|
|
8606
9505
|
* ```typescript
|
|
@@ -8644,7 +9543,7 @@ const abiEntrySchema = zod.z.object({
|
|
|
8644
9543
|
* This schema validates arrays of ABI entries, ensuring each entry is properly
|
|
8645
9544
|
* structured for contract interaction.
|
|
8646
9545
|
*
|
|
8647
|
-
* @throws
|
|
9546
|
+
* @throws KitError if validation fails
|
|
8648
9547
|
*
|
|
8649
9548
|
* @example
|
|
8650
9549
|
* ```typescript
|
|
@@ -8675,7 +9574,7 @@ const abiSchema = zod.z
|
|
|
8675
9574
|
* - An actionRegistry with registerHandlers method and actionHandlers record
|
|
8676
9575
|
* - A prepare method for creating chain requests
|
|
8677
9576
|
*
|
|
8678
|
-
* @throws
|
|
9577
|
+
* @throws KitError if validation fails
|
|
8679
9578
|
*
|
|
8680
9579
|
* @example
|
|
8681
9580
|
* ```typescript
|
|
@@ -8709,7 +9608,7 @@ adapterSchema.extend({
|
|
|
8709
9608
|
* This schema validates the parameters needed to execute a contract call on an EVM-compatible chain.
|
|
8710
9609
|
* It ensures proper formatting of addresses, ABI structure, and function parameters.
|
|
8711
9610
|
*
|
|
8712
|
-
* @throws
|
|
9611
|
+
* @throws KitError if validation fails
|
|
8713
9612
|
*
|
|
8714
9613
|
* @example
|
|
8715
9614
|
* ```typescript
|
|
@@ -8761,7 +9660,7 @@ const evmPreparedChainRequestParamsSchema = zod.z.object({
|
|
|
8761
9660
|
* - Must be exactly 66 characters long (0x + 64 hex characters)
|
|
8762
9661
|
* - Must contain only valid hexadecimal characters (0-9, a-f, A-F)
|
|
8763
9662
|
*
|
|
8764
|
-
* @throws
|
|
9663
|
+
* @throws KitError if validation fails
|
|
8765
9664
|
*
|
|
8766
9665
|
* @example
|
|
8767
9666
|
* ```typescript
|
|
@@ -8804,7 +9703,7 @@ const evmTransactionHashSchema = zod.z
|
|
|
8804
9703
|
* This is a shared schema used by both ethers.v6 and viem.v2 adapters to ensure
|
|
8805
9704
|
* consistent private key validation across all EVM adapters.
|
|
8806
9705
|
*
|
|
8807
|
-
* @throws
|
|
9706
|
+
* @throws KitError if validation fails
|
|
8808
9707
|
*
|
|
8809
9708
|
* @example
|
|
8810
9709
|
* ```typescript
|
|
@@ -8840,7 +9739,7 @@ const assertEvmPreparedChainRequestParamsSymbol = Symbol('assertEvmPreparedChain
|
|
|
8840
9739
|
* - Arguments that match the function signature
|
|
8841
9740
|
*
|
|
8842
9741
|
* @param params - The parameters to validate
|
|
8843
|
-
* @throws
|
|
9742
|
+
* @throws KitError with INPUT_VALIDATION_FAILED code if validation fails, with details about which properties failed
|
|
8844
9743
|
*
|
|
8845
9744
|
* @example
|
|
8846
9745
|
* ```typescript
|
|
@@ -8855,7 +9754,7 @@ const assertEvmPreparedChainRequestParamsSymbol = Symbol('assertEvmPreparedChain
|
|
|
8855
9754
|
* args: ['0xSpenderAddress', BigInt(1000000)]
|
|
8856
9755
|
* }
|
|
8857
9756
|
*
|
|
8858
|
-
* // This will throw if validation fails
|
|
9757
|
+
* // This will throw KitError if validation fails
|
|
8859
9758
|
* assertEvmPreparedChainRequestParams(requestParams)
|
|
8860
9759
|
*
|
|
8861
9760
|
* // If we get here, request parameters are guaranteed to be valid
|
|
@@ -8876,7 +9775,7 @@ function assertEvmPreparedChainRequestParams(params) {
|
|
|
8876
9775
|
* - Must contain only valid hexadecimal characters (0-9, a-f, A-F)
|
|
8877
9776
|
*
|
|
8878
9777
|
* @param txHash - The transaction hash to validate
|
|
8879
|
-
* @throws
|
|
9778
|
+
* @throws KitError with INPUT_VALIDATION_FAILED code if validation fails, with details about which properties failed
|
|
8880
9779
|
*
|
|
8881
9780
|
* @example
|
|
8882
9781
|
* ```typescript
|
|
@@ -8888,7 +9787,7 @@ function assertEvmPreparedChainRequestParams(params) {
|
|
|
8888
9787
|
*
|
|
8889
9788
|
* // Invalid transaction hash
|
|
8890
9789
|
* assertEvmTransactionHash('invalid-hash')
|
|
8891
|
-
* // Throws
|
|
9790
|
+
* // Throws KitError with descriptive message
|
|
8892
9791
|
* ```
|
|
8893
9792
|
*/
|
|
8894
9793
|
function assertEvmTransactionHash(txHash) {
|
|
@@ -8912,7 +9811,7 @@ function assertEvmTransactionHash(txHash) {
|
|
|
8912
9811
|
* @typeParam Types - The EIP-712 types definition for the standard being validated
|
|
8913
9812
|
* @typeParam Message - The message structure for the standard being validated
|
|
8914
9813
|
* @param typedData - The typed data to validate
|
|
8915
|
-
* @throws {
|
|
9814
|
+
* @throws {KitError} If validation fails with INPUT_VALIDATION_FAILED code (1098), with details about which properties failed
|
|
8916
9815
|
*
|
|
8917
9816
|
* @example
|
|
8918
9817
|
* ```typescript
|
|
@@ -9711,7 +10610,7 @@ zod.z.object({
|
|
|
9711
10610
|
* Validates EthersAdapterOptions configuration.
|
|
9712
10611
|
*
|
|
9713
10612
|
* @param options - The EthersAdapterOptions to validate. Must include a `getProvider` function and a `signer` object.
|
|
9714
|
-
* @throws
|
|
10613
|
+
* @throws KitError with INPUT_VALIDATION_FAILED code if validation fails.
|
|
9715
10614
|
*
|
|
9716
10615
|
* @example
|
|
9717
10616
|
* ```typescript
|
|
@@ -9723,7 +10622,7 @@ zod.z.object({
|
|
|
9723
10622
|
* signer: Wallet.createRandom(),
|
|
9724
10623
|
* }
|
|
9725
10624
|
*
|
|
9726
|
-
* validateEthersAdapterOptions(options) // throws if invalid
|
|
10625
|
+
* validateEthersAdapterOptions(options) // throws KitError if invalid
|
|
9727
10626
|
* ```
|
|
9728
10627
|
*/
|
|
9729
10628
|
function validateEthersAdapterOptions(options) {
|
|
@@ -9732,11 +10631,11 @@ function validateEthersAdapterOptions(options) {
|
|
|
9732
10631
|
/**
|
|
9733
10632
|
* Validates parameters for creating an Ethers adapter from an EIP-1193 provider.
|
|
9734
10633
|
*
|
|
9735
|
-
* This function validates the parameters used by the
|
|
10634
|
+
* This function validates the parameters used by the createEthersAdapterFromProvider
|
|
9736
10635
|
* factory function for the Ethers adapter.
|
|
9737
10636
|
*
|
|
9738
10637
|
* @param params - The parameters to validate. Must include a `provider` object (EIP-1193 compatible) and optionally a `getProvider` function.
|
|
9739
|
-
* @throws
|
|
10638
|
+
* @throws KitError with INPUT_VALIDATION_FAILED code if validation fails.
|
|
9740
10639
|
* @example
|
|
9741
10640
|
* ```typescript
|
|
9742
10641
|
* import { validateCreateAdapterFromProviderParams } from '@circle-fin/adapter-ethers-v6/validation'
|
|
@@ -9745,7 +10644,7 @@ function validateEthersAdapterOptions(options) {
|
|
|
9745
10644
|
* provider: window.ethereum,
|
|
9746
10645
|
* }
|
|
9747
10646
|
*
|
|
9748
|
-
* validateCreateAdapterFromProviderParams(params) // throws if invalid
|
|
10647
|
+
* validateCreateAdapterFromProviderParams(params) // throws KitError if invalid
|
|
9749
10648
|
* ```
|
|
9750
10649
|
*/
|
|
9751
10650
|
function validateCreateAdapterFromProviderParams(params) {
|
|
@@ -9759,7 +10658,7 @@ function validateCreateAdapterFromProviderParams(params) {
|
|
|
9759
10658
|
* checking the supportedChains property for EVM compatibility.
|
|
9760
10659
|
*
|
|
9761
10660
|
* @param capabilities - The adapter capabilities to validate
|
|
9762
|
-
* @throws
|
|
10661
|
+
* @throws KitError with INPUT_VALIDATION_FAILED code when capabilities validation fails
|
|
9763
10662
|
*
|
|
9764
10663
|
* @example
|
|
9765
10664
|
* ```typescript
|
|
@@ -9790,7 +10689,7 @@ function validateAdapterCapabilities(capabilities) {
|
|
|
9790
10689
|
* Creates a new EthersAdapter instance with explicit capabilities and OperationContext support.
|
|
9791
10690
|
*
|
|
9792
10691
|
* This constructor is typically not called directly. Instead, use the factory functions
|
|
9793
|
-
* {@link
|
|
10692
|
+
* {@link createEthersAdapterFromPrivateKey} or {@link createEthersAdapterFromProvider} which provide
|
|
9794
10693
|
* smart defaults and better developer experience.
|
|
9795
10694
|
*
|
|
9796
10695
|
* @remarks
|
|
@@ -9802,8 +10701,8 @@ function validateAdapterCapabilities(capabilities) {
|
|
|
9802
10701
|
* - `supportedChains`: Array of EVM chains this adapter can operate on
|
|
9803
10702
|
*
|
|
9804
10703
|
* **Factory Functions (Recommended):**
|
|
9805
|
-
* - {@link
|
|
9806
|
-
* - {@link
|
|
10704
|
+
* - {@link createEthersAdapterFromPrivateKey} - For server-side/programmatic use with private keys
|
|
10705
|
+
* - {@link createEthersAdapterFromProvider} - For browser wallets (MetaMask, WalletConnect, etc.)
|
|
9807
10706
|
*
|
|
9808
10707
|
* @param options - Configuration options including provider getter and signer
|
|
9809
10708
|
* @param capabilities - Adapter capabilities defining address control and supported chains
|
|
@@ -9827,10 +10726,10 @@ function validateAdapterCapabilities(capabilities) {
|
|
|
9827
10726
|
* @example
|
|
9828
10727
|
* ```typescript
|
|
9829
10728
|
* // Recommended: Use factory functions instead
|
|
9830
|
-
* import {
|
|
10729
|
+
* import { createEthersAdapterFromPrivateKey } from '@circle-fin/adapter-ethers-v6'
|
|
9831
10730
|
*
|
|
9832
10731
|
* // Minimal configuration with default capabilities
|
|
9833
|
-
* const adapter =
|
|
10732
|
+
* const adapter = createEthersAdapterFromPrivateKey({
|
|
9834
10733
|
* privateKey: '0x...'
|
|
9835
10734
|
* // Defaults: user-controlled + all EVM chains
|
|
9836
10735
|
* })
|
|
@@ -9890,8 +10789,31 @@ class EthersAdapter extends EvmAdapter {
|
|
|
9890
10789
|
if (chain.type !== 'evm') {
|
|
9891
10790
|
throw new Error(`EthersAdapter can only switch to EVM chains. Received: ${String(chain.type)} (${chain.name})`);
|
|
9892
10791
|
}
|
|
9893
|
-
//
|
|
9894
|
-
|
|
10792
|
+
// Check if we're already on the target chain
|
|
10793
|
+
const currentSigner = this.getSigner();
|
|
10794
|
+
const currentProvider = currentSigner?.provider;
|
|
10795
|
+
if (currentProvider) {
|
|
10796
|
+
try {
|
|
10797
|
+
const network = await currentProvider.getNetwork();
|
|
10798
|
+
// If already on the target chain, skip switching
|
|
10799
|
+
if (network.chainId === BigInt(chain.chainId)) {
|
|
10800
|
+
return;
|
|
10801
|
+
}
|
|
10802
|
+
}
|
|
10803
|
+
catch {
|
|
10804
|
+
// If we can't get the current chain ID, proceed with switching
|
|
10805
|
+
// This ensures we don't break existing functionality if getNetwork fails
|
|
10806
|
+
}
|
|
10807
|
+
}
|
|
10808
|
+
const provider = await this.getProvider(chain);
|
|
10809
|
+
// If the signer is a Wallet, reconnect it with the correct provider
|
|
10810
|
+
if (currentSigner instanceof ethers.Wallet) {
|
|
10811
|
+
this._signer = currentSigner.connect(provider);
|
|
10812
|
+
}
|
|
10813
|
+
else {
|
|
10814
|
+
// For browser wallets, use the original switchChain logic
|
|
10815
|
+
this._signer = await switchChain(currentSigner, chain);
|
|
10816
|
+
}
|
|
9895
10817
|
}
|
|
9896
10818
|
/**
|
|
9897
10819
|
* Gets the cached Provider or initializes it from options if not already cached.
|
|
@@ -9957,14 +10879,17 @@ class EthersAdapter extends EvmAdapter {
|
|
|
9957
10879
|
/**
|
|
9958
10880
|
* Simulates a contract function call using Ethers v6 `.staticCall`.
|
|
9959
10881
|
*/
|
|
9960
|
-
async simulateFunctionCall(contract, functionName, args) {
|
|
10882
|
+
async simulateFunctionCall(contract, functionName, args, chain) {
|
|
9961
10883
|
try {
|
|
9962
10884
|
const func = contract.getFunction(functionName);
|
|
9963
10885
|
await func.staticCall(...args);
|
|
9964
10886
|
}
|
|
9965
10887
|
catch (err) {
|
|
9966
|
-
|
|
9967
|
-
throw
|
|
10888
|
+
// Wrap simulation errors with structured error format
|
|
10889
|
+
throw parseBlockchainError(err, {
|
|
10890
|
+
chain: chain.name,
|
|
10891
|
+
operation: 'simulation',
|
|
10892
|
+
});
|
|
9968
10893
|
}
|
|
9969
10894
|
}
|
|
9970
10895
|
/**
|
|
@@ -9991,7 +10916,11 @@ class EthersAdapter extends EvmAdapter {
|
|
|
9991
10916
|
errorMessage.toLocaleLowerCase().includes('execution reverted')) {
|
|
9992
10917
|
return fallback;
|
|
9993
10918
|
}
|
|
9994
|
-
|
|
10919
|
+
// Wrap gas estimation errors with structured error format
|
|
10920
|
+
throw parseBlockchainError(error, {
|
|
10921
|
+
chain: chain.name,
|
|
10922
|
+
operation: 'estimateGas',
|
|
10923
|
+
});
|
|
9995
10924
|
}
|
|
9996
10925
|
let gasPrice;
|
|
9997
10926
|
try {
|
|
@@ -9999,7 +10928,11 @@ class EthersAdapter extends EvmAdapter {
|
|
|
9999
10928
|
gasPrice = await this.fetchGasPrice(chain);
|
|
10000
10929
|
}
|
|
10001
10930
|
catch (error) {
|
|
10002
|
-
|
|
10931
|
+
// Wrap gas price errors with structured error format
|
|
10932
|
+
throw parseBlockchainError(error, {
|
|
10933
|
+
chain: chain.name,
|
|
10934
|
+
operation: 'getGasPrice',
|
|
10935
|
+
});
|
|
10003
10936
|
}
|
|
10004
10937
|
return { gas, gasPrice, fee: (gas * gasPrice).toString() };
|
|
10005
10938
|
}
|
|
@@ -10055,7 +10988,11 @@ class EthersAdapter extends EvmAdapter {
|
|
|
10055
10988
|
const shouldRetry = ethersNonceManager.shouldRetryNonceError(error, userProvidedNonce);
|
|
10056
10989
|
// Don't retry if: not a nonce error, user provided nonce, or this is the last attempt
|
|
10057
10990
|
if (!shouldRetry || attempt === maxAttempts) {
|
|
10058
|
-
|
|
10991
|
+
// Wrap transaction execution errors with structured error format
|
|
10992
|
+
throw parseBlockchainError(error, {
|
|
10993
|
+
chain: chain.name,
|
|
10994
|
+
operation: 'sendTransaction',
|
|
10995
|
+
});
|
|
10059
10996
|
}
|
|
10060
10997
|
// Retry with resync on nonce-related errors (this will set a new nonce)
|
|
10061
10998
|
txRequest.nonce = await ethersNonceManager.resyncAndAllocate(provider, chain.chainId, fromAddress);
|
|
@@ -10094,10 +11031,10 @@ class EthersAdapter extends EvmAdapter {
|
|
|
10094
11031
|
*
|
|
10095
11032
|
* @example
|
|
10096
11033
|
* ```typescript
|
|
10097
|
-
* import {
|
|
11034
|
+
* import { createEthersAdapterFromPrivateKey } from '@circle-fin/adapter-ethers-v6'
|
|
10098
11035
|
* import { parseAbi } from 'ethers'
|
|
10099
11036
|
*
|
|
10100
|
-
* const adapter =
|
|
11037
|
+
* const adapter = createEthersAdapterFromPrivateKey({
|
|
10101
11038
|
* privateKey: process.env.PRIVATE_KEY
|
|
10102
11039
|
* })
|
|
10103
11040
|
*
|
|
@@ -10121,7 +11058,7 @@ class EthersAdapter extends EvmAdapter {
|
|
|
10121
11058
|
* @example
|
|
10122
11059
|
* ```typescript
|
|
10123
11060
|
* // Multi-chain usage with same adapter instance
|
|
10124
|
-
* const adapter =
|
|
11061
|
+
* const adapter = createEthersAdapterFromPrivateKey({
|
|
10125
11062
|
* privateKey: process.env.PRIVATE_KEY
|
|
10126
11063
|
* })
|
|
10127
11064
|
*
|
|
@@ -10149,9 +11086,9 @@ class EthersAdapter extends EvmAdapter {
|
|
|
10149
11086
|
* @example
|
|
10150
11087
|
* ```typescript
|
|
10151
11088
|
* // Address resolution patterns
|
|
10152
|
-
* import {
|
|
11089
|
+
* import { createEthersAdapterFromProvider } from '@circle-fin/adapter-ethers-v6'
|
|
10153
11090
|
*
|
|
10154
|
-
* const adapter = await
|
|
11091
|
+
* const adapter = await createEthersAdapterFromProvider({
|
|
10155
11092
|
* provider: window.ethereum
|
|
10156
11093
|
* // Defaults to user-controlled address context
|
|
10157
11094
|
* })
|
|
@@ -10228,7 +11165,7 @@ class EthersAdapter extends EvmAdapter {
|
|
|
10228
11165
|
},
|
|
10229
11166
|
execute: async (overrides) => {
|
|
10230
11167
|
// Simulate the function call to catch errors before submission
|
|
10231
|
-
await this.simulateFunctionCall(contract, functionName, args);
|
|
11168
|
+
await this.simulateFunctionCall(contract, functionName, args, targetChain);
|
|
10232
11169
|
await this.ensureChain(targetChain);
|
|
10233
11170
|
// Reconnect the contract with the current signer, which is on the correct
|
|
10234
11171
|
// chain after `ensureChain`, to ensure the transaction is populated and
|
|
@@ -10257,7 +11194,7 @@ class EthersAdapter extends EvmAdapter {
|
|
|
10257
11194
|
* adapter interface, it is effectively required and enforced at runtime. This ensures
|
|
10258
11195
|
* the adapter is connected to the correct chain before querying the signer.
|
|
10259
11196
|
*
|
|
10260
|
-
* @param chain - The chain to use for address resolution
|
|
11197
|
+
* @param chain - The chain to use for address resolution.
|
|
10261
11198
|
* @returns A promise that resolves to the signer's address
|
|
10262
11199
|
* @throws Error when no chain is provided
|
|
10263
11200
|
*
|
|
@@ -10269,26 +11206,15 @@ class EthersAdapter extends EvmAdapter {
|
|
|
10269
11206
|
* const address = await adapter.getAddress(Ethereum)
|
|
10270
11207
|
* console.log('Wallet address:', address)
|
|
10271
11208
|
* ```
|
|
10272
|
-
*
|
|
10273
|
-
* @example
|
|
10274
|
-
* ```typescript
|
|
10275
|
-
* // In practice, address resolution happens automatically
|
|
10276
|
-
* const prepared = await adapter.prepare(params, {
|
|
10277
|
-
* chain: 'Ethereum'
|
|
10278
|
-
* // Address automatically resolved via getAddress() internally
|
|
10279
|
-
* })
|
|
10280
|
-
* ```
|
|
10281
11209
|
*/
|
|
10282
11210
|
async getAddress(chain) {
|
|
10283
11211
|
// Prevent calling getAddress on developer-controlled adapters
|
|
10284
11212
|
if (this.capabilities?.addressContext === 'developer-controlled') {
|
|
10285
|
-
throw new Error('Cannot call getAddress() on developer-controlled adapters. '
|
|
10286
|
-
'Address must be provided explicitly in the operation context.');
|
|
11213
|
+
throw new Error('Cannot call getAddress() on developer-controlled adapters. Address must be provided explicitly in the operation context.');
|
|
10287
11214
|
}
|
|
10288
11215
|
// Chain parameter should now be provided by resolveOperationContext
|
|
10289
11216
|
if (!chain) {
|
|
10290
|
-
throw new Error('Chain parameter is required for address resolution. '
|
|
10291
|
-
'This should be provided by the OperationContext pattern.');
|
|
11217
|
+
throw new Error('Chain parameter is required for address resolution. This should be provided by the OperationContext pattern.');
|
|
10292
11218
|
}
|
|
10293
11219
|
// Ensure we're on the correct chain before getting address
|
|
10294
11220
|
await this.ensureChain(chain);
|
|
@@ -10376,9 +11302,9 @@ class EthersAdapter extends EvmAdapter {
|
|
|
10376
11302
|
*
|
|
10377
11303
|
* @example
|
|
10378
11304
|
* ```typescript
|
|
10379
|
-
* import {
|
|
11305
|
+
* import { createEthersAdapterFromPrivateKey } from '@circle-fin/adapter-ethers-v6'
|
|
10380
11306
|
*
|
|
10381
|
-
* const adapter =
|
|
11307
|
+
* const adapter = createEthersAdapterFromPrivateKey({
|
|
10382
11308
|
* privateKey: process.env.PRIVATE_KEY
|
|
10383
11309
|
* })
|
|
10384
11310
|
*
|
|
@@ -10416,7 +11342,7 @@ class EthersAdapter extends EvmAdapter {
|
|
|
10416
11342
|
* @example
|
|
10417
11343
|
* ```typescript
|
|
10418
11344
|
* // Multi-chain permit signing with same adapter
|
|
10419
|
-
* const adapter =
|
|
11345
|
+
* const adapter = createEthersAdapterFromPrivateKey({
|
|
10420
11346
|
* privateKey: process.env.PRIVATE_KEY
|
|
10421
11347
|
* })
|
|
10422
11348
|
*
|
|
@@ -10434,9 +11360,9 @@ class EthersAdapter extends EvmAdapter {
|
|
|
10434
11360
|
* @example
|
|
10435
11361
|
* ```typescript
|
|
10436
11362
|
* // Browser wallet usage
|
|
10437
|
-
* import {
|
|
11363
|
+
* import { createEthersAdapterFromProvider } from '@circle-fin/adapter-ethers-v6'
|
|
10438
11364
|
*
|
|
10439
|
-
* const adapter = await
|
|
11365
|
+
* const adapter = await createEthersAdapterFromProvider({
|
|
10440
11366
|
* provider: window.ethereum
|
|
10441
11367
|
* })
|
|
10442
11368
|
*
|
|
@@ -10568,14 +11494,14 @@ class EthersAdapter extends EvmAdapter {
|
|
|
10568
11494
|
*
|
|
10569
11495
|
* @example
|
|
10570
11496
|
* ```typescript
|
|
10571
|
-
* import {
|
|
11497
|
+
* import { createEthersAdapterFromPrivateKey } from '@circle-fin/adapter-ethers-v6'
|
|
10572
11498
|
*
|
|
10573
11499
|
* // Both private key formats are supported (with or without '0x' prefix):
|
|
10574
|
-
* const adapter1 =
|
|
11500
|
+
* const adapter1 = createEthersAdapterFromPrivateKey({
|
|
10575
11501
|
* privateKey: '0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef' // With prefix
|
|
10576
11502
|
* })
|
|
10577
11503
|
*
|
|
10578
|
-
* const adapter2 =
|
|
11504
|
+
* const adapter2 = createEthersAdapterFromPrivateKey({
|
|
10579
11505
|
* privateKey: '1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef' // Without prefix (automatically normalized)
|
|
10580
11506
|
* })
|
|
10581
11507
|
*
|
|
@@ -10590,10 +11516,10 @@ class EthersAdapter extends EvmAdapter {
|
|
|
10590
11516
|
*
|
|
10591
11517
|
* @example
|
|
10592
11518
|
* ```typescript
|
|
10593
|
-
* import {
|
|
11519
|
+
* import { createEthersAdapterFromPrivateKey } from '@circle-fin/adapter-ethers-v6'
|
|
10594
11520
|
*
|
|
10595
11521
|
* // Multi-chain usage with same adapter instance
|
|
10596
|
-
* const adapter =
|
|
11522
|
+
* const adapter = createEthersAdapterFromPrivateKey({
|
|
10597
11523
|
* privateKey: process.env.PRIVATE_KEY as `0x${string}`
|
|
10598
11524
|
* })
|
|
10599
11525
|
*
|
|
@@ -10606,11 +11532,11 @@ class EthersAdapter extends EvmAdapter {
|
|
|
10606
11532
|
*
|
|
10607
11533
|
* @example
|
|
10608
11534
|
* ```typescript
|
|
10609
|
-
* import {
|
|
11535
|
+
* import { createEthersAdapterFromPrivateKey } from '@circle-fin/adapter-ethers-v6'
|
|
10610
11536
|
* import { Ethereum, Base, Polygon } from '@core/chains'
|
|
10611
11537
|
*
|
|
10612
11538
|
* // Custom capabilities configuration
|
|
10613
|
-
* const adapter =
|
|
11539
|
+
* const adapter = createEthersAdapterFromPrivateKey({
|
|
10614
11540
|
* privateKey: process.env.PRIVATE_KEY as `0x${string}`,
|
|
10615
11541
|
* capabilities: {
|
|
10616
11542
|
* supportedChains: [Ethereum, Base, Polygon]
|
|
@@ -10620,11 +11546,11 @@ class EthersAdapter extends EvmAdapter {
|
|
|
10620
11546
|
*
|
|
10621
11547
|
* @example
|
|
10622
11548
|
* ```typescript
|
|
10623
|
-
* import {
|
|
11549
|
+
* import { createEthersAdapterFromPrivateKey } from '@circle-fin/adapter-ethers-v6'
|
|
10624
11550
|
* import { JsonRpcProvider } from 'ethers'
|
|
10625
11551
|
*
|
|
10626
11552
|
* // Custom provider configuration with explicit chain mapping
|
|
10627
|
-
* const adapter =
|
|
11553
|
+
* const adapter = createEthersAdapterFromPrivateKey({
|
|
10628
11554
|
* privateKey: process.env.PRIVATE_KEY as `0x${string}`,
|
|
10629
11555
|
* getProvider: ({ chain }) => {
|
|
10630
11556
|
* const rpcEndpoints: Record<string, string> = {
|
|
@@ -10638,7 +11564,7 @@ class EthersAdapter extends EvmAdapter {
|
|
|
10638
11564
|
* })
|
|
10639
11565
|
* ```
|
|
10640
11566
|
*/
|
|
10641
|
-
function
|
|
11567
|
+
function createEthersAdapterFromPrivateKey(params) {
|
|
10642
11568
|
// Parse and validate input parameters at runtime (normalizes the private key by adding '0x' prefix if missing)
|
|
10643
11569
|
const { privateKey } = createAdapterFromPrivateKeyParamsSchema.parse(params);
|
|
10644
11570
|
const { getProvider, capabilities } = params;
|
|
@@ -10664,6 +11590,94 @@ function createAdapterFromPrivateKey(params) {
|
|
|
10664
11590
|
signer: wallet,
|
|
10665
11591
|
}, resolvedCapabilities);
|
|
10666
11592
|
}
|
|
11593
|
+
/**
|
|
11594
|
+
* @deprecated Use {@link createEthersAdapterFromPrivateKey} instead.
|
|
11595
|
+
*
|
|
11596
|
+
* Creates an EthersAdapter instance from a private key with automatic type inference.
|
|
11597
|
+
*
|
|
11598
|
+
* This function creates an EthersAdapter for server-side or programmatic use
|
|
11599
|
+
* by deriving a wallet from the provided private key. It uses lazy initialization
|
|
11600
|
+
* where the wallet is created without a provider and is connected to chain-specific
|
|
11601
|
+
* providers on-demand during operations. This matches the Viem adapter pattern and
|
|
11602
|
+
* enables seamless multi-chain operations with a single adapter instance.
|
|
11603
|
+
*
|
|
11604
|
+
* @remarks
|
|
11605
|
+
* The function performs the following operations:
|
|
11606
|
+
* 1. Validates the input parameters at runtime
|
|
11607
|
+
* 2. Creates a Wallet from the private key (without provider - lazy initialization)
|
|
11608
|
+
* 3. Applies smart defaults for capabilities (user-controlled + all EVM chains)
|
|
11609
|
+
* 4. Returns a configured EthersAdapter instance
|
|
11610
|
+
*
|
|
11611
|
+
* Chain context is provided through OperationContext during individual operations.
|
|
11612
|
+
* The wallet will be connected to a provider when ensureChain() is called in prepare().
|
|
11613
|
+
*
|
|
11614
|
+
* **Default Configuration:**
|
|
11615
|
+
* - `addressContext`: `'user-controlled'` (address derived from private key automatically)
|
|
11616
|
+
* - `supportedChains`: All EVM-compatible chains (~29 networks)
|
|
11617
|
+
* - Lazy initialization: Chain connection deferred until first operation
|
|
11618
|
+
*
|
|
11619
|
+
* **Security Warning:** Never use this function in client-side code or expose
|
|
11620
|
+
* private keys in any way. This function is intended for server-side use only.
|
|
11621
|
+
* Store private keys securely using environment variables or secret management systems.
|
|
11622
|
+
*
|
|
11623
|
+
* @typeParam TCapabilities - The adapter capabilities type for compile-time address validation
|
|
11624
|
+
* @param params - Configuration parameters for creating the adapter
|
|
11625
|
+
* @returns A configured EthersAdapter instance with automatically inferred capabilities
|
|
11626
|
+
* @throws Error when validation fails or wallet derivation fails
|
|
11627
|
+
*
|
|
11628
|
+
* @example
|
|
11629
|
+
* ```typescript
|
|
11630
|
+
* import { createAdapterFromPrivateKey } from '@circle-fin/adapter-ethers-v6'
|
|
11631
|
+
*
|
|
11632
|
+
* // Both private key formats are supported (with or without '0x' prefix):
|
|
11633
|
+
* const adapter1 = createAdapterFromPrivateKey({
|
|
11634
|
+
* privateKey: '0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef' // With prefix
|
|
11635
|
+
* })
|
|
11636
|
+
*
|
|
11637
|
+
* const adapter2 = createAdapterFromPrivateKey({
|
|
11638
|
+
* privateKey: '1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef' // Without prefix (automatically normalized)
|
|
11639
|
+
* })
|
|
11640
|
+
*
|
|
11641
|
+
* // Chain specified per-operation via OperationContext
|
|
11642
|
+
* const prepared = await adapter1.prepare({
|
|
11643
|
+
* address: '0x...',
|
|
11644
|
+
* abi: contractAbi,
|
|
11645
|
+
* functionName: 'transfer',
|
|
11646
|
+
* args: ['0xto', '1000']
|
|
11647
|
+
* }, { chain: 'Ethereum' })
|
|
11648
|
+
* ```
|
|
11649
|
+
*
|
|
11650
|
+
* @example
|
|
11651
|
+
* ```typescript
|
|
11652
|
+
* import { createAdapterFromPrivateKey } from '@circle-fin/adapter-ethers-v6'
|
|
11653
|
+
*
|
|
11654
|
+
* // Multi-chain usage with same adapter instance
|
|
11655
|
+
* const adapter = createAdapterFromPrivateKey({
|
|
11656
|
+
* privateKey: process.env.PRIVATE_KEY as `0x${string}`
|
|
11657
|
+
* })
|
|
11658
|
+
*
|
|
11659
|
+
* // Transfer USDC on Ethereum
|
|
11660
|
+
* await adapter.prepare(params, { chain: 'Ethereum' })
|
|
11661
|
+
*
|
|
11662
|
+
* // Transfer USDC on Base using the same adapter
|
|
11663
|
+
* await adapter.prepare(params, { chain: 'Base' })
|
|
11664
|
+
* ```
|
|
11665
|
+
*
|
|
11666
|
+
* @example
|
|
11667
|
+
* ```typescript
|
|
11668
|
+
* import { createAdapterFromPrivateKey } from '@circle-fin/adapter-ethers-v6'
|
|
11669
|
+
* import { Ethereum, Base, Polygon } from '@core/chains'
|
|
11670
|
+
*
|
|
11671
|
+
* // Custom capabilities configuration
|
|
11672
|
+
* const adapter = createAdapterFromPrivateKey({
|
|
11673
|
+
* privateKey: process.env.PRIVATE_KEY as `0x${string}`,
|
|
11674
|
+
* capabilities: {
|
|
11675
|
+
* supportedChains: [Ethereum, Base, Polygon]
|
|
11676
|
+
* }
|
|
11677
|
+
* })
|
|
11678
|
+
* ```
|
|
11679
|
+
*/
|
|
11680
|
+
const createAdapterFromPrivateKey = createEthersAdapterFromPrivateKey;
|
|
10667
11681
|
|
|
10668
11682
|
/**
|
|
10669
11683
|
* Creates an EthersAdapter instance from an EIP-1193 provider with smart default capabilities.
|
|
@@ -10707,10 +11721,10 @@ function createAdapterFromPrivateKey(params) {
|
|
|
10707
11721
|
*
|
|
10708
11722
|
* @example
|
|
10709
11723
|
* ```typescript
|
|
10710
|
-
* import {
|
|
11724
|
+
* import { createEthersAdapterFromProvider } from '@circle-fin/adapter-ethers-v6'
|
|
10711
11725
|
*
|
|
10712
11726
|
* // Minimal browser wallet configuration
|
|
10713
|
-
* const adapter = await
|
|
11727
|
+
* const adapter = await createEthersAdapterFromProvider({
|
|
10714
11728
|
* provider: window.ethereum
|
|
10715
11729
|
* // Smart defaults applied:
|
|
10716
11730
|
* // - addressContext: 'user-controlled'
|
|
@@ -10728,11 +11742,11 @@ function createAdapterFromPrivateKey(params) {
|
|
|
10728
11742
|
*
|
|
10729
11743
|
* @example
|
|
10730
11744
|
* ```typescript
|
|
10731
|
-
* import {
|
|
11745
|
+
* import { createEthersAdapterFromProvider } from '@circle-fin/adapter-ethers-v6'
|
|
10732
11746
|
* import { Ethereum, Base, Polygon } from '@core/chains'
|
|
10733
11747
|
*
|
|
10734
11748
|
* // Advanced: custom capabilities for production use
|
|
10735
|
-
* const adapter = await
|
|
11749
|
+
* const adapter = await createEthersAdapterFromProvider({
|
|
10736
11750
|
* provider: window.ethereum,
|
|
10737
11751
|
* capabilities: {
|
|
10738
11752
|
* supportedChains: [Ethereum, Base, Polygon] // Restrict to specific chains
|
|
@@ -10743,11 +11757,11 @@ function createAdapterFromPrivateKey(params) {
|
|
|
10743
11757
|
*
|
|
10744
11758
|
* @example
|
|
10745
11759
|
* ```typescript
|
|
10746
|
-
* import {
|
|
11760
|
+
* import { createEthersAdapterFromProvider } from '@circle-fin/adapter-ethers-v6'
|
|
10747
11761
|
* import { JsonRpcProvider } from 'ethers'
|
|
10748
11762
|
*
|
|
10749
11763
|
* // Advanced usage with custom provider logic for production
|
|
10750
|
-
* const adapter = await
|
|
11764
|
+
* const adapter = await createEthersAdapterFromProvider({
|
|
10751
11765
|
* provider: window.ethereum,
|
|
10752
11766
|
* getProvider: ({ chain }) => new JsonRpcProvider(
|
|
10753
11767
|
* `https://eth-mainnet.alchemyapi.io/v2/${process.env.ALCHEMY_KEY}`,
|
|
@@ -10764,10 +11778,10 @@ function createAdapterFromPrivateKey(params) {
|
|
|
10764
11778
|
* @example
|
|
10765
11779
|
* ```typescript
|
|
10766
11780
|
* // Cross-chain transfer using a single adapter
|
|
10767
|
-
* import {
|
|
11781
|
+
* import { createEthersAdapterFromProvider } from '@circle-fin/adapter-ethers-v6'
|
|
10768
11782
|
* import { BridgeKit } from '@circle-fin/bridge-kit'
|
|
10769
11783
|
*
|
|
10770
|
-
* const adapter = await
|
|
11784
|
+
* const adapter = await createEthersAdapterFromProvider({
|
|
10771
11785
|
* provider: window.ethereum
|
|
10772
11786
|
* })
|
|
10773
11787
|
*
|
|
@@ -10783,11 +11797,11 @@ function createAdapterFromPrivateKey(params) {
|
|
|
10783
11797
|
*
|
|
10784
11798
|
* @example
|
|
10785
11799
|
* ```typescript
|
|
10786
|
-
* import {
|
|
11800
|
+
* import { createEthersAdapterFromProvider } from '@circle-fin/adapter-ethers-v6'
|
|
10787
11801
|
*
|
|
10788
11802
|
* // Error handling example - wallet prompt occurs during factory creation
|
|
10789
11803
|
* try {
|
|
10790
|
-
* const adapter = await
|
|
11804
|
+
* const adapter = await createEthersAdapterFromProvider({
|
|
10791
11805
|
* provider: window.ethereum
|
|
10792
11806
|
* })
|
|
10793
11807
|
*
|
|
@@ -10804,7 +11818,7 @@ function createAdapterFromPrivateKey(params) {
|
|
|
10804
11818
|
* }
|
|
10805
11819
|
* ```
|
|
10806
11820
|
*/
|
|
10807
|
-
const
|
|
11821
|
+
const createEthersAdapterFromProvider = async (params) => {
|
|
10808
11822
|
// Validate input
|
|
10809
11823
|
// Type assertion needed due to Partial<AdapterCapabilities> with exactOptionalPropertyTypes
|
|
10810
11824
|
validateCreateAdapterFromProviderParams(params);
|
|
@@ -10833,6 +11847,105 @@ const createAdapterFromProvider = async (params) => {
|
|
|
10833
11847
|
signer,
|
|
10834
11848
|
}, resolvedCapabilities);
|
|
10835
11849
|
};
|
|
11850
|
+
/**
|
|
11851
|
+
* @deprecated Use {@link createEthersAdapterFromProvider} instead.
|
|
11852
|
+
*
|
|
11853
|
+
* Creates an EthersAdapter instance from an EIP-1193 provider with smart default capabilities.
|
|
11854
|
+
*
|
|
11855
|
+
* This function creates an EthersAdapter for browser or injected wallet use
|
|
11856
|
+
* by connecting to the provided EIP-1193-compatible provider (such as MetaMask,
|
|
11857
|
+
* WalletConnect, or any browser wallet). The adapter is automatically configured
|
|
11858
|
+
* with user-controlled address context and support for all EVM-compatible chains.
|
|
11859
|
+
*
|
|
11860
|
+
* @remarks
|
|
11861
|
+
* The function performs the following operations:
|
|
11862
|
+
* 1. Validates the input parameters at runtime
|
|
11863
|
+
* 2. Creates a BrowserProvider for the injected provider
|
|
11864
|
+
* 3. Requests account access from the user (wallet prompt appears here)
|
|
11865
|
+
* 4. Retrieves the signer from the connected wallet
|
|
11866
|
+
* 5. Applies smart defaults for capabilities (user-controlled + all EVM chains)
|
|
11867
|
+
* 6. Returns a configured EthersAdapter instance ready for immediate use
|
|
11868
|
+
*
|
|
11869
|
+
* The adapter will be ready to use immediately after creation with the
|
|
11870
|
+
* specified provider and signer active. Chain context is provided through
|
|
11871
|
+
* OperationContext during individual operations.
|
|
11872
|
+
*
|
|
11873
|
+
* **Smart Defaults:**
|
|
11874
|
+
* - `addressContext`: `'user-controlled'` (addresses managed through wallet UI)
|
|
11875
|
+
* - `supportedChains`: All EVM-compatible chains (~29 networks) for maximum flexibility
|
|
11876
|
+
* - OperationContext support for per-operation chain specification
|
|
11877
|
+
*
|
|
11878
|
+
* **Account Access:**
|
|
11879
|
+
* Note: Unlike Viem, Ethers requires eager signer initialization because:
|
|
11880
|
+
* - Ethers Signer objects are chain-specific and must be recreated during chain switches
|
|
11881
|
+
* - The switchChain utility requires an initialized Signer as input
|
|
11882
|
+
* - This is an architectural difference between Ethers and Viem, not a limitation
|
|
11883
|
+
* The user will be prompted for account access when this factory is called (before adapter is returned).
|
|
11884
|
+
*
|
|
11885
|
+
* **Security Note:** This function is intended for use with injected/browser wallets.
|
|
11886
|
+
* Do not use in server-side code with private keys.
|
|
11887
|
+
*
|
|
11888
|
+
* @param params - Configuration parameters for creating the adapter
|
|
11889
|
+
* @returns A configured EthersAdapter instance
|
|
11890
|
+
* @throws Error when validation fails, user rejects connection, or provider initialization fails
|
|
11891
|
+
*
|
|
11892
|
+
* @example
|
|
11893
|
+
* ```typescript
|
|
11894
|
+
* import { createAdapterFromProvider } from '@circle-fin/adapter-ethers-v6'
|
|
11895
|
+
*
|
|
11896
|
+
* // Minimal browser wallet configuration
|
|
11897
|
+
* const adapter = await createAdapterFromProvider({
|
|
11898
|
+
* provider: window.ethereum
|
|
11899
|
+
* // Smart defaults applied:
|
|
11900
|
+
* // - addressContext: 'user-controlled'
|
|
11901
|
+
* // - supportedChains: all EVM chains (~29 networks)
|
|
11902
|
+
* })
|
|
11903
|
+
*
|
|
11904
|
+
* // Use with OperationContext pattern
|
|
11905
|
+
* const prepared = await adapter.prepare({
|
|
11906
|
+
* address: '0x...',
|
|
11907
|
+
* abi: contractAbi,
|
|
11908
|
+
* functionName: 'transfer',
|
|
11909
|
+
* args: ['0xto', '1000']
|
|
11910
|
+
* }, { chain: 'Ethereum' }) // Chain specified in context
|
|
11911
|
+
* ```
|
|
11912
|
+
*
|
|
11913
|
+
* @example
|
|
11914
|
+
* ```typescript
|
|
11915
|
+
* import { createAdapterFromProvider } from '@circle-fin/adapter-ethers-v6'
|
|
11916
|
+
* import { Ethereum, Base, Polygon } from '@core/chains'
|
|
11917
|
+
*
|
|
11918
|
+
* // Advanced: custom capabilities for production use
|
|
11919
|
+
* const adapter = await createAdapterFromProvider({
|
|
11920
|
+
* provider: window.ethereum,
|
|
11921
|
+
* capabilities: {
|
|
11922
|
+
* supportedChains: [Ethereum, Base, Polygon] // Restrict to specific chains
|
|
11923
|
+
* // addressContext still defaults to 'user-controlled'
|
|
11924
|
+
* }
|
|
11925
|
+
* })
|
|
11926
|
+
* ```
|
|
11927
|
+
*
|
|
11928
|
+
* @example
|
|
11929
|
+
* ```typescript
|
|
11930
|
+
* import { createAdapterFromProvider } from '@circle-fin/adapter-ethers-v6'
|
|
11931
|
+
* import { JsonRpcProvider } from 'ethers'
|
|
11932
|
+
*
|
|
11933
|
+
* // Advanced usage with custom provider logic for production
|
|
11934
|
+
* const adapter = await createAdapterFromProvider({
|
|
11935
|
+
* provider: window.ethereum,
|
|
11936
|
+
* getProvider: ({ chain }) => new JsonRpcProvider(
|
|
11937
|
+
* `https://eth-mainnet.alchemyapi.io/v2/${process.env.ALCHEMY_KEY}`,
|
|
11938
|
+
* { name: chain.name, chainId: chain.chainId }
|
|
11939
|
+
* )
|
|
11940
|
+
* })
|
|
11941
|
+
*
|
|
11942
|
+
* // Address is automatically resolved from connected wallet
|
|
11943
|
+
* const prepared = await adapter.prepare(params, {
|
|
11944
|
+
* chain: 'Polygon' // Address comes from wallet UI
|
|
11945
|
+
* })
|
|
11946
|
+
* ```
|
|
11947
|
+
*/
|
|
11948
|
+
const createAdapterFromProvider = createEthersAdapterFromProvider;
|
|
10836
11949
|
|
|
10837
11950
|
exports.JsonRpcProvider = ethers.JsonRpcProvider;
|
|
10838
11951
|
exports.EthersAdapter = EthersAdapter;
|
|
@@ -10840,6 +11953,8 @@ exports.buildEIP2612TypedData = buildEIP2612TypedData;
|
|
|
10840
11953
|
exports.computeDefaultDeadline = computeDefaultDeadline;
|
|
10841
11954
|
exports.createAdapterFromPrivateKey = createAdapterFromPrivateKey;
|
|
10842
11955
|
exports.createAdapterFromProvider = createAdapterFromProvider;
|
|
11956
|
+
exports.createEthersAdapterFromPrivateKey = createEthersAdapterFromPrivateKey;
|
|
11957
|
+
exports.createEthersAdapterFromProvider = createEthersAdapterFromProvider;
|
|
10843
11958
|
exports.parseSignature = parseSignature;
|
|
10844
11959
|
exports.validateAdapterCapabilities = validateAdapterCapabilities;
|
|
10845
11960
|
//# sourceMappingURL=index.cjs.map
|